Compare commits

...

88 Commits

Author SHA1 Message Date
yuri 6c1fb42f59 exclude by email address 2015-11-26 15:04:30 +02:00
yuri 233da7a0be mass email: dont send twice 2015-11-26 12:24:14 +02:00
yuri 612f73dac9 link Parent filters 2015-11-26 11:48:40 +02:00
yuri 3ae7634b15 campaign log lan 2015-11-26 11:10:27 +02:00
yuri 9cfabebdad fix default config 2015-11-25 16:01:57 +02:00
yuri 0522796fe3 po lang fix 2015-11-25 12:46:20 +02:00
yuri 261e76458a fix lang 2015-11-25 12:45:36 +02:00
yuri 4f2d218a82 improve po and lang 2015-11-25 12:22:46 +02:00
yuri 4f531da9f5 de_DE 2015-11-25 10:33:39 +02:00
yuri c8adb52b7a dashlet calendar and scopeList from metadata 2015-11-24 13:13:35 +02:00
yuri 5152e1ff36 po.js code style 2015-11-24 12:55:50 +02:00
yuri 5b1cfbdcfc fix lang 2015-11-24 12:34:13 +02:00
yuri 00b690da99 notification enhancement 2015-11-24 12:21:33 +02:00
yuri ed63848efa fix lang 2015-11-24 11:37:15 +02:00
yuri d174fe3e6f fix activities 2015-11-23 18:19:54 +02:00
yuri e1a505955e calendar metadata 2015-11-23 17:49:20 +02:00
yuri a30b638a8d personal email accounts link on admin panel 2015-11-23 16:57:22 +02:00
yuri c37fb02b9e record view impovements 2015-11-23 16:44:40 +02:00
yuri 3fc5b08969 form leave off confirmation while inline edit 2015-11-23 16:06:43 +02:00
yuri 0209bc3cd3 Merge branch 'master' of https://github.com/espocrm/espocrm 2015-11-23 12:52:12 +02:00
yuri db02fc4e55 refactor calendar backend 2015-11-23 12:52:05 +02:00
yuri d7787a829e refactor activities backend 2015-11-23 12:25:31 +02:00
Yuri Kuznetsov acdea144bc Merge pull request #70 from PanuWeb/translateES
New Translations ES
2015-11-22 16:23:17 +02:00
PanuWeb 700bb150bb Remaking translations II. Translations dumped and not know why. 2015-11-20 22:56:12 -03:00
PanuWeb 9a22ed262b Remaking translations. Translations dumped and not know why. 2015-11-20 22:52:44 -03:00
PanuWeb 65827b2c02 Fix 'options'. 2015-11-20 22:38:59 -03:00
PanuWeb e23e80347b Difficult words translated. 2015-11-20 22:29:05 -03:00
PanuWeb ddedbfc999 Fix missing 'Save'. Add missing translations. 2015-11-20 21:55:32 -03:00
yuri d6144f777c cleanup 2015-11-20 16:58:43 +02:00
yuri a328eff3ae fix package.json 2015-11-20 16:43:59 +02:00
yuri 45bc019967 ver 2015-11-20 16:43:21 +02:00
yuri 2aafca7ae4 fix in about 2015-11-20 16:35:06 +02:00
yuri 13a38f9767 fix in about 2015-11-20 16:32:18 +02:00
yuri fcf07cdf7c additions in about page 2015-11-20 16:29:30 +02:00
yuri 723a01c12d fix warning 2015-11-20 15:53:50 +02:00
yuri 0c1526cb69 fix notices and logging 2015-11-20 15:47:45 +02:00
yuri e25cd0c1a5 jobSchedulingMap 2015-11-20 15:21:33 +02:00
yuri a3683b76b8 authTokenControl job 2015-11-20 15:02:13 +02:00
yuri e4e56489d7 fix notice 2015-11-20 13:13:04 +02:00
yuri 1db01f1b5f naming fix 2015-11-19 16:38:37 +02:00
yuri d9a134bb3a logo refactoring 2015-11-19 15:09:11 +02:00
yuri 1cb762fae7 map field 2015-11-19 11:25:33 +02:00
yuri 16e57e38a3 fix image preview 2015-11-18 17:38:21 +02:00
yuri 42cb6fff5c navigate through image attachments 2015-11-18 16:55:01 +02:00
yuri 55239615ef email trash 2015-11-18 12:45:47 +02:00
yuri 6083531c4a record list changes 2015-11-18 12:45:07 +02:00
yuri 65cae656c3 fix stream note rowActions 2015-11-18 10:43:17 +02:00
yuri ab928c56ad list view improvement: keep selection after return 2015-11-17 17:15:58 +02:00
yuri 884a135397 email is read fix 2015-11-17 16:54:28 +02:00
yuri da2baec89d improve list layout load 2015-11-17 16:03:29 +02:00
yuri 59dfa99ff3 inbound email: addAllTeamUsers field 2015-11-17 15:44:50 +02:00
yuri e15c748470 ics cleanup 2015-11-17 12:12:07 +02:00
yuri 51fc8ac9b1 fix linkMultiple field 2015-11-17 12:08:12 +02:00
yuri a4c0d7874f link fields create attributes 2015-11-17 11:46:26 +02:00
yuri 2f62a455a3 email: change list layout 2015-11-16 18:41:40 +02:00
yuri a7a3d8a0a7 fix record tree 2015-11-16 17:40:36 +02:00
yuri 1a7774341a fix 2015-11-16 17:33:51 +02:00
yuri 0d4565962f email template editQuick is wide 2015-11-16 17:28:05 +02:00
yuri a2ff2e2f5e Email: show replied field after reply 2015-11-16 17:22:07 +02:00
yuri b038a9449f Email Template: one-off field added 2015-11-16 16:52:14 +02:00
yuri 160d10c5d1 opt out in ui 2015-11-16 13:11:29 +02:00
yuri 4eb842d37d fix linkMultiple edit html 2015-11-16 12:47:30 +02:00
yuri 7fd78a71fd cleanup 2015-11-16 12:45:41 +02:00
yuri 88fb06d982 fix documents 2015-11-16 12:43:20 +02:00
yuri c8a208db4b documents: account panel by default 2015-11-16 12:27:38 +02:00
yuri 2aaec4ea99 documents navigation fix 2015-11-16 12:22:05 +02:00
yuri b64c897f33 email template filters 2015-11-13 16:52:59 +02:00
yuri 64bf4b8296 change order of email dropdown 2015-11-13 16:35:25 +02:00
yuri 52bb6d10be sort email templates by date created 2015-11-13 15:32:53 +02:00
yuri 3527abb171 tagetLists field for Account 2015-11-13 12:08:41 +02:00
yuri 8bdbac85a8 configurable notification sound 2015-11-13 11:46:10 +02:00
yuri 13eb8630ca try catch for email sendings 2015-11-12 17:47:43 +02:00
yuri e749ec6943 Merge branch 'hotfix/3.8.1' 2015-11-12 15:29:36 +02:00
yuri c0d0c72392 fix email filter 2015-11-12 15:29:25 +02:00
yuri f1834147c7 is on of filter 2015-11-12 12:21:33 +02:00
yuri e044d34abd entity manager: dont allow to change linkMultipleField if not custom 2015-11-12 10:37:20 +02:00
yuri bfcc5b0b14 spelling fix 2015-11-11 17:42:04 +02:00
yuri 7a5b3b23e6 varchar filters added 2015-11-11 16:50:15 +02:00
yuri 9e15565227 linkMultiple in Entity Manager 2015-11-11 15:21:18 +02:00
yuri 79e6befdf4 commit fix link manager view 2015-11-11 11:06:47 +02:00
yuri 07ff4cb3d1 refactoring and modal fix 2015-11-11 10:41:08 +02:00
yuri 961a5bb8a0 entityManager relationName 2015-11-10 18:26:10 +02:00
yuri bf5f0e0e7f cleanup 2015-11-10 16:15:05 +02:00
yuri 329f05fd5b email attachment encoded fix 2015-11-10 12:42:57 +02:00
yuri 2661511bbc silent email notification param 2015-11-10 11:46:55 +02:00
yuri cea3c8609b email failed 2015-11-10 10:40:35 +02:00
yuri 35cbbb06ce remove stream from default tabList 2015-11-10 10:31:02 +02:00
yuri 1383a5d5da fix warning 2015-11-10 10:30:14 +02:00
193 changed files with 3696 additions and 1492 deletions
+32 -4
View File
@@ -81,7 +81,7 @@ class Email extends \Espo\Core\Controllers\Record
throw new BadRequest();
}
}
return $this->getRecordService()->markAsReadByIds($ids);
return $this->getRecordService()->markAsReadByIdList($ids);
}
public function postActionMarkAsNotRead($params, $data, $request)
@@ -95,7 +95,7 @@ class Email extends \Espo\Core\Controllers\Record
throw new BadRequest();
}
}
return $this->getRecordService()->markAsNotReadByIds($ids);
return $this->getRecordService()->markAsNotReadByIdList($ids);
}
public function postActionMarkAllAsRead($params, $data, $request)
@@ -114,7 +114,7 @@ class Email extends \Espo\Core\Controllers\Record
throw new BadRequest();
}
}
return $this->getRecordService()->markAsImportantByIds($ids);
return $this->getRecordService()->markAsImportantByIdList($ids);
}
public function postActionMarkAsNotImportant($params, $data, $request)
@@ -128,7 +128,35 @@ class Email extends \Espo\Core\Controllers\Record
throw new BadRequest();
}
}
return $this->getRecordService()->markAsNotImportantByIds($ids);
return $this->getRecordService()->markAsNotImportantByIdList($ids);
}
public function postActionMoveToTrash($params, $data)
{
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
return $this->getRecordService()->moveToTrashByIdList($ids);
}
public function postActionRetrieveFromTrash($params, $data)
{
if (!empty($data['ids'])) {
$ids = $data['ids'];
} else {
if (!empty($data['id'])) {
$ids = [$data['id']];
} else {
throw new BadRequest();
}
}
return $this->getRecordService()->retrieveFromTrashByIdList($ids);
}
}
+35 -6
View File
@@ -165,15 +165,31 @@ class EntityManager extends \Espo\Core\Controllers\Base
'linkType'
];
$d = array();
$additionalParamList = [
'relationName',
];
$params = array();
foreach ($paramList as $item) {
if (empty($data[$item])) {
throw new BadRequest();
}
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
$result = $this->getContainer()->get('entityManagerUtil')->createLink($d);
foreach ($additionalParamList as $item) {
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
if (array_key_exists('linkMultipleField', $data)) {
$params['linkMultipleField'] = $data['linkMultipleField'];
}
if (array_key_exists('linkMultipleFieldForeign', $data)) {
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
}
$result = $this->getContainer()->get('entityManagerUtil')->createLink($params);
if ($result) {
$this->getContainer()->get('dataManager')->rebuild();
@@ -199,12 +215,25 @@ class EntityManager extends \Espo\Core\Controllers\Base
'labelForeign'
];
$d = array();
$additionalParamList = [];
$params = array();
foreach ($paramList as $item) {
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($d);
foreach ($additionalParamList as $item) {
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
if (array_key_exists('linkMultipleField', $data)) {
$params['linkMultipleField'] = $data['linkMultipleField'];
}
if (array_key_exists('linkMultipleFieldForeign', $data)) {
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
}
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($params);
if ($result) {
$this->getContainer()->get('dataManager')->clearCache();
+9 -6
View File
@@ -87,16 +87,19 @@ class Container
protected function loadLog()
{
$logConfig = $this->get('config')->get('logger');
$config = $this->get('config');
$path = $config->get('logger.path', 'data/logs/espo.log');
$rotation = $config->get('logger.rotation', true);
$log = new \Espo\Core\Utils\Log('Espo');
$levelCode = $log->getLevelCode($config->get('logger.level', 'WARNING'));
$levelCode = $log->getLevelCode($logConfig['level']);
if ($logConfig['isRotate']) {
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\RotatingFileHandler($logConfig['path'], $logConfig['maxRotateFiles'], $levelCode);
if ($rotation) {
$maxFileNumber = $config->get('logger.maxFileNumber', 30);
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\RotatingFileHandler($path, $maxFileNumber, $levelCode);
} else {
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\StreamHandler($logConfig['path'], $levelCode);
$handler = new \Espo\Core\Utils\Log\Monolog\Handler\StreamHandler($path, $levelCode);
}
$log->pushHandler($handler);
+49 -11
View File
@@ -71,7 +71,7 @@ class Importer
return $this->filtersMatcher;
}
public function importMessage($message, $userId, $teamsIds = [], $filterList = [])
public function importMessage($message, $userId = null, $teamsIdList = [], $userIdList = [], $filterList = [])
{
try {
$email = $this->getEntityManager()->getEntity('Email');
@@ -85,8 +85,14 @@ class Importer
$email->set('name', $subject);
$email->set('status', 'Archived');
$email->set('attachmentsIds', array());
$email->set('assignedUserId', $userId);
$email->set('teamsIds', $teamsIds);
if ($userId) {
$email->set('assignedUserId', $userId);
}
$email->set('teamsIds', $teamsIdList);
if (!empty($userIdList)) {
$email->set('usersIds', $userIdList);
}
$fromArr = $this->getAddressListFromMessage($message, 'from');
if (isset($message->from)) {
@@ -123,13 +129,24 @@ class Importer
if ($duplicate = $this->findDuplicate($email)) {
$duplicate->loadLinkMultipleField('users');
$usersIds = $duplicate->get('usersIds');
$usersIds[] = $userId;
if ($userId) {
if (!in_array($userId, $usersIds)) {
$usersIds[] = $userId;
}
}
if (!empty($userIdList)) {
foreach ($userIdList as $additionalUserId) {
if (!in_array($additionalUserId, $usersIds)) {
$usersIds[] = $additionalUserId;
}
}
}
$duplicate->set('usersIds', $usersIds);
$this->getEntityManager()->saveEntity($duplicate);
if (!empty($teamsIds)) {
foreach ($teamsIds as $teamId) {
if (!empty($teamsIdList)) {
foreach ($teamsIdList as $teamId) {
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);
}
}
@@ -384,9 +401,10 @@ class Importer
$contentId = null;
if ($contentDisposition) {
if ($contentDisposition === 'attachment') {
if (preg_match('/filename="?([^"]+)"?/i', $part->ContentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
} else if ($contentDisposition === 'inline') {
@@ -395,10 +413,10 @@ class Importer
$fileName = $contentId;
$disposition = 'inline';
} else {
// hack for iOS not proper attachments
// for iOS attachments
if (empty($fileName)) {
if (preg_match('/filename="?([^"]+)"?/i', $part->ContentDisposition, $m)) {
$fileName = $m[1];
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
if ($fileName) {
$disposition = 'attachment';
}
}
@@ -442,6 +460,26 @@ class Importer
} catch (\Exception $e) {}
}
protected function fetchFileNameFromContentDisposition($contentDisposition)
{
$m = array();
if (preg_match('/filename="?([^"]+)"?/i', $contentDisposition, $m)) {
$fileName = $m[1];
return $fileName;
} else if (preg_match('/filename\*="?([^"]+)"?/i', $contentDisposition, $m)) {
$fileName = $m[1];
if ($fileName && stripos($fileName, "''") !== false) {
list($encoding, $fileName) = explode("''", $fileName);
$fileName = rawurldecode($fileName);
if (strtoupper($encoding) !== 'UTF-8') {
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
}
return $fileName;
}
}
return false;
}
protected function getContentFromPart($part)
{
if ($part instanceof \Zend\Mime\Part) {
+30 -5
View File
@@ -357,10 +357,8 @@ class Base
if (!$this->getSeed()->hasField('teamsIds')) {
return;
}
$result['distinct'] = true;
if (!in_array('teams', $result['joins'])) {
$result['leftJoins'][] = 'teams';
}
$this->setDistinct(true, $result);
$this->addLeftJoin('teams', $result);
$result['whereClause'][] = array(
'OR' => array(
'teams.id' => $this->user->get('teamsIds'),
@@ -601,7 +599,7 @@ class Base
foreach ($item['value'] as $i) {
$a = $this->getWherePart($i);
foreach ($a as $left => $right) {
if (!empty($right)) {
if (!empty($right) || is_null($right) || $right === '') {
$arr[] = array($left => $right);
}
}
@@ -800,6 +798,33 @@ class Base
$this->textFilter($textFilter, $result);
}
public function addJoin($join, &$result)
{
if (empty($result['joins'])) {
$result['joins'] = [];
}
if (!in_array($join, $result['joins'])) {
$result['joins'][] = $join;
}
}
public function addLeftJoin($leftJoin, &$result)
{
if (empty($result['leftJoins'])) {
$result['leftJoins'] = [];
}
if (!in_array($leftJoin, $result['leftJoins'])) {
$result['leftJoins'][] = $leftJoin;
}
}
public function setDistinct($distinct, &$result)
{
$result['distinct'] = (bool) $distinct;
}
protected function textFilter($textFilter, &$result)
{
$fieldDefs = $this->getSeed()->getFields();
+8 -4
View File
@@ -158,12 +158,16 @@ class Config
$data = include($this->configPath);
foreach ($values as $key => $value) {
$data[$key] = $value;
if (is_array($values)) {
foreach ($values as $key => $value) {
$data[$key] = $value;
}
}
foreach ($removeData as $key) {
unset($data[$key]);
if (is_array($removeData)) {
foreach ($removeData as $key) {
unset($data[$key]);
}
}
$result = $this->getFileManager()->putPhpContents($this->configPath, $data);
@@ -296,9 +296,9 @@ class Converter
$entityMeta = $ormMeta[$entityName];
//load custom field definitions and customCodes
foreach($entityMeta['fields'] as $fieldName => $fieldParams) {
foreach ($entityMeta['fields'] as $fieldName => $fieldParams) {
if (empty($fieldParams['type'])) continue;
//load custom field definitions
$fieldType = ucfirst($fieldParams['type']);
$className = '\Espo\Custom\Core\Utils\Database\Orm\Fields\\' . $fieldType;
if (!class_exists($className)) {
@@ -315,7 +315,7 @@ class Converter
$ormMeta = Util::merge($ormMeta, $fieldResult);
} //END: load custom field definitions
}
}
//todo move to separate file
@@ -371,7 +371,7 @@ class Converter
}
/** check and set the field length */
if (!isset($fieldDefs['len']) && in_array($fieldDefs['type'], array_keys($this->defaultLength))) {
if (isset($fieldDefs['type']) && !isset($fieldDefs['len']) && in_array($fieldDefs['type'], array_keys($this->defaultLength))) {
$fieldDefs['len'] = $this->defaultLength[$fieldDefs['type']];
}
@@ -385,7 +385,7 @@ class Converter
}
$relationships = array();
foreach($entityMeta['links'] as $linkName => $linkParams) {
foreach ($entityMeta['links'] as $linkName => $linkParams) {
$convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityName, $ormMeta);
@@ -28,6 +28,7 @@
************************************************************************/
namespace Espo\Core\Utils\Database\Orm;
use Espo\Core\Utils\Util;
class RelationManager
@@ -111,18 +112,22 @@ class RelationManager
$currentType = $linkParams['type'];
$method = $currentType;
$relType = $currentType;
if ($foreignLink !== false) {
$method .= '_' . $foreignLink['params']['type'];
$relType .= '_' . $foreignLink['params']['type'];
}
$method = Util::toCamelCase($method);
$relType = Util::toCamelCase($relType);
$relationName = $this->isRelationExists($method) ? $method /*hasManyHasMany*/ : $currentType /*hasMany*/;
$relationName = $this->isRelationExists($relType) ? $relType /*hasManyHasMany*/ : $currentType /*hasMany*/;
//relationDefs defined in separate file
if (isset($linkParams['relationName']) && $this->isMethodExists($linkParams['relationName'])) {
if (isset($linkParams['relationName'])) {
$className = $this->getRelationClass($linkParams['relationName']);
} else if ($this->isMethodExists($relationName)) {
if (!$className) {
$relationName = $this->isRelationExists($relType) ? $relType : $currentType;
$className = $this->getRelationClass($relationName);
}
} else {
$className = $this->getRelationClass($relationName);
}
@@ -108,6 +108,7 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
$this->setMethods($inputs);
$convertedDefs = $this->load($linkName, $entityName);
$convertedDefs = $this->mergeAllowedParams($convertedDefs);
$inputs = $this->setArrayValue(null, $inputs);
@@ -125,13 +126,12 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
$linkParams = &$loads[$entityName]['relations'][$linkName];
foreach ($this->allowedParams as $name) {
$additionalParam = $this->getAllowedAdditionalParam($name);
$additionalParrams = $this->getAllowedAdditionalParams($name);
if (isset($additionalParrams)) {
$linkParams[$name] = $additionalParrams;
if (isset($additionalParam)) {
$linkParams[$name] = $additionalParam;
if (isset($linkParams[$name]) && is_array($linkParams[$name])) {
$linkParams[$name] = Util::merge($linkParams[$name], $additionalParrams);
$linkParams[$name] = Util::merge($linkParams[$name], $additionalParam);
}
}
}
@@ -140,7 +140,7 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
return $loads;
}
private function getAllowedAdditionalParams($allowedItemName)
private function getAllowedAdditionalParam($allowedItemName)
{
$linkParams = $this->getLinkParams();
$foreignLinkParams = $this->getForeignLinkParams();
@@ -148,17 +148,26 @@ class Base extends \Espo\Core\Utils\Database\Orm\Base
$itemLinkParams = isset($linkParams[$allowedItemName]) ? $linkParams[$allowedItemName] : null;
$itemForeignLinkParams = isset($foreignLinkParams[$allowedItemName]) ? $foreignLinkParams[$allowedItemName] : null;
$additionalParrams = null;
$additionalParam = null;
$linkName = $this->getLinkName();
$entityName = $this->getEntityName();
if (isset($itemLinkParams) && isset($itemForeignLinkParams)) {
$additionalParrams = Util::merge($itemLinkParams, $itemForeignLinkParams);
if (!empty($itemLinkParams) && !is_array($itemLinkParams)) {
$additionalParam = $itemLinkParams;
} else if (!empty($itemForeignLinkParams) && !is_array($itemForeignLinkParams)) {
$additionalParam = $itemForeignLinkParams;
} else {
$additionalParam = Util::merge($itemLinkParams, $itemForeignLinkParams);
}
} else if (isset($itemLinkParams)) {
$additionalParrams = $itemLinkParams;
$additionalParam = $itemLinkParams;
} else if (isset($itemForeignLinkParams)) {
$additionalParrams = $itemForeignLinkParams;
$additionalParam = $itemForeignLinkParams;
}
return $additionalParrams;
return $additionalParam;
}
}
@@ -37,6 +37,14 @@ class ManyMany extends Base
{
$foreignEntityName = $this->getForeignEntityName();
$linkParams = $this->getLinkParams();
if (!empty($linkParams['relationName'])) {
$relationName = $linkParams['relationName'];
} else {
$relationName = $this->getJoinTable($entityName, $foreignEntityName);
}
return array(
$entityName => array(
'fields' => array(
@@ -53,7 +61,7 @@ class ManyMany extends Base
$linkName => array(
'type' => 'manyMany',
'entity' => $foreignEntityName,
'relationName' => $this->getJoinTable($entityName, $foreignEntityName),
'relationName' => $relationName,
'key' => 'id', //todo specify 'key'
'foreignKey' => 'id', //todo specify 'foreignKey'
'midKeys' => array(
@@ -326,7 +326,11 @@ class Converter
break;
case 'bool':
$dbFieldParams['default'] = intval($dbFieldParams['default']);
$default = false;
if (array_key_exists('default', $dbFieldParams)) {
$default = $dbFieldParams['default'];
}
$dbFieldParams['default'] = intval($default);
break;
}
+86 -16
View File
@@ -268,6 +268,24 @@ class EntityManager
$label = $params['label'];
$labelForeign = $params['labelForeign'];
if ($linkType === 'manyToMany') {
if (!empty($params['relationName'])) {
$relationName = $params['relationName'];
} else {
$relationName = lcfirst($entity) . $entityForeign;
}
}
$linkMultipleField = false;
if (!empty($params['linkMultipleField'])) {
$linkMultipleField = true;
}
$linkMultipleFieldForeign = false;
if (!empty($params['linkMultipleFieldForeign'])) {
$linkMultipleFieldForeign = true;
}
if (empty($linkType)) {
throw new Error();
}
@@ -300,11 +318,11 @@ class EntityManager
'fields' => array(
$link => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => true,
"layoutDetailDisabled" => !$linkMultipleField,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => true,
"noLoad" => true,
"importDisabled" => true,
"layoutMassUpdateDisabled" => !$linkMultipleField,
"noLoad" => !$linkMultipleField,
"importDisabled" => !$linkMultipleField,
'isCustom' => true
)
),
@@ -356,11 +374,11 @@ class EntityManager
'fields' => array(
$linkForeign => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => true,
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => true,
"noLoad" => true,
"importDisabled" => true,
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
"noLoad" => !$linkMultipleFieldForeign,
"importDisabled" => !$linkMultipleFieldForeign,
'isCustom' => true
)
),
@@ -379,17 +397,18 @@ class EntityManager
'fields' => array(
$link => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => true,
"layoutDetailDisabled" => !$linkMultipleField,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => true,
"importDisabled" => true,
"noLoad" => true,
"layoutMassUpdateDisabled" => !$linkMultipleField,
"importDisabled" => !$linkMultipleField,
"noLoad" => !$linkMultipleField,
'isCustom' => true
)
),
'links' => array(
$link => array(
'type' => 'hasMany',
'relationName' => $relationName,
'foreign' => $linkForeign,
'entity' => $entityForeign,
'isCustom' => true
@@ -400,17 +419,18 @@ class EntityManager
'fields' => array(
$linkForeign => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => true,
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => true,
"importDisabled" => true,
"noLoad" => true,
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
"importDisabled" => !$linkMultipleFieldForeign,
"noLoad" => !$linkMultipleFieldForeign,
'isCustom' => true
)
),
'links' => array(
$linkForeign => array(
'type' => 'hasMany',
'relationName' => $relationName,
'foreign' => $link,
'entity' => $entity,
'isCustom' => true
@@ -455,6 +475,56 @@ class EntityManager
throw new Error();
}
if (
$this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.type") == 'hasMany'
&&
$this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.isCustom")
) {
if (array_key_exists('linkMultipleField', $params)) {
$linkMultipleField = $params['linkMultipleField'];
$dataLeft = array(
'fields' => array(
$link => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => !$linkMultipleField,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => !$linkMultipleField,
"noLoad" => !$linkMultipleField,
"importDisabled" => !$linkMultipleField,
'isCustom' => true
)
)
);
$this->getMetadata()->set('entityDefs', $entity, $dataLeft);
$this->getMetadata()->save();
}
}
if (
$this->getMetadata()->get("entityDefs.{$entityForeign}.links.{$linkForeign}.type") == 'hasMany'
&&
$this->getMetadata()->get("entityDefs.{$entityForeign}.links.{$linkForeign}.isCustom")
) {
if (array_key_exists('linkMultipleFieldForeign', $params)) {
$linkMultipleFieldForeign = $params['linkMultipleFieldForeign'];
$dataRight = array(
'fields' => array(
$linkForeign => array(
"type" => "linkMultiple",
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
"layoutListDisabled" => true,
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
"noLoad" => !$linkMultipleFieldForeign,
"importDisabled" => !$linkMultipleFieldForeign,
'isCustom' => true
)
)
);
$this->getMetadata()->set('entityDefs', $entityForeign, $dataRight);
$this->getMetadata()->save();
}
}
$this->getLanguage()->set($entity, 'fields', $link, $label);
$this->getLanguage()->set($entity, 'links', $link, $label);
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
+47 -47
View File
@@ -70,47 +70,47 @@ class FieldManager
public function read($name, $scope)
{
$fieldDef = $this->getFieldDef($name, $scope);
$fieldDefs = $this->getFieldDefs($name, $scope);
$fieldDef['label'] = $this->getLanguage()->translate($name, 'fields', $scope);
$fieldDefs['label'] = $this->getLanguage()->translate($name, 'fields', $scope);
return $fieldDef;
return $fieldDefs;
}
public function create($name, $fieldDef, $scope)
public function create($name, $fieldDefs, $scope)
{
$existingField = $this->getFieldDef($name, $scope);
$existingField = $this->getFieldDefs($name, $scope);
if (isset($existingField)) {
throw new Conflict('Field ['.$name.'] exists in '.$scope);
}
return $this->update($name, $fieldDef, $scope);
return $this->update($name, $fieldDefs, $scope);
}
public function update($name, $fieldDef, $scope)
public function update($name, $fieldDefs, $scope)
{
/*Add option to metadata to identify the custom field*/
if (!$this->isCore($name, $scope)) {
$fieldDef[$this->customOptionName] = true;
$fieldDefs[$this->customOptionName] = true;
}
$res = true;
if (isset($fieldDef['label'])) {
$this->setLabel($name, $fieldDef['label'], $scope);
if (isset($fieldDefs['label'])) {
$this->setLabel($name, $fieldDefs['label'], $scope);
}
if (isset($fieldDef['type']) && $fieldDef['type'] == 'enum') {
if (isset($fieldDef['translatedOptions'])) {
$this->setTranslatedOptions($name, $fieldDef['translatedOptions'], $scope);
if (isset($fieldDefs['type']) && $fieldDefs['type'] == 'enum') {
if (isset($fieldDefs['translatedOptions'])) {
$this->setTranslatedOptions($name, $fieldDefs['translatedOptions'], $scope);
}
}
if (isset($fieldDef['label']) || isset($fieldDef['translatedOptions'])) {
if (isset($fieldDefs['label']) || isset($fieldDefs['translatedOptions'])) {
$res &= $this->getLanguage()->save();
}
if ($this->isDefsChanged($name, $fieldDef, $scope)) {
$res &= $this->setEntityDefs($name, $fieldDef, $scope);
if ($this->isDefsChanged($name, $fieldDefs, $scope)) {
$res &= $this->setEntityDefs($name, $fieldDefs, $scope);
}
return (bool) $res;
@@ -134,11 +134,11 @@ class FieldManager
return (bool) $res;
}
protected function setEntityDefs($name, $fieldDef, $scope)
protected function setEntityDefs($name, $fieldDefs, $scope)
{
$fieldDef = $this->normalizeDefs($name, $fieldDef, $scope);
$fieldDefs = $this->normalizeDefs($name, $fieldDefs, $scope);
$this->getMetadata()->set($this->metadataType, $scope, $fieldDef);
$this->getMetadata()->set($this->metadataType, $scope, $fieldDefs);
$res = $this->getMetadata()->save();
return $res;
@@ -161,12 +161,12 @@ class FieldManager
return $this->getLanguage()->save();
}
protected function getFieldDef($name, $scope)
protected function getFieldDefs($name, $scope)
{
return $this->getMetadata()->get($this->metadataType.'.'.$scope.'.fields.'.$name);
}
protected function getLinkDef($name, $scope)
protected function getLinkDefs($name, $scope)
{
return $this->getMetadata()->get($this->metadataType.'.'.$scope.'.links.'.$name);
}
@@ -175,11 +175,11 @@ class FieldManager
* Prepare input fieldDefs, remove unnecessary fields
*
* @param string $fieldName
* @param array $fieldDef
* @param array $fieldDefs
* @param string $scope
* @return array
*/
protected function prepareFieldDef($name, $fieldDef, $scope)
protected function prepareFieldDefs($name, $fieldDefs, $scope)
{
$unnecessaryFields = array(
'name',
@@ -188,53 +188,53 @@ class FieldManager
);
foreach ($unnecessaryFields as $fieldName) {
if (isset($fieldDef[$fieldName])) {
unset($fieldDef[$fieldName]);
if (isset($fieldDefs[$fieldName])) {
unset($fieldDefs[$fieldName]);
}
}
$currentOptionList = array_keys((array) $this->getFieldDef($name, $scope));
foreach ($fieldDef as $defName => $defValue) {
$currentOptionList = array_keys((array) $this->getFieldDefs($name, $scope));
foreach ($fieldDefs as $defName => $defValue) {
if ( (!isset($defValue) || $defValue === '') && !in_array($defName, $currentOptionList) ) {
unset($fieldDef[$defName]);
unset($fieldDefs[$defName]);
}
}
return $fieldDef;
return $fieldDefs;
}
/**
* Add all needed block for a field defenition
*
* @param string $fieldName
* @param array $fieldDef
* @param array $fieldDefs
* @param string $scope
* @return array
*/
protected function normalizeDefs($fieldName, array $fieldDef, $scope)
protected function normalizeDefs($fieldName, array $fieldDefs, $scope)
{
$fieldDef = $this->prepareFieldDef($fieldName, $fieldDef, $scope);
$fieldDefs = $this->prepareFieldDefs($fieldName, $fieldDefs, $scope);
$metaFieldDef = $this->getMetadataHelper()->getFieldDefsInFieldMeta($fieldDef);
if (isset($metaFieldDef)) {
$fieldDef = Util::merge($metaFieldDef, $fieldDef);
$metaFieldDefs = $this->getMetadataHelper()->getFieldDefsInFieldMeta($fieldDefs);
if (isset($metaFieldDefs)) {
$fieldDefs = Util::merge($metaFieldDefs, $fieldDefs);
}
if (isset($fieldDef['linkDefs'])) {
$linkDefs = $fieldDef['linkDefs'];
unset($fieldDef['linkDefs']);
if (isset($fieldDefs['linkDefs'])) {
$linkDefs = $fieldDefs['linkDefs'];
unset($fieldDefs['linkDefs']);
}
$defs = array(
'fields' => array(
$fieldName => $fieldDef,
$fieldName => $fieldDefs,
),
);
/** Save links for a field. */
$metaLinkDef = $this->getMetadataHelper()->getLinkDefsInFieldMeta($scope, $fieldDef);
if (isset($linkDefs) || isset($metaLinkDef)) {
$linkDefs = Util::merge((array) $metaLinkDef, (array) $linkDefs);
$metaLinkDefs = $this->getMetadataHelper()->getLinkDefsInFieldMeta($scope, $fieldDefs);
if (isset($linkDefs) || isset($metaLinkDefs)) {
$linkDefs = Util::merge((array) $metaLinkDefs, (array) $linkDefs);
$defs['links'] = array(
$fieldName => $linkDefs,
);
@@ -248,12 +248,12 @@ class FieldManager
*
* @return boolean
*/
protected function isDefsChanged($name, $fieldDef, $scope)
protected function isDefsChanged($name, $fieldDefs, $scope)
{
$fieldDef = $this->prepareFieldDef($name, $fieldDef, $scope);
$currentFieldDef = $this->getFieldDef($name, $scope);
$fieldDefs = $this->prepareFieldDefs($name, $fieldDefs, $scope);
$currentFieldDefs = $this->getFieldDefs($name, $scope);
$this->isChanged = Util::isEquals($fieldDef, $currentFieldDef) ? false : true;
$this->isChanged = Util::isEquals($fieldDefs, $currentFieldDefs) ? false : true;
return $this->isChanged;
}
@@ -277,7 +277,7 @@ class FieldManager
*/
protected function isCore($name, $scope)
{
$existingField = $this->getFieldDef($name, $scope);
$existingField = $this->getFieldDefs($name, $scope);
if (isset($existingField) && (!isset($existingField[$this->customOptionName]) || !$existingField[$this->customOptionName])) {
return true;
}
+16 -17
View File
@@ -27,8 +27,8 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
return array ( 'database' =>
array (
return array (
'database' => array (
'driver' => 'pdo_mysql',
'host' => 'localhost',
'port' => '',
@@ -48,13 +48,10 @@ return array ( 'database' =>
'thousandSeparator' => ',',
'decimalMark' => '.',
'exportDelimiter' => ';',
'currencyList' =>
array (
),
'currencyList' => ['USD'],
'defaultCurrency' => 'USD',
'baseCurrency' => 'USD',
'currencyRates' => array(
),
'currencyRates' => [],
'outboundEmailIsShared' => true,
'outboundEmailFromName' => 'EspoCRM',
'outboundEmailFromAddress' => '',
@@ -64,7 +61,7 @@ return array ( 'database' =>
'smtpSecurity' => '',
'smtpUsername' => '',
'smtpPassword' => '',
'languageList' => array(
'languageList' => [
'en_US',
'de_DE',
'es_ES',
@@ -77,14 +74,14 @@ return array ( 'database' =>
'pt_BR',
'uk_UA',
'vi_VN'
),
],
'language' => 'en_US',
'logger' =>
array (
'path' => 'data/logs/espo.log',
'level' => 'WARNING', /** DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY */
'isRotate' => true, /** rotate log files every day */
'maxRotateFiles' => 30, /** max number of rotate files */
'rotation' => true,
'maxFileNumber' => 30,
),
'authenticationMethod' => 'Espo',
'globalSearchEntityList' =>
@@ -94,16 +91,16 @@ return array ( 'database' =>
'Lead',
'Opportunity',
),
"tabList" => array("Account", "Contact", "Lead", "Opportunity", "Calendar", "Meeting", "Call", "Task", "Case", "Email", "Document", "Campaign", "Stream"),
"quickCreateList" => array("Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case"),
"tabList" => ["Account", "Contact", "Lead", "Opportunity", "Calendar", "Meeting", "Call", "Task", "Case", "Email", "Document", "Campaign"],
"quickCreateList" => ["Account", "Contact", "Lead", "Opportunity", "Meeting", "Call", "Task", "Case"],
'calendarDefaultEntity' => 'Meeting',
'exportDisabled' => false,
'assignmentEmailNotifications' => false,
'assignmentEmailNotificationsEntityList' => array('Lead', 'Opportunity', 'Task', 'Case'),
'assignmentNotificationsEntityList' => array('Meeting', 'Call', 'Task', 'Email'),
'assignmentEmailNotificationsEntityList' => ['Lead', 'Opportunity', 'Task', 'Case'],
'assignmentNotificationsEntityList' => ['Meeting', 'Call', 'Task', 'Email'],
'emailMessageMaxSize' => 10,
'notificationsCheckInterval' => 10,
'disabledCountQueryEntityList' => array('Email'),
'disabledCountQueryEntityList' => ['Email'],
'maxEmailAccountCount' => 2,
'followCreatedEntities' => false,
'b2cMode' => false,
@@ -112,6 +109,8 @@ return array ( 'database' =>
'massEmailMaxPerHourCount' => 100,
'personalEmailMaxPortionSize' => 10,
'inboundEmailMaxPortionSize' => 20,
'isInstalled' => false,
'authTokenLifetime' => 0,
'authTokenMaxIdleTime' => 120,
'isInstalled' => false
);
@@ -125,7 +125,9 @@ return array ( 'defaultPermissions' =>
'maxEmailAccountCount',
'massEmailMaxPerHourCount',
'personalEmailMaxPortionSize',
'inboundEmailMaxPortionSize'
'inboundEmailMaxPortionSize',
'authTokenLifetime',
'authTokenMaxIdleTime'
),
'isInstalled' => false,
);
+1 -1
View File
@@ -106,7 +106,7 @@ class Avatar extends Image
ob_clean();
flush();
$identicon->displayImage($userId, $width, $this->getColor($userId));
echo $identicon->getImageData($userId, $width, $this->getColor($userId));
exit;
}
}
@@ -33,14 +33,17 @@ use Espo\ORM\Entity;
class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
{
public function afterSave(Entity $entity)
public function afterSave(Entity $entity, array $options = [])
{
if (!empty($options['silent']) || !empty($options['noNotifications'])) {
return;
}
if (
$this->getConfig()->get('assignmentEmailNotifications')
&&
$entity->has('assignedUserId')
&&
in_array($entity->getEntityName(), $this->getConfig()->get('assignmentEmailNotificationsEntityList', array()))
in_array($entity->getEntityType(), $this->getConfig()->get('assignmentEmailNotificationsEntityList', []))
) {
$userId = $entity->get('assignedUserId');
@@ -0,0 +1,70 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Jobs;
use \Espo\Core\Exceptions;
class AuthTokenControl extends \Espo\Core\Jobs\Base
{
public function run()
{
$authTokenLifetime = $this->getConfig()->get('authTokenLifetime');
$authTokenMaxIdleTime = $this->getConfig()->get('authTokenMaxIdleTime');
if (!$authTokenLifetime && !$authTokenMaxIdleTime) {
return;
}
$whereClause = array();
if ($authTokenLifetime) {
$dt = new \DateTime();
$dt->modify('-' . $authTokenLifetime . ' hours');
$authTokenLifetimeThreshold = $dt->format('Y-m-d H:i:s');
$whereClause['createdAt<'] = $authTokenLifetimeThreshold;
}
if ($authTokenMaxIdleTime) {
$dt = new \DateTime();
$dt->modify('-' . $authTokenMaxIdleTime . ' hours');
$authTokenMaxIdleTimeThreshold = $dt->format('Y-m-d H:i:s');
$whereClause['lastAccess<'] = $authTokenMaxIdleTimeThreshold;
}
$tokenList = $this->getEntityManager()->getRepository('AuthToken')->where($whereClause)->limit(0, 100)->find();
foreach ($tokenList as $token) {
$this->getEntityManager()->removeEntity($token);
}
}
}
+3 -1
View File
@@ -78,8 +78,10 @@ class Cleanup extends \Espo\Core\Jobs\Base
}
}
protected function getCleanupFromDate($format = 'Y-m-d')
protected function getCleanupFromDate()
{
$format = 'Y-m-d';
$datetime = new \DateTime();
$datetime->modify($this->period);
return $datetime->format($format);
@@ -1,4 +1,4 @@
<?php
<?php
/************************************************************************
* This file is part of EspoCRM.
*
@@ -40,13 +40,13 @@ class Ics
private $_s_description;
private $_s_html;
private $_s_who;
private $_s_email;
private $_s_uri;
private $_s_uid;
private $_s_summary;
@@ -54,7 +54,7 @@ class Ics
private $_s_output;
private $_s_prodid;
public function __construct($prodid, array $attributes = array())
{
if (!is_string($prodid) || $prodid === '') {
@@ -67,7 +67,7 @@ class Ics
$this->$key = $value;
}
}
public function __set($name, $value)
{
switch ($name) {
@@ -86,11 +86,11 @@ class Ics
case 'summary':
$this->_s_summary = $value;
break;
case 'who':
$this->_s_who = $value;
break;
case 'email':
$this->_s_email = $value;
break;
@@ -98,7 +98,7 @@ class Ics
case 'uri':
$this->_s_uri = $value;
break;
case 'uid':
$this->_s_uid = $value;
break;
@@ -114,7 +114,7 @@ class Ics
return $this;
}
public function __get($name) {
switch ($name)
{
@@ -137,15 +137,15 @@ class Ics
case 'uri':
return $this->_s_uri;
break;
case 'who':
return $this->_s_who;
break;
case 'email':
return $this->_s_email;
break;
case 'uid':
return $this->_s_uid;
break;
@@ -159,36 +159,34 @@ class Ics
break;
}
}
public function get()
{
($this->_s_output) ? $this->_s_output : $this->_generate();
return $this->_s_output;
}
private function _generate()
{
$this->_s_output = "BEGIN:VCALENDAR\n".
"VERSION:2.0\n".
"PRODID:-".$this->_s_prodid."\n".
"METHOD:REQUEST\n".
"BEGIN:VEVENT\n".
"DTSTART:".$this->_dateToCal($this->startDate)."\n".
"DTEND:".$this->_dateToCal($this->endDate)."\n".
"SUMMARY:New ".$this->_escapeString($this->summary)."\n".
"LOCATION:".$this->_escapeString($this->address)."\n".
"ORGANIZER;CN=".$this->_escapeString($this->who).":MAILTO:" . $this->_escapeString($this->email)."\n".
"DESCRIPTION:".$this->_escapeString($this->description)."\n".
"X-ALT-DESC;FMTTYPE=text/html:".$this->_escapeString($this->html)."\n".
"URL;VALUE=URI:".$this->_escapeString($this->uri)."\n".
"UID:".$this->uid."\n".
"SEQUENCE:0\n".
"DTSTAMP:".$this->_dateToCal(time())."\n".
"END:VEVENT\n".
"END:VCALENDAR\n";
"VERSION:2.0\n".
"PRODID:-".$this->_s_prodid."\n".
"METHOD:REQUEST\n".
"BEGIN:VEVENT\n".
"DTSTART:".$this->_dateToCal($this->startDate)."\n".
"DTEND:".$this->_dateToCal($this->endDate)."\n".
"SUMMARY:New ".$this->_escapeString($this->summary)."\n".
"LOCATION:".$this->_escapeString($this->address)."\n".
"ORGANIZER;CN=".$this->_escapeString($this->who).":MAILTO:" . $this->_escapeString($this->email)."\n".
"DESCRIPTION:".$this->_escapeString($this->description)."\n".
"UID:".$this->uid."\n".
"SEQUENCE:0\n".
"DTSTAMP:".$this->_dateToCal(time())."\n".
"END:VEVENT\n".
"END:VCALENDAR";
}
private function _dateToCal($timestamp)
{
return date('Ymd\THis\Z', ($timestamp) ? $timestamp : time());
@@ -197,6 +195,6 @@ class Ics
private function _escapeString($string)
{
return preg_replace('/([\,;])/','\\\$1', ($string) ? $string : '');
}
}
}
@@ -52,6 +52,20 @@ class TargetList extends \Espo\Core\Controllers\Record
return $this->getRecordService()->unlinkAll($data['id'], $data['link']);
}
public function postActionOptOut($params, $data)
{
if (empty($data['id'])) {
throw new BadRequest();
}
if (empty($data['targetType'])) {
throw new BadRequest();
}
if (empty($data['targetId'])) {
throw new BadRequest();
}
return $this->getRecordService()->optOut($data['id'], $data['targetType'], $data['targetId']);
}
public function postActionCancelOptOut($params, $data)
{
if (empty($data['id'])) {
@@ -58,12 +58,16 @@ class SendEmailReminders extends \Espo\Core\Jobs\Base
);
$pdo = $this->getEntityManager()->getPDO();
}
foreach ($collection as $i => $entity) {
if ($i >= self::MAX_PORTION_SIZE) {
break;
}
$emailReminder->send($entity);
try {
$emailReminder->send($entity);
} catch (\Exception $e) {
$GLOBALS['log']->error('Job SendEmailReminders '.$entity->id.': [' . $e->getCode() . '] ' .$e->getMessage());
}
$sql = "DELETE FROM `reminder` WHERE id = ". $pdo->quote($entity->id);
$pdo->query($sql);
@@ -0,0 +1,45 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class Account extends \Espo\Core\ORM\Repositories\RDB
{
public function afterSave(Entity $entity, array $options)
{
parent::afterSave($entity, $options);
if ($entity->has('targetListId')) {
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
}
}
}
@@ -10,8 +10,10 @@
"sicCode": "WKN Nummer",
"industry": "Branche",
"type": "Typ",
"contactRole": "Funktion",
"campaign": "Kampagne"
"contactRole": "Rolle",
"campaign": "Kampagne",
"targetLists": "Kontaktlisten",
"targetList": "Kontaktliste"
},
"links": {
"contacts": "Kontakte",
@@ -40,6 +42,7 @@
"Automotive": "Automobilindustrie",
"Banking": "Bankwesen",
"Biotechnology": "Biotechnologie",
"Building Materials & Equipment": "Baumaterial & -ausstattung",
"Chemical": "Chemieindustrie",
"Computer": "Informationstechnologie",
"Education": "Bildungswesen",
@@ -14,8 +14,7 @@
"reminders": "Erinnerungen",
"account": "Firma"
},
"links": {
},
"links": {},
"options": {
"status": {
"Planned": "Geplant",
@@ -7,6 +7,7 @@
"startDate": "Startdatum",
"endDate": "Enddatum",
"targetLists": "Kontaktlisten",
"excludingTargetLists": "Kontaktlisten ausschließen",
"sentCount": "Gesendet",
"openedCount": "Geöffnet",
"clickedCount": "Geklickt",
@@ -21,6 +22,7 @@
},
"links": {
"targetLists": "Kontaktlisten",
"excludingTargetLists": "Kontaktlisten ausschließen",
"accounts": "Firmen",
"contacts": "Kontakte",
"leads": "Interessenten",
@@ -58,5 +60,9 @@
},
"messages": {
"unsubscribed": "Sie wurden erfolgreich von unserem Verteiler entfernt"
},
"tooltips": {
"targetLists": "Zielkontakte die Nachrichten empfangen sollen ",
"excludingTargetLists": "Zielkontakte die keine Nachrichten empfangen sollen "
}
}
@@ -1,7 +1,7 @@
{
"fields": {
"url": "URL",
"urlToUse": "Code zu Einfügen anstelle einer URL",
"urlToUse": "Code zum Einfügen anstelle einer URL",
"campaign": "Kampagne"
},
"links": {
@@ -9,10 +9,10 @@
"priority": "Priorität",
"type": "Typ",
"description": "Beschreibung",
"inboundEmail": "Eingehende E-Mails"
"inboundEmail": "Eingehende E-Mail"
},
"links": {
"inboundEmail": "Eingehende E-Mails",
"inboundEmail": "Eingehende E-Mail",
"account": "Firma",
"contact": "Kontakt (Primär)",
"Contacts": "Kontakte",
@@ -30,7 +30,7 @@
"Rejected": "Abgelehnt",
"Duplicate": "Duplizieren"
},
"priority" : {
"priority": {
"Low": "Niedrig",
"Normal": "Normal",
"High": "Hoch",
@@ -69,18 +69,21 @@
"billingAddressCity": "Ort",
"billingAddressCountry": "Land",
"billingAddressPostalCode": "PLZ",
"billingAddressState": "Bundesland/Kanton",
"billingAddressState": "Bundesland\/Kanton",
"billingAddressStreet": "Straße",
"billingAddressMap": "Karte",
"addressCity": "Ort",
"addressStreet": "Straße",
"addressCountry": "Land",
"addressState": "Bundesland/Kanton",
"addressState": "Bundesland\/Kanton",
"addressPostalCode": "PLZ",
"addressMap": "Karte",
"shippingAddressCity": "Ort (Lieferadresse)",
"shippingAddressStreet": "Straße (Lieferadresse)",
"shippingAddressCountry": "Land (Lieferadresse)",
"shippingAddressState": "Bundesland/Kanton (Lieferadresse)",
"shippingAddressPostalCode": "PLZ (Lieferadresse)"
"shippingAddressState": "Bundesland\/Kanton (Lieferadresse)",
"shippingAddressPostalCode": "PLZ (Lieferadresse)",
"shippingAddressState": "Karte (Lieferadresse)"
},
"links": {
"contacts": "Kontakte",
@@ -56,7 +56,7 @@
},
"presetFilters": {
"active": "Aktiv",
"actual": "Tatsächlich",
"actual": "Aktuell",
"converted": "Umgewandelt"
}
}
@@ -11,10 +11,13 @@
"campaign": "Kampagne",
"emailTemplate": "E-Mail Vorlage",
"inboundEmail": "E-Mail Konto",
"targetLists": "Kontaktlisten"
"targetLists": "Kontaktlisten",
"excludingTargetLists": "Kontaktlisten ausschließen",
"optOutEntirely": "Überhaupt keine E-Mail"
},
"links": {
"targetLists": "Kontaktlisten",
"excludingTargetLists": "Kontaktlisten ausschließen",
"queueItems": "Warteschlangeneinträge",
"campaign": "Kampagne",
"emailTemplate": "E-Mail Vorlage",
@@ -37,5 +40,10 @@
"messages": {
"selectAtLeastOneTarget": "Zumindest ein Ziel auswählen",
"testSent": "Test E-Mail(s) die gesendet werden sollen"
},
"tooltips": {
"optOutEntirely": "Die E-Mail Adresse von abgemeldeten Empfängern wird mit 'Keine E-Mails' markiert, diese werden keine Massenaussendungen mehr erhalten.",
"targetLists": "Zielkontakte die Nachrichten empfangen sollen ",
"excludingTargetLists": "Zielkontakte die keine Nachrichten empfangen sollen "
}
}
@@ -13,8 +13,7 @@
"reminders": "Erinnerungen",
"account": "Firma"
},
"links": {
},
"links": {},
"options": {
"status": {
"Planned": "Geplant",
@@ -38,7 +37,7 @@
"Set Not Held": "Auf nicht gehalten setzen",
"Send Invitations": "Einladungen versenden",
"on time": "Aktuelle Zeit",
"before": "bevor"
"before": "vor"
},
"presetFilters": {
"planned": "Geplant",
@@ -28,7 +28,7 @@
"Id. Decision Makers": "Entscheider ident.",
"Perception Analysis": "Analyse Sichtweise",
"Proposal/Price Quote": "Preisangebot",
"Negotiation/Review": "Verhandlung/Überarbeitung",
"Negotiation/Review": "Verhandlung\/Überarbeitung",
"Closed Won": "Gewonnen",
"Closed Lost": "Verloren"
}
@@ -39,6 +39,6 @@
"presetFilters": {
"open": "Offen",
"won": "Gewonnen",
"lost": "Lost"
"lost": "Verloren"
}
}
@@ -10,8 +10,7 @@
"address": "Adresse",
"description": "Beschreibung"
},
"links": {
},
"links": {},
"labels": {
"Create Target": "Zielkontakt erstellen",
"Convert to Lead": "Zu Interessent umwandeln"
@@ -26,6 +26,7 @@
"labels": {
"Create TargetList": "Kontaktliste erstellen",
"Opted Out": "Keine E-Mails",
"Cancel Opt-Out": "Opt-Out zurücksetzen"
"Cancel Opt-Out": "Opt-Out zurücksetzen",
"Opt-Out": "Keine E-Mails"
}
}
@@ -25,7 +25,7 @@
"Canceled": "Storniert",
"Deferred": "Zurückgestellt"
},
"priority" : {
"priority": {
"Low": "Niedrig",
"Normal": "Normal",
"High": "Hoch",
@@ -37,7 +37,7 @@
"Complete": "Fertig"
},
"presetFilters": {
"actual": "Tatsächlich",
"actual": "Aktuell",
"completed": "Abgeschlossen",
"todays": "Heutige",
"overdue": "Überfällig"
@@ -11,7 +11,9 @@
"industry": "Industry",
"type": "Type",
"contactRole": "Title",
"campaign": "Campaign"
"campaign": "Campaign",
"targetLists": "Target Lists",
"targetList": "Target List"
},
"links": {
"contacts": "Contacts",
@@ -7,7 +7,14 @@
"parent": "Target",
"object": "Object",
"application": "Application",
"queueItem": "Queue Item"
"queueItem": "Queue Item",
"stringData": "String Data",
"stringAdditionalData": "String Additional Data"
},
"links": {
"queueItem": "Queue Item",
"parent": "Parent",
"object": "Object"
},
"options": {
"action": {
@@ -71,16 +71,19 @@
"billingAddressPostalCode": "Postal Code",
"billingAddressState": "State",
"billingAddressStreet": "Street",
"billingAddressMap": "Map",
"addressCity": "City",
"addressStreet": "Street",
"addressCountry": "Country",
"addressState": "State",
"addressPostalCode": "Postal Code",
"addressMap": "Map",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
"shippingAddressState": "State (Shipping)",
"shippingAddressPostalCode": "Postal Code (Shipping)"
"shippingAddressPostalCode": "Postal Code (Shipping)",
"shippingAddressState": "Map (Shipping)"
},
"links": {
"contacts": "Contacts",
@@ -26,6 +26,7 @@
"labels": {
"Create TargetList": "Create Target List",
"Opted Out": "Opted Out",
"Cancel Opt-Out": "Cancel Opt-Out"
"Cancel Opt-Out": "Cancel Opt-Out",
"Opt-Out": "Opt-Out"
}
}
@@ -11,7 +11,9 @@
"industry": "Industria",
"type": "Tipo",
"contactRole": "Título",
"campaign": "Campaña"
"campaign": "Campaña",
"targetLists": "Lista de Objetivos",
"targetList": "Lista de Objetivos"
},
"links": {
"contacts": "Contactos",
@@ -8,8 +8,8 @@
"endDate": "Fecha de Fin",
"targetLists": "Lista de Objetivos",
"excludingTargetLists": "Lista de Objetivos Excluídos",
"sentCount": "Enviados",
"openedCount": "Abiertos",
"sentCount": "Enviado",
"openedCount": "Abierto",
"clickedCount": "Cliqueados",
"optedOutCount": "optado por no",
"bouncedCount": "Rebotados",
@@ -11,8 +11,8 @@
},
"options": {
"action": {
"Sent": "Enviados",
"Opened": "Abiertos",
"Sent": "Enviado",
"Opened": "Abierto",
"Opted Out": "optado por no",
"Bounced": "Rebotados",
"Clicked": "Cliqueados",
@@ -23,8 +23,8 @@
"All": "Todos"
},
"presetFilters": {
"sent": "Enviados",
"opened": "Abiertos",
"sent": "Enviado",
"opened": "Abierto",
"optedOut": "optado por no",
"bounced": "Rebotados",
"clicked": "Cliqueados",
@@ -16,13 +16,13 @@
"options": {
"status": {
"Pending": "Pendiente",
"Sent": "Enviados",
"Sent": "Enviado",
"Failed": "Falló"
}
},
"presetFilters": {
"pending": "Pendiente",
"sent": "Enviados",
"sent": "Enviado",
"failed": "Falló"
}
}
@@ -71,16 +71,19 @@
"billingAddressPostalCode": "Código Postal",
"billingAddressState": "Estado/Distrito",
"billingAddressStreet": "Calle",
"billingAddressMap": "Mapa",
"addressCity": "Ciudad",
"addressStreet": "Calle",
"addressCountry": "País",
"addressState": "Estado/Distrito",
"addressPostalCode": "Código Postal",
"addressMap": "Mapa",
"shippingAddressCity": "Ciudad (Envío)",
"shippingAddressStreet": "Calle (Envío)",
"shippingAddressCountry": "País (Envío)",
"shippingAddressState": "Estado (Envío)",
"shippingAddressPostalCode": "Código Postal (Envío)"
"shippingAddressState": "State (Shipping)",
"shippingAddressPostalCode": "Código Postal (Envío)",
"shippingAddressState": "Estado (Envío)"
},
"links": {
"contacts": "Contactos",
@@ -26,6 +26,7 @@
"labels": {
"Create TargetList": "Crear un objetivo de lista",
"Opted Out": "optado por no",
"Cancel Opt-Out": "Cancelar Opt-Out"
"Cancel Opt-Out": "Cancelar Opt-Out",
"Opt-Out": "Opt-Out"
}
}
@@ -5,5 +5,6 @@
"emailAddress",
"industry",
"phoneNumber",
"targetLists",
"type"
]
@@ -0,0 +1,3 @@
[
"accounts"
]
@@ -0,0 +1,11 @@
{
"colors": {
"Meeting": "#558BBD",
"Call": "#CF605D",
"Task": "#76BA4E"
},
"scopeList": ["Meeting", "Call", "Task"],
"allDayScopeList": ["Task"],
"modeList": ["month", "agendaWeek", "agendaDay"],
"completedStatusList": ["Held", "Completed"]
}
@@ -1,10 +1,10 @@
{
"controller": "controllers/record",
"views": {
"list": "Crm:Document.List"
"list": "crm:views/document/list"
},
"modalViews": {
"select": "Crm:Document.Modals.SelectRecords"
"select": "crm:views/document/modals/select-records"
},
"filterList": [
"active",
@@ -6,7 +6,7 @@
{
"name":"optedOut",
"label":"Opted Out",
"view":"Crm:TargetList.Record.Panels.OptedOut"
"view":"crm:views/target-list/record/panels/opted-out"
}
]
},
@@ -22,7 +22,8 @@
}
}
],
"rowActionsView": "views/record/row-actions/relationship-no-remove"
"rowActionsView": "crm:views/target-list/record/row-actions/default",
"view": "crm:views/target-list/record/panels/relationship"
},
"leads": {
"actionList": [
@@ -35,7 +36,8 @@
}
}
],
"rowActionsView": "views/record/row-actions/relationship-no-remove"
"rowActionsView": "crm:views/target-list/record/row-actions/default",
"view": "crm:views/target-list/record/panels/relationship"
},
"accounts": {
"actionList": [
@@ -48,7 +50,8 @@
}
}
],
"rowActionsView": "views/record/row-actions/relationship-no-remove"
"rowActionsView": "crm:views/target-list/record/row-actions/default",
"view": "crm:views/target-list/record/panels/relationship"
},
"users": {
"create": false,
@@ -62,7 +65,8 @@
}
}
],
"rowActionsView": "views/record/row-actions/relationship-no-remove"
"rowActionsView": "crm:views/target-list/record/row-actions/default",
"view": "crm:views/target-list/record/panels/relationship"
}
}
}
@@ -136,6 +136,23 @@
},
"teams": {
"type": "linkMultiple"
},
"targetLists": {
"type": "linkMultiple",
"layoutDetailDisabled": true,
"layoutListDisabled": true,
"layoutMassUpdateDisabled": true,
"importDisabled": true,
"noLoad": true
},
"targetList": {
"type": "link",
"notStorable": true,
"layoutDetailDisabled": true,
"layoutListDisabled": true,
"layoutMassUpdateDisabled": true,
"layoutFiltersDisabled": true,
"entity": "TargetList"
}
},
"links": {
@@ -39,7 +39,8 @@
},
"emailTemplate": {
"type": "link",
"required": true
"required": true,
"view": "crm:views/mass-email/fields/email-template"
},
"campaign": {
"type": "link"
@@ -45,6 +45,8 @@ class Activities extends \Espo\Core\Services\Base
'selectManagerFactory'
);
protected $calendarScopeList = ['Meeting', 'Call', 'Task'];
protected function getPDO()
{
return $this->getEntityManager()->getPDO();
@@ -80,337 +82,420 @@ class Activities extends \Espo\Core\Services\Base
return in_array($scope, ['Contact', 'Lead', 'User']);
}
protected function getUserMeetingQuery($id, $op, $notIn)
protected function getUserMeetingQuery($id, $op = 'IN', $statusList = null)
{
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT meeting.id AS 'id', meeting.name AS 'name', meeting.date_start AS 'dateStart', meeting.date_end AS 'dateEnd', 'Meeting' AS '_scope',
meeting.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
meeting.parent_type AS 'parentType', meeting.parent_id AS 'parentId', meeting.status AS status, meeting.created_at AS createdAt
FROM `meeting`
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = meeting.assigned_user_id
JOIN `meeting_user` AS `usersMiddle` ON usersMiddle.meeting_id = meeting.id AND usersMiddle.deleted = 0 AND usersMiddle.status <> 'Declined'
WHERE meeting.deleted = 0 AND usersMiddle.user_id = ".$pdo->quote($id)."
";
if (!empty($notIn)) {
$sql .= "
AND meeting.status {$op} ('". implode("', '", $notIn) . "')
";
$selectManager = $this->getSelectManagerFactory()->create('Meeting');
$selectParams = array(
'select' => [
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
['VALUE:Meeting', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'leftJoins' => ['users'],
'whereClause' => array(
'usersMiddle.userId' => $id
)
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$selectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Meeting', $selectParams);
return $sql;
}
protected function getUserCallQuery($id, $op, $notIn)
protected function getUserCallQuery($id, $op = 'IN', $statusList = null)
{
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT call.id AS 'id', call.name AS 'name', call.date_start AS 'dateStart', call.date_end AS 'dateEnd', 'Call' AS '_scope',
call.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
call.parent_type AS 'parentType', call.parent_id AS 'parentId', call.status AS status, call.created_at AS createdAt
FROM `call`
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = call.assigned_user_id
JOIN `call_user` AS `usersMiddle` ON usersMiddle.call_id = call.id AND usersMiddle.deleted = 0 AND usersMiddle.status <> 'Declined'
WHERE call.deleted = 0 AND usersMiddle.user_id = ".$pdo->quote($id)."
";
if (!empty($notIn)) {
$sql .= "
AND call.status {$op} ('". implode("', '", $notIn) . "')
";
$selectManager = $this->getSelectManagerFactory()->create('Call');
$selectParams = array(
'select' => [
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
['VALUE:Call', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'leftJoins' => ['users'],
'whereClause' => array(
'usersMiddle.userId' => $id
)
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$selectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Call', $selectParams);
return $sql;
}
protected function getUserEmailQuery($id, $op, $notIn)
protected function getUserEmailQuery($id, $op = 'IN', $statusList = null)
{
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT email.id AS 'id', email.name AS 'name', email.date_sent AS 'dateStart', '' AS 'dateEnd', 'Email' AS '_scope',
email.assigned_user_id AS assignedUserId, TRIM(CONCAT(assignedUser.first_name, ' ', assignedUser.last_name)) AS assignedUserName,
email.parent_type AS 'parentType', email.parent_id AS 'parentId', email.status AS status, email.created_at AS createdAt
FROM `email`
LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = email.assigned_user_id
JOIN `email_user` AS `usersMiddle` ON usersMiddle.email_id = email.id AND usersMiddle.deleted = 0
WHERE email.deleted = 0 AND usersMiddle.user_id = ".$pdo->quote($id)."
";
if (!empty($notIn)) {
$sql .= "
AND email.status {$op} ('". implode("', '", $notIn) . "')
";
$selectManager = $this->getSelectManagerFactory()->create('Email');
$selectParams = array(
'select' => [
'id',
'name',
['dateSent', 'dateStart'],
['VALUE:', 'dateEnd'],
['VALUE:Email', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'leftJoins' => ['users'],
'whereClause' => array(
'usersMiddle.userId' => $id
)
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$selectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Email', $selectParams);
return $sql;
}
protected function getMeetingQuery($scope, $id, $op = 'IN', $notIn = [])
protected function getMeetingQuery($scope, $id, $op = 'IN', $statusList = null)
{
$methodName = 'get' .$scope . 'MeetingQuery';
if (method_exists($this, $methodName)) {
return $this->$methodName($id, $op, $notIn);
return $this->$methodName($id, $op, $statusList);
}
$baseSql = "
SELECT meeting.id AS 'id', meeting.name AS 'name', meeting.date_start AS 'dateStart', meeting.date_end AS 'dateEnd', 'Meeting' AS '_scope',
meeting.assigned_user_id AS assignedUserId, TRIM(CONCAT(user.first_name, ' ', user.last_name)) AS assignedUserName,
meeting.parent_type AS 'parentType', meeting.parent_id AS 'parentId', meeting.status AS status, meeting.created_at AS createdAt
FROM `meeting`
LEFT JOIN `user` ON user.id = meeting.assigned_user_id
";
$selectManager = $this->getSelectManagerFactory()->create('Meeting');
$baseSelectParams = array(
'select' => [
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
['VALUE:Meeting', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'whereClause' => array()
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$baseSelectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectParams = $baseSelectParams;
$sql = $baseSql;
$sql .= "
WHERE
meeting.deleted = 0 AND
";
if ($scope == 'Account') {
$sql .= "
(meeting.parent_type = ".$this->getPDO()->quote($scope)." AND meeting.parent_id = ".$this->getPDO()->quote($id)."
OR
meeting.account_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause'][] = array(
'OR' => array(
array(
'parentId' => $id,
'parentType' => 'Account'
),
array(
'accountId' => $id
)
)
);
} else {
$sql .= "
(meeting.parent_type = ".$this->getPDO()->quote($scope)." AND meeting.parent_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause']['parentId'] = $id;
$selectParams['whereClause']['parentType'] = $scope;
}
if (!empty($notIn)) {
$sql .= "
AND meeting.status {$op} ('". implode("', '", $notIn) . "')
";
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Meeting', $selectParams);
if ($this->isPerson($scope)) {
$sql = $sql . "
UNION
" . $baseSql;
$link = null;
switch ($scope) {
case 'Contact':
$joinTable = 'contact_meeting';
$key = 'contact_id';
$link = 'contacts';
break;
case 'Lead':
$joinTable = 'lead_meeting';
$key = 'lead_id';
$link = 'leads';
break;
case 'User':
$joinTable = 'meeting_user';
$key = 'user_id';
$link = 'users';
break;
}
$sql .= "
JOIN `{$joinTable}` ON
meeting.id = {$joinTable}.meeting_id AND
{$joinTable}.deleted = 0 AND
{$joinTable}.{$key} = ".$this->getPDO()->quote($id)."
";
$sql .= "
WHERE
(
meeting.parent_type <> ".$this->getPDO()->quote($scope)." OR
meeting.parent_id <> ".$this->getPDO()->quote($id)." OR
meeting.parent_type IS NULL OR
meeting.parent_id IS NULL
) AND
meeting.deleted = 0
";
if (!empty($notIn)) {
$sql .= "
AND meeting.status {$op} ('". implode("', '", $notIn) . "')
";
}
if ($link) {
$selectParams = $baseSelectParams;
$selectManager->addJoin($link, $selectParams);
$selectParams['whereClause'][$link .'.id'] = $id;
$selectParams['whereClause'][] = array(
'OR' => array(
'parentType!=' => $scope,
'parentId!=' => $id,
'parentType' => null,
'parentId' => null
)
);
$selectManager->applyAccess($selectParams);
$sql .= ' UNION ' . $this->getEntityManager()->getQuery()->createSelectQuery('Meeting', $selectParams);
}
}
return $sql;
}
protected function getCallQuery($scope, $id, $op = 'IN', $notIn = [])
protected function getCallQuery($scope, $id, $op = 'IN', $statusList = null)
{
$methodName = 'get' .$scope . 'CallQuery';
if (method_exists($this, $methodName)) {
return $this->$methodName($id, $op, $notIn);
}
$baseSql = "
SELECT call.id AS 'id', call.name AS 'name', call.date_start AS 'dateStart', call.date_end AS 'dateEnd', 'Call' AS '_scope',
call.assigned_user_id AS assignedUserId, TRIM(CONCAT(user.first_name, ' ', user.last_name)) AS assignedUserName,
call.parent_type AS 'parentType', call.parent_id AS 'parentId', call.status AS status, call.created_at AS createdAt
FROM `call`
LEFT JOIN `user` ON user.id = call.assigned_user_id
";
$selectManager = $this->getSelectManagerFactory()->create('Call');
$baseSelectParams = array(
'select' => [
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
['VALUE:Call', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'whereClause' => array()
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$baseSelectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectParams = $baseSelectParams;
$sql = $baseSql;
$sql .= "
WHERE
call.deleted = 0 AND
";
if ($scope == 'Account') {
$sql .= "
(call.parent_type = ".$this->getPDO()->quote($scope)." AND call.parent_id = ".$this->getPDO()->quote($id)."
OR
call.account_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause'][] = array(
'OR' => array(
array(
'parentId' => $id,
'parentType' => 'Account'
),
array(
'accountId' => $id
)
)
);
} else {
$sql .= "
(call.parent_type = ".$this->getPDO()->quote($scope)." AND call.parent_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause']['parentId'] = $id;
$selectParams['whereClause']['parentType'] = $scope;
}
if (!empty($notIn)) {
$sql .= "
AND call.status {$op} ('". implode("', '", $notIn) . "')
";
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Call', $selectParams);
if ($this->isPerson($scope)) {
$sql = $sql . "
UNION
" . $baseSql;
$link = null;
switch ($scope) {
case 'Contact':
$joinTable = 'call_contact';
$key = 'contact_id';
$link = 'contacts';
break;
case 'Lead':
$joinTable = 'call_lead';
$key = 'lead_id';
$link = 'leads';
break;
case 'User':
$joinTable = 'call_user';
$key = 'user_id';
$link = 'users';
break;
}
$sql .= "
JOIN `{$joinTable}` ON
call.id = {$joinTable}.call_id AND
{$joinTable}.deleted = 0 AND
{$joinTable}.{$key} = ".$this->getPDO()->quote($id)."
";
$sql .= "
WHERE
(
call.parent_type <> ".$this->getPDO()->quote($scope)." OR
call.parent_id <> ".$this->getPDO()->quote($id)." OR
call.parent_type IS NULL OR
call.parent_id IS NULL
) AND
call.deleted = 0
";
if (!empty($notIn)) {
$sql .= "
AND call.status {$op} ('". implode("', '", $notIn) . "')
";
}
if ($link) {
$selectParams = $baseSelectParams;
$selectManager->addJoin($link, $selectParams);
$selectParams['whereClause'][$link .'.id'] = $id;
$selectParams['whereClause'][] = array(
'OR' => array(
'parentType!=' => $scope,
'parentId!=' => $id,
'parentType' => null,
'parentId' => null
)
);
$selectManager->applyAccess($selectParams);
$sql .= ' UNION ' . $this->getEntityManager()->getQuery()->createSelectQuery('Call', $selectParams);
}
}
return $sql;
}
protected function getEmailQuery($scope, $id, $op = 'IN', $notIn = [])
protected function getEmailQuery($scope, $id, $op = 'IN', $statusList = null)
{
$methodName = 'get' .$scope . 'EmailQuery';
if (method_exists($this, $methodName)) {
return $this->$methodName($id, $op, $notIn);
return $this->$methodName($id, $op, $statusList);
}
$baseSql = "
SELECT DISTINCT
email.id AS 'id', email.name AS 'name', email.date_sent AS 'dateStart', '' AS 'dateEnd', 'Email' AS '_scope',
email.assigned_user_id AS assignedUserId, TRIM(CONCAT(user.first_name, ' ', user.last_name)) AS assignedUserName,
email.parent_type AS 'parentType', email.parent_id AS 'parentId', email.status AS status, email.created_at AS createdAt
FROM `email`
LEFT JOIN `user` ON user.id = email.assigned_user_id
";
$selectManager = $this->getSelectManagerFactory()->create('Email');
$baseSelectParams = array(
'select' => [
'id',
'name',
['dateSent', 'dateStart'],
['VALUE:', 'dateEnd'],
['VALUE:Email', '_scope'],
'assignedUserId',
'assignedUserName',
'parentType',
'parentId',
'status',
'createdAt'
],
'whereClause' => array()
);
if (!empty($statusList)) {
$statusOpKey = 'status';
if ($op == 'NOT IN') {
$statusOpKey .= '!=';
}
$baseSelectParams['whereClause'][$statusOpKey] = $statusList;
}
$selectParams = $baseSelectParams;
$sql = $baseSql;
$sql .= "
WHERE
email.deleted = 0 AND
";
if ($scope == 'Account') {
$sql .= "
(email.parent_type = ".$this->getPDO()->quote($scope)." AND email.parent_id = ".$this->getPDO()->quote($id)."
OR
email.account_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause'][] = array(
'OR' => array(
array(
'parentId' => $id,
'parentType' => 'Account'
),
array(
'accountId' => $id
)
)
);
} else {
$sql .= "
(email.parent_type = ".$this->getPDO()->quote($scope)." AND email.parent_id = ".$this->getPDO()->quote($id).")
";
$selectParams['whereClause']['parentId'] = $id;
$selectParams['whereClause']['parentType'] = $scope;
}
if (!empty($notIn)) {
$sql .= "
AND email.status {$op} ('". implode("', '", $notIn) . "')
";
}
$selectManager->applyAccess($selectParams);
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Email', $selectParams);
if ($this->isPerson($scope) || $scope == 'Account') {
$sql = $sql . "
UNION
" . $baseSql;
$sql .= "
LEFT JOIN entity_email_address AS entity_email_address_2 ON
entity_email_address_2.email_address_id = email.from_email_address_id AND
entity_email_address_2.entity_type = " . $this->getPDO()->quote($scope) . " AND
entity_email_address_2.deleted = 0
$selectParams = $baseSelectParams;
$selectParams['customJoin'] .= "
LEFT JOIN entity_email_address AS entityEmailAddress2 ON
entityEmailAddress2.email_address_id = email.from_email_address_id AND
entityEmailAddress2.entity_type = " . $this->getPDO()->quote($scope) . " AND
entityEmailAddress2.deleted = 0
";
$sql .= "
WHERE
email.deleted = 0 AND
(
email.parent_type <> ".$this->getPDO()->quote($scope)." OR
email.parent_id <> ".$this->getPDO()->quote($id)." OR
email.parent_type IS NULL OR
email.parent_id IS NULL
) AND
(entity_email_address_2.entity_id = ".$this->getPDO()->quote($id).")
";
if (!empty($notIn)) {
$sql .= "
AND email.status {$op} ('". implode("', '", $notIn) . "')
";
}
$selectParams['whereClause'][] = array(
'OR' => array(
'parentType!=' => $scope,
'parentId!=' => $id,
'parentType' => null,
'parentId' => null
)
);
$selectParams['whereClause']['entityEmailAddress2.entityId'] = $id;
$selectManager->applyAccess($selectParams);
$sql .= "\n UNION \n" . $this->getEntityManager()->getQuery()->createSelectQuery('Email', $selectParams);
$sql = $sql . "
UNION
" . $baseSql;
$sql .= "
$selectParams = $baseSelectParams;
$selectParams['customJoin'] .= "
LEFT JOIN email_email_address ON
email_email_address.email_id = email.id AND
email_email_address.deleted = 0
LEFT JOIN entity_email_address AS entity_email_address_1 ON
entity_email_address_1.email_address_id = email_email_address.email_address_id AND
LEFT JOIN entity_email_address AS entityEmailAddress1 ON
entityEmailAddress1.email_address_id = email_email_address.email_address_id AND
entity_email_address_1.entity_type = " . $this->getPDO()->quote($scope) . " AND
entity_email_address_1.deleted = 0
entityEmailAddress1.entity_type = " . $this->getPDO()->quote($scope) . " AND
entityEmailAddress1.deleted = 0
";
$sql .= "
WHERE
email.deleted = 0 AND
(
email.parent_type <> ".$this->getPDO()->quote($scope)." OR
email.parent_id <> ".$this->getPDO()->quote($id)." OR
email.parent_type IS NULL OR
email.parent_id IS NULL
) AND
(entity_email_address_1.entity_id = ".$this->getPDO()->quote($id).")
";
if (!empty($notIn)) {
$sql .= "
AND email.status {$op} ('". implode("', '", $notIn) . "')
";
}
$selectParams['whereClause'][] = array(
'OR' => array(
'parentType!=' => $scope,
'parentId!=' => $id,
'parentType' => null,
'parentId' => null
)
);
$selectParams['whereClause']['entityEmailAddress1.entityId'] = $id;
$selectManager->applyAccess($selectParams);
$sql .= "\n UNION \n" . $this->getEntityManager()->getQuery()->createSelectQuery('Email', $selectParams);
}
return $sql;
}
protected function getResult($parts, $scope, $id, $params)
protected function getResultFromQueryParts($parts, $scope, $id, $params)
{
if (empty($parts)) {
return array(
'list' => [],
'total' => 0
);
}
$pdo = $this->getEntityManager()->getPDO();
$onlyScope = false;
@@ -500,7 +585,7 @@ class Activities extends \Espo\Core\Services\Base
if ($this->getAcl()->checkScope('Call')) {
$parts['Call'] = ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : [];
}
return $this->getResult($parts, $scope, $id, $params);
return $this->getResultFromQueryParts($parts, $scope, $id, $params);
}
public function getHistory($scope, $id, $params)
@@ -521,7 +606,7 @@ class Activities extends \Espo\Core\Services\Base
if ($this->getAcl()->checkScope('Email')) {
$parts['Email'] = ($fetchAll || $params['scope'] == 'Email') ? $this->getEmailQuery($scope, $id, 'IN', ['Archived', 'Sent']) : [];
}
$result = $this->getResult($parts, $scope, $id, $params);
$result = $this->getResultFromQueryParts($parts, $scope, $id, $params);
foreach ($result['list'] as &$item) {
if ($item['_scope'] == 'Email') {
@@ -532,6 +617,111 @@ class Activities extends \Espo\Core\Services\Base
return $result;
}
protected function getCalendarMeetingQuery($userId, $from, $to)
{
$selectManager = $this->getSelectManagerFactory()->create('Meeting');
$selectParams = array(
'select' => [
['VALUE:Meeting', 'scope'],
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
'status',
['VALUE:', 'dateStartDate'],
['VALUE:', 'dateEndDate'],
'parentType',
'parentId',
'createdAt'
],
'leftJoins' => ['users'],
'whereClause' => array(
'usersMiddle.userId' => $userId,
'dateStart>=' => $from,
'dateStart<' => $to,
'usersMiddle.status!=' => 'Declined'
)
);
return $this->getEntityManager()->getQuery()->createSelectQuery('Meeting', $selectParams);
}
protected function getCalendarCallQuery($userId, $from, $to)
{
$selectManager = $this->getSelectManagerFactory()->create('Call');
$selectParams = array(
'select' => [
['VALUE:Call', 'scope'],
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
'status',
['VALUE:', 'dateStartDate'],
['VALUE:', 'dateEndDate'],
'parentType',
'parentId',
'createdAt'
],
'leftJoins' => ['users'],
'whereClause' => array(
'usersMiddle.userId' => $userId,
'dateStart>=' => $from,
'dateStart<' => $to,
'usersMiddle.status!=' => 'Declined'
)
);
return $this->getEntityManager()->getQuery()->createSelectQuery('Call', $selectParams);
}
protected function getCalendarTaskQuery($userId, $from, $to)
{
$selectManager = $this->getSelectManagerFactory()->create('Task');
$selectParams = array(
'select' => [
['VALUE:Task', 'scope'],
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
'status',
['dateStartDate', 'dateStartDate'],
['dateEndDate', 'dateEndDate'],
'parentType',
'parentId',
'createdAt'
],
'whereClause' => array(
'assignedUserId' => $userId,
array(
'OR' => array(
array(
'dateEnd' => null,
'dateStart>=' => $from,
'dateStart<' => $to,
),
array(
'dateEnd>=' => $from,
'dateEnd<' => $to,
),
array(
'dateEndDate!=' => null,
'dateEndDate>=' => $from,
'dateEndDate<' => $to,
)
)
)
)
);
return $this->getEntityManager()->getQuery()->createSelectQuery('Task', $selectParams);
}
public function getEvents($userId, $from, $to, $scopeList = null)
{
$user = $this->getEntityManager()->getEntity('User', $userId);
@@ -543,80 +733,21 @@ class Activities extends \Espo\Core\Services\Base
$pdo = $this->getPDO();
if (is_null($scopeList)) {
$scopeList = ['Meeting', 'Call', 'Task'];
$scopeList = $this->calendarScopeList;
}
$sqlPartList = [];
if (in_array('Meeting', $scopeList) && $this->getAcl()->checkScope('Meeting')) {
$sqlPartList[] = "
SELECT
'Meeting' AS scope,
meeting.id AS id, meeting.name AS name,
meeting.date_start AS dateStart,
meeting.date_end AS dateEnd,
meeting.status AS status,
'' AS dateStartDate,
'' AS dateEndDate
FROM `meeting`
JOIN meeting_user ON meeting_user.meeting_id = meeting.id AND meeting_user.deleted = 0 AND meeting_user.status <> 'Declined'
WHERE
meeting.deleted = 0 AND
meeting.date_start >= ".$pdo->quote($from)." AND
meeting.date_start < ".$pdo->quote($to)." AND
meeting_user.user_id =".$pdo->quote($userId)."
";
};
if (in_array('Call', $scopeList) && $this->getAcl()->checkScope('Call')) {
$sqlPartList[] = "
SELECT
'Call' AS scope,
call.id AS id,
call.name AS name,
call.date_start AS dateStart,
call.date_end AS dateEnd,
call.status AS status,
'' AS dateStartDate,
'' AS dateEndDate
FROM `call`
JOIN call_user ON call_user.call_id = call.id AND call_user.deleted = 0 AND call_user.status <> 'Declined'
WHERE
call.deleted = 0 AND
call.date_start >= ".$pdo->quote($from)." AND
call.date_start < ".$pdo->quote($to)." AND
call_user.user_id = ".$pdo->quote($userId)."
";
}
if (in_array('Task', $scopeList) && $this->getAcl()->checkScope('Task')) {
$sqlPartList[] = "
SELECT
'Task' AS scope,
task.id AS id,
task.name AS name,
task.date_start AS dateStart,
task.date_end AS dateEnd,
task.status AS status,
task.date_start_date AS dateStartDate,
task.date_end_date AS dateEndDate
FROM `task`
WHERE
task.deleted = 0 AND
(
(
task.date_end IS NULL AND
task.date_start >= ".$pdo->quote($from)." AND
task.date_start < ".$pdo->quote($to)."
) OR (
task.date_end >= ".$pdo->quote($from)." AND
task.date_end < ".$pdo->quote($to)."
) OR (
task.date_end_date IS NOT NULL AND
task.date_end_date >= ".$pdo->quote($from)." AND
task.date_end_date < ".$pdo->quote($to)."
)
) AND
task.assigned_user_id = ".$pdo->quote($userId)."
";
foreach ($scopeList as $scope) {
if (!in_array($scope, $this->calendarScopeList)) {
continue;
}
if ($this->getAcl()->checkScope($scopeList)) {
$methodName = 'getCalendar' . $scope . 'Query';
if (method_exists($this, $methodName)) {
$sqlPartList[] = $this->$methodName($userId, $from, $to);
}
}
}
if (empty($sqlPartList)) {
@@ -694,7 +825,7 @@ class Activities extends \Espo\Core\Services\Base
return $result;
}
public function getUpcomingActivities($userId, $params)
public function getUpcomingActivities($userId, $params = array())
{
$user = $this->getEntityManager()->getEntity('User', $userId);
$this->accessCheck($user);
@@ -716,7 +847,11 @@ class Activities extends \Espo\Core\Services\Base
$selectManager->applyAccess($selectParams);
$selectManager->applyTextFilter($query, $selectParams);
if (!empty($prams['textFilter'])) {
$selectManager->applyTextFilter($prams['textFilter'], $selectParams);
}
$selectManager->applyPrimaryFilter('planned', $selectParams);
$selectManager->applyBoolFilter('onlyMy', $selectParams);
$selectManager->applyWhere(array(
@@ -97,19 +97,27 @@ class MassEmail extends \Espo\Services\Record
}
}
$targetHash = array();
$metTargetHash = array();
$metEmailAddressHash = array();
$entityList = [];
$pdo = $this->getEntityManager()->getPDO();
if (!$isTest) {
if (!$massEmail->has('excludingTargetListsIds')) {
$massEmail->loadLinkMultipleField('excludingTargetLists');
$excludingTargetListList = $massEmail->get('excludingTargetLists');
foreach ($excludingTargetListList as $excludingTargetList) {
foreach (['accounts', 'contacts', 'leads', 'users'] as $link) {
foreach ($excludingTargetList->get($link) as $excludingTarget) {
$hashId = $excludingTarget->getEntityType() . '-'. $excludingTarget->id;
$metTargetHash[$hashId] = true;
$emailAddress = $excludingTarget->get('emailAddress');
if ($emailAddress) {
$metEmailAddressHash[$emailAddress] = true;
}
}
}
}
$excludingTargetListIdList = $massEmail->get('excludingTargetListsIds');
$targetListCollection = $massEmail->get('targetLists');
foreach ($targetListCollection as $targetList) {
$accountList = $targetList->get('accounts', array(
@@ -119,30 +127,20 @@ class MassEmail extends \Espo\Services\Record
));
foreach ($accountList as $account) {
$hashId = $account->getEntityType() . '-'. $account->id;
if (!empty($targetHash[$hashId])) {
$emailAddress = $account->get('emailAddress');
if (empty($emailAddress)) {
continue;
}
$toExclude = false;
foreach ($excludingTargetListIdList as $excludingTargetListId) {
$sql = "
SELECT id FROM account_target_list
WHERE
account_id = ".$pdo->quote($account->id)." AND
target_list_id = ".$pdo->quote($excludingTargetListId)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if ($sth->fetch()) {
$toExclude = true;
break;
}
}
if ($toExclude) {
if (!empty($metEmailAddressHash[$emailAddress])) {
continue;
}
if (!empty($metTargetHash[$hashId])) {
continue;
}
$entityList[] = $account;
$targetHash[$hashId] = true;
$metTargetHash[$hashId] = true;
$metEmailAddressHash[$emailAddress] = true;
}
$contactList = $targetList->get('contacts', array(
@@ -152,30 +150,20 @@ class MassEmail extends \Espo\Services\Record
));
foreach ($contactList as $contact) {
$hashId = $contact->getEntityType() . '-'. $contact->id;
if (!empty($targetHash[$hashId])) {
$emailAddress = $contact->get('emailAddress');
if (empty($emailAddress)) {
continue;
}
$toExclude = false;
foreach ($excludingTargetListIdList as $excludingTargetListId) {
$sql = "
SELECT id FROM contact_target_list
WHERE
contact_id = ".$pdo->quote($contact->id)." AND
target_list_id = ".$pdo->quote($excludingTargetListId)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if ($sth->fetch()) {
$toExclude = true;
break;
}
}
if ($toExclude) {
if (!empty($metEmailAddressHash[$emailAddress])) {
continue;
}
if (!empty($metTargetHash[$hashId])) {
continue;
}
$entityList[] = $contact;
$targetHash[$hashId] = true;
$metTargetHash[$hashId] = true;
$metEmailAddressHash[$emailAddress] = true;
}
$leadList = $targetList->get('leads', array(
@@ -185,30 +173,20 @@ class MassEmail extends \Espo\Services\Record
));
foreach ($leadList as $lead) {
$hashId = $lead->getEntityType() . '-'. $lead->id;
if (!empty($targetHash[$hashId])) {
$emailAddress = $lead->get('emailAddress');
if (empty($emailAddress)) {
continue;
}
$toExclude = false;
foreach ($excludingTargetListIdList as $excludingTargetListId) {
$sql = "
SELECT id FROM lead_target_list
WHERE
lead_id = ".$pdo->quote($lead->id)." AND
target_list_id = ".$pdo->quote($excludingTargetListId)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if ($sth->fetch()) {
$toExclude = true;
break;
}
}
if ($toExclude) {
if (!empty($metEmailAddressHash[$emailAddress])) {
continue;
}
if (!empty($metTargetHash[$hashId])) {
continue;
}
$entityList[] = $lead;
$targetHash[$hashId] = true;
$metTargetHash[$hashId] = true;
$metEmailAddressHash[$emailAddress] = true;
}
$userList = $targetList->get('users', array(
@@ -218,30 +196,20 @@ class MassEmail extends \Espo\Services\Record
));
foreach ($userList as $user) {
$hashId = $user->getEntityType() . '-'. $user->id;
if (!empty($targetHash[$hashId])) {
$emailAddress = $user->get('emailAddress');
if (empty($emailAddress)) {
continue;
}
$toExclude = false;
foreach ($excludingTargetListIdList as $excludingTargetListId) {
$sql = "
SELECT id FROM target_list_user
WHERE
user_id = ".$pdo->quote($user->id)." AND
target_list_id = ".$pdo->quote($excludingTargetListId)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if ($sth->fetch()) {
$toExclude = true;
break;
}
}
if ($toExclude) {
if (!empty($metEmailAddressHash[$emailAddress])) {
continue;
}
if (!empty($metTargetHash[$hashId])) {
continue;
}
$entityList[] = $user;
$targetHash[$hashId] = true;
$metTargetHash[$hashId] = true;
$metEmailAddressHash[$emailAddress] = true;
}
}
}
@@ -168,6 +168,33 @@ class TargetList extends \Espo\Services\Record
);
}
public function optOut($id, $targetType, $targetId)
{
$targetList = $this->getEntityManager()->getEntity('TargetList', $id);
if (!$targetList) {
throw new NotFound();
}
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
if (!$target) {
throw new NotFound();
}
$map = array(
'Account' => 'accounts',
'Contact' => 'contacts',
'Lead' => 'leads',
'User' => 'users'
);
if (empty($map[$targetType])) {
throw new Error();
}
$link = $map[$targetType];
return $this->getEntityManager()->getRepository('TargetList')->relate($targetList, $link, $targetId, array(
'optedOut' => true
));
}
public function cancelOptOut($id, $targetType, $targetId)
{
$targetList = $this->getEntityManager()->getEntity('TargetList', $id);
+5 -2
View File
@@ -336,6 +336,9 @@ abstract class Base
$relationsToJoin = array();
if (is_array($select) && is_array($fieldDefs)) {
foreach ($select as $field) {
if (is_array($field)) {
continue;
}
if (!empty($fieldDefs[$field]) && !empty($fieldDefs[$field]['type']) && $fieldDefs[$field]['type'] == 'foreign' && !empty($fieldDefs[$field]['relation'])) {
$relationsToJoin[] = $fieldDefs[$field]['relation'];
}
@@ -659,10 +662,10 @@ abstract class Base
}
$oppose = '';
if ($operator == '<>') {
$oppose = 'NOT';
$oppose = 'NOT ';
}
if (!empty($valArr)) {
$whereParts[] = $leftPart . " {$oppose} IN " . "(" . implode(',', $valArr) . ")";
$whereParts[] = $leftPart . " {$oppose}IN " . "(" . implode(',', $valArr) . ")";
} else {
$whereParts[] = " 0";
}
@@ -9,7 +9,7 @@
"Customization": "Anpassung",
"Available Fields": "Verfügbare Felder",
"Layout": "Aktuelles Layout",
"Entity Manager": "Entitäts-/Modulmanager",
"Entity Manager": "Entitäts-\/Modulmanager",
"Add Panel": "Panel hinzufügen",
"Add Field": "Feld hinzufügen",
"Settings": "Einstellungen",
@@ -21,6 +21,7 @@
"Roles": "Rollen",
"Outbound Emails": "Ausgehende E-Mails",
"Group Email Accounts": "Gruppen E-Mail Konten",
"Personal Email Accounts": "Persönliche E-Mail Konten",
"Inbound Emails": "Eingehende E-Mails",
"Email Templates": "E-Mail Vorlagen",
"Import": "Import",
@@ -42,8 +43,8 @@
"Ready for installation": "Bereit für Installation",
"Uninstalling...": "Deinstalliere...",
"Uninstalled": "Deinstalliert",
"Create Entity": "Entität/Modul erstellen",
"Edit Entity": "Entität/Modul bearbeiten",
"Create Entity": "Entität\/Modul erstellen",
"Edit Entity": "Entität\/Modul bearbeiten",
"Create Link": "Relation erstellen",
"Edit Link": "Relation bearbeiten",
"Notifications": "Benachrichtigungen",
@@ -62,7 +63,7 @@
},
"fieldTypes": {
"address": "Adresse",
"array": "Mehrfachauswahl",
"array": "Liste",
"foreign": "Fremdbezug",
"duration": "Dauer",
"password": "Passwort",
@@ -71,8 +72,8 @@
"bool": "Bool",
"currency": "Währung",
"date": "Datum",
"datetime": "Datum/Zeit",
"datetimeOptional": "Datum/Datum-Zeit",
"datetime": "Datum\/Zeit",
"datetimeOptional": "Datum\/Datum-Zeit",
"email": "E-Mail",
"enum": "Einfachauswahl",
"enumInt": "Einfachauswahl Ganzzahlwerte",
@@ -93,8 +94,9 @@
"attachmentMultiple": "Mehrfach Anhänge",
"rangeInt": "Bereich Ganzzahl",
"rangeFloat": "Bereich Fließkommawerte",
"rangeCurrency": "bereich Währung",
"wysiwyg": "Wysiwyg"
"rangeCurrency": "Bereich Währung",
"wysiwyg": "Wysiwyg",
"map": "Karte"
},
"fields": {
"type": "Typ",
@@ -119,17 +121,18 @@
"isSorted": "Sortiert (alphabetisch)",
"audited": "Auditiert",
"trim": "Abschneiden",
"height": "Height (px)"
"height": "Höhe (px)",
"provider": "Provider"
},
"messages": {
"upgradeVersion": "Ihr EspoCRM wird nun auf Version <strong>{version}</strong> aktualisiert. Dies kann einige Zeit dauern.",
"upgradeDone": "Ihr EspoCRM wurde auf Version <strong>{version}</strong> aktualisiert.",
"upgradeVersion": "Ihr EspoCRM wird nun auf Version <strong>{version}<\/strong> aktualisiert. Dies kann einige Zeit dauern.",
"upgradeDone": "Ihr EspoCRM wurde auf Version <strong>{version}<\/strong> aktualisiert.",
"upgradeBackup": "Wie empfehlen VOR einer Aktualisierung die Espo Dateien sowie die Datenbank zu sichern.",
"thousandSeparatorEqualsDecimalMark": "Das Tausendertrennzeichen kann nicht gleich dem Dezimaltrennzeichen sein",
"userHasNoEmailAddress": "Der Benutzer hat keine E-Mail Adresse.",
"selectEntityType": "Modul/Entität links auswählen",
"selectEntityType": "Modul\/Entität links auswählen",
"selectUpgradePackage": "Aktualisierungspaket auswählen",
"downloadUpgradePackage": "Aktualisierungspaket(e) <a href=\"{url}\">hier </a>herunterladen.",
"downloadUpgradePackage": "Aktualisierungspaket(e) <a href=\"{url}\">hier <\/a>herunterladen.",
"selectLayout": "Layout zum Editieren links auswählen",
"selectExtensionPackage": "Erweiterungspaket auswählen",
"extensionInstalled": "Erweiterung {name} {version} wurde installiert.",
@@ -147,16 +150,17 @@
"roles": "Rollenverwaltung.",
"outboundEmails": "SMTP Einstellungen für ausgehende E-Mails.",
"groupEmailAccounts": "IMAP Gruppenkonten. E-Mail Import und E-Mails für Fälle.",
"personalEmailAccounts": "E-Mail Konten des Benutzers.",
"emailTemplates": "Vorlagen für ausgehende E-Mails.",
"import": "Datenimport aus CSV Datei.",
"layoutManager": "Layouts anpassen (Liste, Detailansicht, Bearbeitungsansicht, Suche, Massenaktualisierung).",
"entityManager": "Module/Entitäten selbst erstellen, existente bearbeiten, Felder und Relationen verwalten.",
"entityManager": "Module\/Entitäten selbst erstellen und existente bearbeiten. Felder und Relationen verwalten.",
"userInterface": "Benutzeroberfläche anpassen.",
"authTokens": "Aktive Auth Sessions. IP Adresse und letztes Zugriffsdatum,",
"authentication": "Authentifizierungs Einstellungen.",
"currency": "Währungseinstellungen und Kurse",
"extensions": "Erweiterungen installieren oder deinstallieren",
"integrations": "Integration mit Drittanbietern ",
"integrations": "Integration mit Drittanbietern",
"notifications": "In-App und E-Mail Benachrichtigungseinstellungen.",
"inboundEmails": "Einstellungen für eingehende E-Mails",
"emailFilters": "E-Mails auf die die angegebenen Filter zutreffen werden nicht importiert"
@@ -6,7 +6,8 @@
"autorefreshInterval": "Aktualisierungsintervall",
"displayRecords": "Sätze anzeigen",
"isDoubleHeight": "Zweifache Höhe",
"mode": "Modus"
"mode": "Modus",
"enabledScopeList": "Was soll angezeigt werden"
},
"options": {
"mode": {
@@ -24,18 +24,23 @@
"replied": "Beantwortet",
"replies": "Antworten",
"isRead": "Ist gelesen",
"isImportant": "Ist wichtig"
"isImportant": "Ist wichtig",
"isUsers": "Gehört Benutzer",
"inTrash": "Im Papierkorb"
},
"links": {
"replied": "Beantwortet",
"replies": "Antworten"
},
"options": {
"Draft": "Entwurf",
"Sending": "Wird gesendet",
"Sent": "Gesendet",
"Archived": "Archiviert",
"Received": "Empfangen"
"status": {
"Draft": "Entwurf",
"Sending": "Wird gesendet",
"Sent": "Gesendet",
"Archived": "Archiviert",
"Received": "Empfangen",
"Failed": "Fehlgeschlagen"
}
},
"labels": {
"Create Email": "E-Mail archivieren",
@@ -58,10 +63,12 @@
"Mark all as read": "Als gelesen markieren",
"Show Plain Text": "Als Text zeigen",
"Mark as Important": "Als wichtig markieren",
"Mark as Not Important": "Als unwichtig markieren"
"Unmark Importance": "Wichtig Markierung entfernen",
"Move to Trash": "In den Papierkorb verschieben",
"Retrieve from Trash": "Aus dem Papierkorb hervorholen"
},
"messages": {
"noSmtpSetup": "Keine SMTP Einstellung, {link}. ",
"noSmtpSetup": "Keine SMTP Einstellungen {link}.",
"testEmailSent": "Eine Test E-Mail wurde gesendet",
"emailSent": "E-Mail wurde gesendet",
"savedAsDraft": "Als Entwurf gepeichert"
@@ -70,12 +77,14 @@
"sent": "Gesendet",
"archived": "Archiviert",
"inbox": "Posteingang",
"drafts": "Entwürfe"
"drafts": "Entwürfe",
"trash": "Papierkorb"
},
"massActions": {
"markAsRead": "Als gelesen markieren",
"markAsNotRead": "Als ungelesen markieren",
"markAsImportant": "Als wichtig markieren",
"markAsNotImportant": "Als unwichtig markieren"
"markAsNotImportant": "Wichtig Markierung entfernen",
"moveToTrash": "In den Papierkorb verschieben"
}
}
@@ -26,7 +26,7 @@
"labels": {
"Create EmailAccount": "E-Mail Konto erstellen",
"IMAP": "IMAP",
"Main": "Wichtig",
"Main": "Hauptteil",
"Test Connection": "Verbindung überprüfen"
},
"messages": {
@@ -35,6 +35,6 @@
},
"tooltips": {
"monitoredFolders": "Sie können einen 'Gesendet' Ordner hinzufügen um E-Mails zu synchronisieren, die von einem externen Programm gesendet wurden.",
"storeSentEmails": "Gesendete E-Mail werden auf einem IMAP Server gespeichert. Die E-Mail Adresse muss jene sein von der die E-Mail gesendet wurde."
"storeSentEmails": "Gesendete E-Mail werden auf einem IMAP Server gespeichert. Die E-Mail Adresse muss jene sein, von der die E-Mail gesendet wurde."
}
}
@@ -12,8 +12,8 @@
"name": "Ein Name für den Filter",
"subject": "Verwendung des Platzhalters *:\n\ntext* - beginnt mit text,\n*text* - enthält text,\n*text - endet mit text.",
"bodyContains": "Der Text der E-Mail enthält eines der angegebenen Worte oder Phrasen.",
"from": "Die E-Mails werden von der angegebenen Adresse gesendet. Bitte leer lassen wenn nicht benötigt.",
"to": "Die E-Mails werden an die angegebene Adresse gesendet. Bitte leer lassen wenn nicht benötigt.",
"from": "Die E-Mails werden von der angegebenen Adresse gesendet. Bitte leer lassen wenn nicht benötigt. Sie können den Platzhalter * verwenden.",
"to": "Die E-Mails werden an die angegebenen Adressen gesendet. Bitte leer lassen wenn nicht benötigt. Sie können den Platzhalter * verwenden.",
"parent": "Leer lassen um den Filter global anzuwenden (für alle eingehenden E-Mails)."
}
}
@@ -6,15 +6,21 @@
"body": "Inhalt",
"subject": "Betreff",
"attachments": "Anhänge",
"insertField": ""
},
"links": {
"insertField": "Feld einfügen",
"oneOff": "Ein-Aus"
},
"links": {},
"labels": {
"Create EmailTemplate": "E-Mail Vorlage erstellen",
"Info": "Info"
},
"messages": {
"infoText": "Verfügbare Variablen:\n\n{optOutUrl} &#8211; URL für den Abmeldelink};\n\n{optOutLink} &#8211; ein Abmeldelink."
},
"tooltips": {
"oneOff": "Überprüfen Sie, ob Sie die Vorlage nur einmal benutzen z.B. für eine Massenaussendung"
},
"presetFilters": {
"actual": "Aktuell"
}
}
@@ -11,12 +11,15 @@
"stream": "Vorgänge",
"label": "Bezeichnung",
"linkType": "Relationstyp",
"entityForeign": "Fremdentität/-modul",
"linkForeign": "Remdrelation/-modul",
"entityForeign": "Fremdentität\/-modul",
"linkForeign": "Fremdrelation\/-modul",
"link": "Link",
"labelForeign": "Fremdbezeichnung",
"sortBy": "Standardsortierung (Feld)",
"sortDirection": "Standardsortierung (Richtung)"
"sortDirection": "Standardsortierung (Richtung)",
"relationName": "Mittlerer Tabellenname",
"linkMultipleField": "Mehrere Felder verlinken",
"linkMultipleFieldForeign": "Fremdlink mehrere Felder"
},
"options": {
"type": {
@@ -39,6 +42,7 @@
},
"messages": {
"entityCreated": "Entität wurde erstellt",
"linkAlreadyExists": "Konflikt Name Relation"
"linkAlreadyExists": "Konflikt Name Relation",
"linkConflict": "Eine Relation mit diesem Namen existiert bereits"
}
}
@@ -3,6 +3,5 @@
"Connect": "Verbinden",
"Connected": "Verbunden"
},
"help": {
}
"help": {}
}
+194 -170
View File
@@ -34,7 +34,7 @@
"Dashboard": "Übersicht",
"InboundEmail": "Gruppen E-Mail Konten",
"Stream": "Vorgänge",
"Import": "Importergebnis",
"Import": "Importergebnisse",
"Template": "Vorlagen",
"Job": "Jobs",
"EmailFilter": "E-Mail Filter"
@@ -43,7 +43,7 @@
"Misc": "Verschiedenes",
"Merge": "Zusammenführen",
"None": "Kein(e)",
"by": "von",
"by": "nach",
"Saved": "Gespeichert.",
"Error": "Fehler",
"Select": "Auswählen",
@@ -53,7 +53,7 @@
"Loading...": "Lade...",
"Uploading...": "Lade hoch...",
"Sending...": "Wird gesendet...",
"Merging...": "Zusammenführen",
"Merging...": "Zusammenführen...",
"Merged": "Zusammengeführt",
"Removed": "Gelöscht",
"Posted": "Geposted",
@@ -61,10 +61,11 @@
"Unlinked": "Verknüpfung gelöscht",
"Done": "Fertig",
"Access denied": "Zugriff verweigert",
"Not found": "Nicht gefunden",
"Access": "Zugang",
"Are you sure?": "Sind Sie sicher?",
"Record has been removed": "Datensatz wurde gelöscht",
"Wrong username/password": "Falscher Benutzername/Passwort",
"Wrong username/password": "Falscher Benutzername\/Passwort",
"Post cannot be empty": "Notiz darf nicht leer sein",
"Removing...": "Lösche...",
"Unlinking...": "Lösche Verknüpfung...",
@@ -102,7 +103,7 @@
"Login": "Anmelden",
"Log Out": "Abmelden",
"Preferences": "Benutzereinstellungen",
"State": "Bundesland/Kanton",
"State": "Bundesland\/Kanton",
"Street": "Straße",
"Country": "Land",
"City": "Ort",
@@ -169,10 +170,12 @@
"Default": "Standard",
"Number": "Nummer",
"From": "Von",
"To": "An"
"To": "An",
"Create Post": "Nachricht erstellen"
},
"messages": {
"pleaseWait": "Bitte warten...",
"posting": "Poste...",
"confirmLeaveOutMessage": "Sind Sie sicher dass Sie das Formular verlassen wollen?",
"notModified": "Sie haben den Datensatz nicht geändert",
"duplicate": "Der Datensatz den Sie erstellen wollen, könnte eine Dublette sein",
@@ -181,7 +184,7 @@
"fieldShouldBeFloat": "{field} muss eine gültige Fließkomma Zahl sein",
"fieldShouldBeInt": "{field} muss eine gültige Ganzzahl sein",
"fieldShouldBeDate": "{field} muss ein gültiges Datum sein",
"fieldShouldBeDatetime": "{field} muss ein gültiges Datum/Zeit Feld sein",
"fieldShouldBeDatetime": "{field} muss ein gültiges Datum\/Zeit Feld sein",
"fieldShouldAfter": "{field} muss nach {otherField} sein",
"fieldShouldBefore": "{field} muss vor {otherField} sein",
"fieldShouldBeBetween": "{field} muss zwischen {min} und {max} sein",
@@ -204,8 +207,11 @@
"massRemoveResultSingle": "{count} Eintrag wurde gelöscht",
"noRecordsRemoved": "Es wurden keine Einträge gelöscht",
"clickToRefresh": "Klicken um zu aktualisieren",
"streamPostInfo": "Schreiben Sie <strong>@username</strong> um Benutzer in der Notiz hervorzuheben.\n\nVerfügbare Syntax:\n`<code>Code</code>`\n**<strong>fetter Text</strong>**\n*<em>Hervorgehobener Text</em>*\n~<del>Durchgestrichen</del>~\n> blockquote\n(url)[link]",
"writeYourCommentHere": "Notiz hier einfügen"
"streamPostInfo": "Schreiben Sie <strong>@username<\/strong> um Benutzer in der Notiz hervorzuheben.\n\nVerfügbare Syntax:\n`<code>Code<\/code>`\n**<strong>fetter Text<\/strong>**\n*<em>Hervorgehobener Text<\/em>*\n~<del>Durchgestrichen<\/del>~\n> blockquote\n(url)[link]",
"writeYourCommentHere": "Notiz hier einfügen",
"writeMessageToUser": "Nachricht an {user} schreiben",
"writeMessageToSelf": "Nachricht an sich selbst schreiben",
"typeAndPressEnter": "Tippen & Enter drücken"
},
"boolFilters": {
"onlyMy": "Nur Meine",
@@ -242,7 +248,7 @@
"phoneNumberFax": "Telefon (Fax)",
"phoneNumberOffice": "Telefon (Büro)",
"phoneNumberOther": "Telefon (Andere)",
"order": "Position",
"order": "Reihenfolge",
"parent": "Bezieht sich auf",
"children": "Kinder"
},
@@ -274,8 +280,22 @@
"status": "{user} hat {field} von {entityType} {entity} aktualisiert",
"update": "{user} hat {entityType} {entity} aktualisiert",
"createRelated": "{user} hat {relatedEntityType} {relatedEntity} verbunden mit {entityType} {entity} erstellt",
"postTargetTeam": "{user} hat an {target} geschrieben",
"postTargetTeams": "{user} hat an Teams {target} geschrieben",
"postTarget": "{user} hat an {target} geschrieben",
"mentionInPost": "{user} bemerkte {mentioned} in {entityType} {entity}",
"postTargetYou": "{user} hat an Sie geschrieben",
"postTargetYouAndOthers": "{user} hat an {target} und an Sie geschrieben",
"postTargetAll": "{user} hat an alle geschrieben",
"postTargetSelf": "{user} hat an sich selbst geschrieben",
"postTargetSelfAndOthers": "{user} hat an {target} und an siuch selbst geschrieben",
"mentionInPost": "{user} erwähnte {mentioned} in {entityType} {entity}",
"mentionYouInPost": "{user} erwähnte Sie in {entityType} {entity}",
"mentionInPostTarget": "{user} erwähnte {mentioned} in Nachricht",
"mentionYouInPostTarget": "{user} erwähnte Sie in Nachricht an {target}",
"mentionYouInPostTargetAll": "{user} erwähnte Sie in Nachricht an alle",
"mentionYouInPostTargetNoTarget": "{user} erwähnte Sie in Nachricht",
"createThis": "{user} hat {entityType} erstellt",
"createAssignedThis": "{user} hat {entityType} erstellt und an {assignee} zugewiesen",
@@ -283,8 +303,8 @@
"postThis": "{user} hat notiert",
"attachThis": "{user} hat hinzugefügt",
"statusThis": "{user} hat {field} aktualisiert",
"updateThis": "{user} hat diese(s/n) {entityType} aktualisiert",
"createRelatedThis": "{user} hat {relatedEntityType} {relatedEntity} verbunden mit dieser/m {entityType} erstellt",
"updateThis": "{user} hat diese(s\/n) {entityType} aktualisiert",
"createRelatedThis": "{user} hat {relatedEntityType} {relatedEntity} verbunden mit dieser\/m {entityType} erstellt",
"emailReceivedFromThis": "E-Mail empfangen von {from}",
"emailReceivedInitialFromThis": "E-Mail empfangen von {from}, ein(e) {entityType} wurde erstellt",
@@ -302,92 +322,93 @@
"emailSent": "{by} hat eine E-Mail verbunden mit {entityType} {entity} gesendet",
"emailSentThis": "{by} hat eine E-Mail gesendet"
},
"lists": {
"monthNames": ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
"monthNamesShort": ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
"dayNames": ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
"dayNamesShort": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
"dayNamesMin": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]
},
"lists": {
"monthNames": ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
"monthNamesShort": ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
"dayNames": ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
"dayNamesShort": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
"dayNamesMin": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]
},
"options": {
"salutationName": {
"Mr.": "Hr.",
"Mrs.": "Fr.",
"Ms.": "Fr.",
"Dr.": "Dr."
"Hr.": "Hr.",
"Fr.": "Fr.",
"Dr.": "Dr.",
"Prof.": "Prof."
},
"language": {
"af_ZA": "Afrikaans",
"az_AZ": "Aserbaidschanisch",
"be_BY": "Weissrussisch",
"bg_BG": "Bulgarisch",
"bn_IN": "Benglaisch",
"bs_BA": "Bosnisch",
"ca_ES": "Katalanisch",
"cs_CZ": "Tschechisch",
"cy_GB": "Walisisch",
"da_DK": "Dänisch",
"de_DE": "Deutsch",
"el_GR": "Griechisch",
"en_GB": "Englisch (UK)",
"en_US": "Englisch (US)",
"es_ES": "Spanisch (ES)",
"et_EE": "Estnisch",
"eu_ES": "Baskisch",
"fa_IR": "Persisch",
"fi_FI": "Finnisch",
"fo_FO": "Färöisch",
"fr_CA": "Französisch (CN)",
"fr_FR": "Französisch (FR)",
"ga_IE": "Irisch",
"gl_ES": "Galizisch",
"gn_PY": "Guarani",
"he_IL": "Hebräisch",
"hi_IN": "Hindi",
"hr_HR": "Kroatisch",
"hu_HU": "Ungarisch",
"hy_AM": "Armenisch",
"id_ID": "Indonesisch",
"is_IS": "Isländisch",
"it_IT": "Italienisch",
"ja_JP": "Japanisch",
"ka_GE": "Georgisch",
"km_KH": "Khmer",
"ko_KR": "Koreanisch",
"ku_TR": "Kurdisch",
"lt_LT": "Litauisch",
"lv_LV": "Lettisch",
"mk_MK": "Mazedonisch",
"ml_IN": "Malayalam",
"ms_MY": "Malaiisch",
"nb_NO": "Norwegisch Bokmál",
"nn_NO": "Norwegisch Nynorsk",
"ne_NP": "Nepali",
"nl_NL": "Niederländisch",
"pa_IN": "Punjabi",
"pl_PL": "Polnisch",
"ps_AF": "Pashto",
"pt_BR": "Portugiesisch (BR)",
"pt_PT": "Portugiesisch (PT)",
"ro_RO": "Rumänisch",
"ru_RU": "Russisch",
"sk_SK": "Slowakisch",
"sl_SI": "Slowenisch",
"sq_AL": "Albanisch",
"sr_RS": "Serbisch",
"sv_SE": "Schwedisch",
"sw_KE": "Suaheli",
"ta_IN": "Tamil",
"te_IN": "Telugu",
"th_TH": "Thailändisch",
"tl_PH": "Tagalog",
"tr_TR": "Tükisch",
"uk_UA": "Ukrainisch",
"ur_PK": "Urdu",
"vi_VN": "Vietnamesisch",
"zh_CN": "Chinesisch vereinfacht (CN)",
"zh_HK": "Chinesisch traditionell (HK)",
"zh_TW": "Chinesisch traditionell (TW)"
"af_ZA":"Afrikaans",
"az_AZ":"Aserbaidschanisch",
"be_BY":"Weissrussisch",
"bg_BG":"Bulgarisch",
"bn_IN":"Benglaisch",
"bs_BA":"Bosnisch",
"ca_ES":"Katalanisch",
"cs_CZ":"Tschechisch",
"cy_GB":"Walisisch",
"da_DK":"Dänisch",
"de_DE":"Deutsch",
"el_GR":"Griechisch",
"en_GB":"Englisch (UK)",
"en_US":"Englisch (US)",
"es_ES":"Spanisch (ES)",
"et_EE":"Estnisch",
"eu_ES":"Baskisch",
"fa_IR":"Persisch",
"fi_FI":"Finnisch",
"fo_FO":"Färöisch",
"fr_CA":"Französisch (CN)",
"fr_FR":"Französisch (FR)",
"ga_IE":"Irisch",
"gl_ES":"Galizisch",
"gn_PY":"Guarani",
"he_IL":"Hebräisch",
"hi_IN":"Hindi",
"hr_HR":"Kroatisch",
"hu_HU":"Ungarisch",
"hy_AM":"Armenisch",
"id_ID":"Indonesisch",
"is_IS":"Isländisch",
"it_IT":"Italienisch",
"ja_JP":"Japanisch",
"ka_GE":"Georgisch",
"km_KH":"Khmer",
"ko_KR":"Koreanisch",
"ku_TR":"Kurdisch",
"lt_LT":"Litauisch",
"lv_LV":"Lettisch",
"mk_MK":"Mazedonisch",
"ml_IN":"Malayalam",
"ms_MY":"Malaiisch",
"nb_NO":"Norwegisch Bokmál",
"nn_NO":"Norwegisch Nynorsk",
"ne_NP":"Nepali",
"nl_NL":"Niederländisch",
"pa_IN":"Punjabi",
"pl_PL":"Polnisch",
"ps_AF":"Pashto",
"pt_BR":"Portugiesisch (BR)",
"pt_PT":"Portugiesisch (PT)",
"ro_RO":"Rumänisch",
"ru_RU":"Russisch",
"sk_SK":"Slowakisch",
"sl_SI":"Slowenisch",
"sq_AL":"Albanisch",
"sr_RS":"Serbisch",
"sv_SE":"Schwedisch",
"sw_KE":"Suaheli",
"ta_IN":"Tamil",
"te_IN":"Telugu",
"th_TH":"Thailändisch",
"tl_PH":"Tagalog",
"tr_TR":"Tükisch",
"uk_UA":"Ukrainisch",
"ur_PK":"Urdu",
"vi_VN":"Vietnamesisch",
"zh_CN":"Chinesisch vereinfacht (CN)",
"zh_HK":"Chinesisch traditionell (HK)",
"zh_TW":"Chinesisch traditionell (TW)"
},
"dateSearchRanges": {
"on": "Am",
@@ -411,12 +432,15 @@
"searchRanges": {
"is": "Ist",
"isEmpty": "Ist leer",
"isNotEmpty": "Ist nicht leer"
"isNotEmpty": "Ist nicht leer",
"isOneOf": "Ist eines(r) von"
},
"varcharSearchRanges": {
"equals": "Gleich",
"startsWith": "Beginnt mit",
"contains": "Enthält"
"contains": "Enthält",
"isEmpty": "Ist leer",
"isNotEmpty": "Ist nicht leer"
},
"intSearchRanges": {
"equals": "Gleich",
@@ -445,104 +469,104 @@
},
"sets": {
"summernote": {
"NOTICE": "Sie finden die Übersetzung hier: https://github.com/HackerWins/summernote/tree/master/lang",
"NOTICE": "Sie finden die Übersetzung hier: https:\/\/github.com\/HackerWins\/summernote\/tree\/master\/lang",
"font":{
"bold": "Fett",
"italic": "Kursiv",
"underline": "Unterstrichen",
"strike": "Durchgestrichen",
"clear": "Font Stil entfernen",
"height": "Zeilenhöhe",
"name": "Schriftfamilie",
"size": "Schriftgröße"
"bold":"Fett",
"italic":"Kursiv",
"underline":"Unterstrichen",
"strike":"Durchgestrichen",
"clear":"Font Stil entfernen",
"height":"Zeilenhöhe",
"name":"Schriftfamilie",
"size":"Schriftgröße"
},
"image":{
"image": "Bild",
"insert": "Bild einfügen",
"resizeFull": "Originalgröße",
"resizeHalf": "Größe 1/2",
"resizeQuarter": "Größe 1/4",
"floatLeft": "Linksbündig",
"floatRight": "Rechtsbündig",
"floatNone": "Kein Textfluss",
"dragImageHere": "Ziehen Sie ein Bild mit der Maus hierher",
"selectFromFiles": "Wählen Sie eine Datei aus",
"url": "Grafik URL",
"remove": "Grafik entfernen"
"image":"Bild",
"insert":"Bild einfügen",
"resizeFull":"Originalgröße",
"resizeHalf":"Größe 1\/2",
"resizeQuarter":"Größe 1\/4",
"floatLeft":"Linksbündig",
"floatRight":"Rechtsbündig",
"floatNone":"Kein Textfluss",
"dragImageHere":"Ziehen Sie ein Bild mit der Maus hierher",
"selectFromFiles":"Wählen Sie eine Datei aus",
"url":"Grafik URL",
"remove":"Grafik entfernen"
},
"link":{
"link": "Link",
"insert": "Link einfügen",
"unlink": "Link entfernen",
"edit": "Bearbeiten",
"textToDisplay": "Anzeigetext",
"url": "Ziel des Links?",
"openInNewWindow": "In einem neuen Fenster öffnen"
"link":"Link",
"insert":"Link einfügen",
"unlink":"Link entfernen",
"edit":"Bearbeiten",
"textToDisplay":"Anzeigetext",
"url":"Ziel des Links?",
"openInNewWindow":"In einem neuen Fenster öffnen"
},
"video":{
"video": "Video",
"videoLink": "Video Link",
"insert": "Video einfügen",
"url": "Video URL?",
"providers": "(YouTube, Vimeo, Vine, Instagram oder DailyMotion)"
"video":"Video",
"videoLink":"Video Link",
"insert":"Video einfügen",
"url":"Video URL?",
"providers":"(YouTube, Vimeo, Vine, Instagram oder DailyMotion)"
},
"table":{
"table": "Tabelle"
"table":"Tabelle"
},
"hr":{
"insert": "Eine horizontale Linie einfügen"
"insert":"Eine horizontale Linie einfügen"
},
"style":{
"style": "Stil",
"normal": "Normal",
"blockquote": "Zitat",
"pre": "Quellcode",
"h1": "Überschrift 1",
"h2": "Überschrift 2",
"h3": "Überschrift 3",
"h4": "Überschrift 4",
"h5": "Überschrift 5",
"h6": "Überschrift 6"
"style":"Stil",
"normal":"Normal",
"blockquote":"Zitat",
"pre":"Quellcode",
"h1":"Überschrift 1",
"h2":"Überschrift 2",
"h3":"Überschrift 3",
"h4":"Überschrift 4",
"h5":"Überschrift 5",
"h6":"Überschrift 6"
},
"lists":{
"unordered": "Unsortierte Liste",
"ordered": "Nummerierte Liste"
"unordered":"Unsortierte Liste",
"ordered":"Nummerierte Liste"
},
"options":{
"help": "Hilfe",
"fullscreen": "Vollbild",
"codeview": "HTML-Code anzeigen"
"help":"Hilfe",
"fullscreen":"Vollbild",
"codeview":"HTML-Code anzeigen"
},
"paragraph":{
"paragraph": "Absatz",
"outdent": "Ausrückung",
"indent": "Einrückung",
"left": "Links ausrichten",
"center": "Zentriert ausrichten",
"right": "Rechts ausrichten",
"justify": "Blocksatz"
"paragraph":"Absatz",
"outdent":"Ausrückung",
"indent":"Einrückung",
"left":"Links ausrichten",
"center":"Zentriert ausrichten",
"right":"Rechts ausrichten",
"justify":"Blocksatz"
},
"color":{
"recent": "Letzte Farbe",
"more": "Mehr Farben",
"background": "Hintergrundfarbe",
"foreground": "Schriftfarbe",
"transparent": "Transparenz",
"setTransparent": "Transparenz setzen",
"reset": "Zurücksetzen",
"resetToDefault": "Zurücksetzen auf Standard"
"recent":"Letzte Farbe",
"more":"Mehr Farben",
"background":"Hintergrundfarbe",
"foreground":"Schriftfarbe",
"transparent":"Transparenz",
"setTransparent":"Transparenz setzen",
"reset":"Zurücksetzen",
"resetToDefault":"Zurücksetzen auf Standard"
},
"shortcut":{
"shortcuts": "Tastaturkürzel",
"close": "Schließen",
"textFormatting": "Textformatierung",
"action": "Aktion",
"paragraphFormatting": "Absatzformatierung",
"documentStyle": "Dokumentenstil"
"shortcuts":"Tastaturkürzel",
"close":"Schließen",
"textFormatting":"Textformatierung",
"action":"Aktion",
"paragraphFormatting":"Absatzformatierung",
"documentStyle":"Dokumentenstil"
},
"history":{
"undo": "Rückgängig",
"redo": "Wiederholen"
"undo":"Rückgängig",
"redo":"Wiederholen"
}
}
},
@@ -13,12 +13,13 @@
"Show records": "Datensätze zeigen",
"Remove Duplicates": "Duplikate entfernen",
"importedCount": "Importiert (Anzahl)",
"duplicateCount": "Duplicate (Anzahl)",
"duplicateCount": "Duplikate (Anzahl)",
"updatedCount": "Aktualisiert (Anzahl)",
"Create Only": "Nur erstellen",
"Create and Update": "Erstellen und aktualisieren",
"Update Only": "Nur aktualisieren",
"Update by": "Aktualisieren durch",
"Set as Not Duplicate": "Als keine Dublette markieren",
"File (CSV)": "Datei (CSV)"
},
"messages": {
@@ -20,16 +20,18 @@
"replyToAddress": "Antwort an Adresse",
"replyFromName": "Von Name antworten",
"targetUserPosition": "Position Zielbenutzer",
"fetchSince": "Holen seit"
"fetchSince": "Holen seit",
"addAllTeamUsers": "Für alle Teambenutzer"
},
"tooltips": {
"reply": "E-Mail Absender informieren, dass die E-Mail empfangen wurde.\n\nEs wird pro Zeitabschnitt nur eine E-Mail an einen bestimmten Empfänger gesendet um Schleifen zu vermeiden.",
"reply": "E-Mail Absender informieren, dass ihre E-Mails empfangen wurde.\n\n Es wird pro Zeitabschnitt nur eine E-Mail an einen bestimmten Empfänger gesendet um Endlosschleifen zu vermeiden.",
"createCase": "Fall aus eingehender E-Mail automatisch erstellen.",
"replyToAddress": "Geben Sie die E-Mail Adresse dieser Mailbox an um Antworten hier zu empfangen.",
"caseDistribution": "Wie Fälle zugewiesen werden. Entweder direkt dem Benutzer oder im Team.",
"assignToUser": "Benutzer E-Mails/Fälle werden zugewiesen an",
"team": "Team E-Mails/Fälle werden verknüpft mit",
"targetUserPosition": "Bestimmen Sie die Position der Benutzer die Fälle zugewiesen bekommen."
"assignToUser": "Benutzer E-Mails\/Fälle werden zugewiesen an",
"team": "Team E-Mails\/Fälle werden verknüpft mit",
"targetUserPosition": "Bestimmen Sie die Position der Benutzer die Fälle zugewiesen bekommen.",
"addAllTeamUsers": "E-Mails erscheinen im Posteingang aller Benutzer eines bestimmten Teams."
},
"links": {
"filters": "Filter"
@@ -49,7 +51,7 @@
"Create InboundEmail": "E-Mail Konto erstellen",
"IMAP": "IMAP",
"Actions": "Aktionen",
"Main": "Wichtig"
"Main": "Hauptteil"
},
"messages": {
"couldNotConnectToImap": "Kann keine Verbindung zum IMAP Server herstellen"
@@ -10,6 +10,6 @@
"noIntegrations": "Keine Integration verfügbar."
},
"help": {
"Google": "<p><b>Holen Sie die OAuth 2.0 Credentials über die Google Developers Console.</b></p><p>Visit <a href=\"https://console.developers.google.com/project\">Google Developers Console</a> um OAuth 2.0 Credentials wie eine Client ID und Client Geheimnis zu erhalten die sowohl Google als auch EspoCRM bekannt sind.</p>"
"Google": "<p><b>Holen Sie die OAuth 2.0 Credentials über die Google Developers Console.<\/b><\/p><p>Besuchen Sie <a href=\"https:\/\/console.developers.google.com\/project\">die Google Developers Console<\/a> um OAuth 2.0 Credentials wie eine Client ID und Client Geheimnis zu erhalten die sowohl Google als auch EspoCRM bekannt sind.<\/p>"
}
}
@@ -1,11 +1,25 @@
{
"fields": {
"post": "Senden",
"attachments": "Anhänge"
"attachments": "Anhänge",
"targetType": "Zielkontakt",
"teams": "Teams",
"users": "Benutzer"
},
"filters": {
"all": "Alle",
"posts": "Posts",
"updates": "Aktualisierungen"
},
"options": {
"targetType": {
"self": "An sich selbst",
"users": "An (einen) bestimmte(n) Benutzer",
"teams": "An (ein) bestimmte(s) Team(s)",
"all": "An alle Benutzer"
}
},
"messages": {
"writeMessage": "Schreiben Sie hier Ihre Nachricht"
}
}
@@ -27,8 +27,7 @@
"useCustomTabList": "Benutzerdefinierte Reiter Liste",
"tabList": "Reiter Liste"
},
"links": {
},
"links": {},
"options": {
"weekStart": {
"0": "Sonntag",
@@ -10,8 +10,8 @@
"teams": "Teams"
},
"tooltips": {
"assignmentPermission": "Ermöglicht das Recht der Zuweisung von Datensätzen an andere Benutzer zu beschränken\n\nAlle - Keine Einschränkung\n\nTeam - Kann an Benutzer des eigenen Teams zuweisen\n\nNein - Kann nur sich selbst zuweisen",
"userPermission": "Erlaubt Benutzern die Aktivitäten anderer Benutzer zu sehen.\n\nAlle - Kann alle sehen\n\nTeam - Kann nur Aktivitäten der Benutzer im eigenen Team sehen\n\nNein - Kann keine anderen Aktivitäten sehen"
"assignmentPermission": "Ermöglicht das Recht der Zuweisung von Datensätzen und das Senden von Nachrichten an andere Benutzer zu beschränken\n\nAlle - Keine Einschränkung\n\nTeam - Kann an Benutzer des eigenen Teams zuweisen\/senden\n\nNein - Kann nur sich selbst zuweisen\/senden",
"userPermission": "Erlaubt Benutzer zu beschränken, die Aktivitäten, Kalendereinträge und Vorgänge anderer Benutzer zu sehen.\n\nAlle - Kann alle sehen\n\nTeam - Kann nur Aktivitäten der Benutzer im eigenen Team sehen\n\nNein - Kann keine anderen Aktivitäten sehen"
},
"labels": {
"Access": "Zugang",
@@ -16,7 +16,8 @@
"Cleanup": "Aufräumen",
"CheckInboundEmails": "Gruppen E-Mail Konten überprüfen",
"CheckEmailAccounts": "Persönliche E-Mail Konten prüfen",
"SendEmailReminders": "E-Mail Erinnerungen senden"
"SendEmailReminders": "E-Mail Erinnerungen senden",
"AuthTokenControl": "Auth Token Control"
},
"cronSetup": {
"linux": "Hinweis: Fügen Sie diese Zeile zu Ihrer Crontab Datei hinzu, um geplante Aufgaben durchführen zu können:",
@@ -30,6 +31,6 @@
}
},
"tooltips": {
"scheduling": "Definiert wann und wie oft ein Job läuft\n\n*/5 * * * * - alle 5 Minuten\n\n0 */2 * * * - alle 2 Stunden\n\n30 1 * * * - um 01:30 einmal am Tag\n\n0 0 1 * * - am ersten Tag des Monats"
"scheduling": "Definiert wann und wie oft ein Job läuft\n\n*\/5 * * * * - alle 5 Minuten\n\n0 *\/2 * * * - alle 2 Stunden\n\n30 1 * * * - um 01:30 einmal am Tag\n\n0 0 1 * * - am ersten Tag des Monats"
}
}
@@ -28,7 +28,7 @@
"tabList": "Reiter Liste",
"quickCreateList": "Liste Schnellerstellung",
"exportDelimiter": "Export Trennzeichen",
"globalSearchEntityList": "Modul-/Entitätsliste globale Suche",
"globalSearchEntityList": "Modul-\/Entitätsliste globale Suche",
"authenticationMethod": "Authentifizierungs Methode",
"ldapHost": "Host",
"ldapPort": "Port",
@@ -51,15 +51,17 @@
"assignmentEmailNotificationsEntityList": "Entitäten über die mit E-Mail bei Zuweisung benachrichtigt werden soll",
"b2cMode": "B2C Modus",
"avatarsDisabled": "Avatare deaktivieren",
"followCreatedEntities": "Erstellten Einträgen beobachten",
"followCreatedEntities": "Erstellte Einträgen beobachten",
"displayListViewRecordCount": "Gesamtanzahl anzeigen (in Listenansicht)",
"theme": "Design",
"userThemesDisabled": "Benutzerdesigns deaktivieren",
"emailMessageMaxSize": "Max. E-Mail Größe (Mb)",
"massEmailMaxPerHourCount": "Max. Anzahl E-Mails pro Stunde",
"personalEmailMaxPortionSize": "Max email portion size for a personal account fetching",
"inboundEmailMaxPortionSize": "Max email portion size for a group account fetching",
"maxEmailAccountCount": "Max count of personal email accounts per user"
"personalEmailMaxPortionSize": "Max. E-Mail Größe für das Holen persönlicher Konten",
"inboundEmailMaxPortionSize": "Max. E-Mail Größe für das Holen von Gruppenkonten",
"maxEmailAccountCount": "Max. Anzahl von E-Mail Konten pro Benutzer",
"authTokenLifetime": "Auth Token Lebenszeit (Stunden)",
"authTokenMaxIdleTime": "Auth Token Max Untätigkeitszeit (Stunden)"
},
"options": {
"weekStart": {
@@ -71,7 +73,9 @@
"recordsPerPageSmall": "Anzahl Sätze in Beziehungssubpanels",
"outboundEmailIsShared": "Benutzern gestatten, E-Mails über dieses SMTP Konto zu senden",
"followCreatedEntities": "Benutzer folgen automatisch jenen Sätzen die sie erstellt haben",
"emailMessageMaxSize": "Alle eingehenden E-Mails die eine angegebene Größe übersteigen, werden übersprungen "
"emailMessageMaxSize": "Alle eingehenden E-Mails die eine angegebene Größe übersteigen, werden übersprungen",
"authTokenLifetime": "Bestimmt wie lange ein Token existiert.\n0 - Kein Ablauf",
"authTokenMaxIdleTime": "Bestimmt wie lange ein Token nach dem letzten Zugriff existiert\n0 - Kein Ablauf"
},
"labels": {
"System": "System",
@@ -5,11 +5,13 @@
"positionList": "Stellungen"
},
"links": {
"users": "Benutzer"
"users": "Benutzer",
"notes": "Notizen",
"roles": "Rollen"
},
"tooltips": {
"roles": "Zugriffsrollen Benutzer dieses Teams erhalten Zugriffsberechtigung von ausgewählten Rollen",
"positionList": "Verfügbare Positionen in diesem Team Z.B. Verkäufer, Manager etc."
"roles": "Zugriffsrollen Benutzer dieses Teams erhalten Zugriffsberechtigung von ausgewählten Rollen.",
"positionList": "Verfügbare Positionen in diesem Team. Z.B. Verkäufer, Manager etc."
},
"labels": {
"Create Team": "Team erstellen"
@@ -12,8 +12,7 @@
"printFooter": "Fuß drucken",
"footerPosition": "Position Fuß"
},
"links": {
},
"links": {},
"labels": {
"Create Template": "Vorlage erstellen"
},
@@ -19,7 +19,8 @@
},
"links": {
"teams": "Teams",
"roles": "Rollen"
"roles": "Rollen",
"notes": "Notizen"
},
"labels": {
"Create User": "Benutzer erstellen",
@@ -47,10 +48,10 @@
"accountInfoEmailSubject": "EspoCRM Benutzerzugriffsinfo",
"accountInfoEmailBody": "Ihre EspoCRM Zugriffsinformation:\n\nBenutzername: {userName}\nPasswort: {password}\n\n{siteUrl}",
"passwordChangeLinkEmailSubject": "Anforderung zur Passwortänderung",
"passwordChangeLinkEmailBody": "Sie können Ihr Passwort über diesen Link {link} ändern Diese eindeutige URL ist nur für kurze Zeit gültig.",
"passwordChangeLinkEmailBody": "Sie können Ihr Passwort über diesen Link {link} ändern. Diese eindeutige URL ist nur für kurze Zeit gültig.",
"passwordChanged": "Das Passwort wurde geändert",
"userCantBeEmpty": "Der Benutzername darf nicht leer sein!",
"wrongUsernamePasword": "Falscher Benutzername/Passwort",
"wrongUsernamePasword": "Falscher Benutzername\/Passwort",
"emailAddressCantBeEmpty": "E-Mail Adresse darf nicht leer sein",
"userNameEmailAddressNotFound": "Benutzername oder E-Mail Adresse nicht gefunden",
"forbidden": "Verboten, bitte später nochmals versuchen",
@@ -21,6 +21,7 @@
"Roles": "Roles",
"Outbound Emails": "Outbound Emails",
"Group Email Accounts": "Group Email Accounts",
"Personal Email Accounts": "Personal Email Accounts",
"Inbound Emails": "Inbound Emails",
"Email Templates": "Email Templates",
"Import": "Import",
@@ -94,7 +95,8 @@
"rangeInt": "Range Integer",
"rangeFloat": "Range Float",
"rangeCurrency": "Range Currency",
"wysiwyg": "Wysiwyg"
"wysiwyg": "Wysiwyg",
"map": "Map"
},
"fields": {
"type": "Type",
@@ -119,7 +121,8 @@
"isSorted": "Is Sorted (alphabetically)",
"audited": "Audited",
"trim": "Trim",
"height": "Height (px)"
"height": "Height (px)",
"provider": "Provider"
},
"messages": {
"upgradeVersion": "Your EspoCRM will be upgraded to version <strong>{version}</strong>. This can take some time.",
@@ -147,6 +150,7 @@
"roles": "Roles management.",
"outboundEmails": "SMTP settings for outgoing emails.",
"groupEmailAccounts": "Group IMAP email accounts. Email import and Email-to-Case.",
"personalEmailAccounts": "Users email accounts.",
"emailTemplates": "Templates for outbound emails.",
"import": "Import data from CSV file.",
"layoutManager": "Customize layouts (list, detail, edit, search, mass update).",
@@ -25,18 +25,22 @@
"replies": "Replies",
"isRead": "Is Read",
"isImportant": "Is Important",
"isUsers": "Is User's"
"isUsers": "Is User's",
"inTrash": "In Trash"
},
"links": {
"replied": "Replied",
"replies": "Replies"
},
"options": {
"Draft": "Draft",
"Sending": "Sending",
"Sent": "Sent",
"Archived": "Archived",
"Received": "Received"
"status": {
"Draft": "Draft",
"Sending": "Sending",
"Sent": "Sent",
"Archived": "Archived",
"Received": "Received",
"Failed": "Failed"
}
},
"labels": {
"Create Email": "Archive Email",
@@ -59,7 +63,9 @@
"Mark all as read": "Mark all as read",
"Show Plain Text": "Show Plain Text",
"Mark as Important": "Mark as Important",
"Unmark Importance": "Unmark Importance"
"Unmark Importance": "Unmark Importance",
"Move to Trash": "Move to Trash",
"Retrieve from Trash": "Retrieve from Trash"
},
"messages": {
"noSmtpSetup": "No SMTP setup. {link}.",
@@ -71,12 +77,14 @@
"sent": "Sent",
"archived": "Archived",
"inbox": "Inbox",
"drafts": "Drafts"
"drafts": "Drafts",
"trash": "Trash"
},
"massActions": {
"markAsRead": "Mark as Read",
"markAsNotRead": "Mark as Not Read",
"markAsImportant": "Mark as Important",
"markAsNotImportant": "Unmark Importance"
"markAsNotImportant": "Unmark Importance",
"moveToTrash": "Move to Trash"
}
}
@@ -6,7 +6,8 @@
"body": "Body",
"subject": "Subject",
"attachments": "Attachments",
"insertField": "Insert Field"
"insertField": "Insert Field",
"oneOff": "One-off"
},
"links": {
},
@@ -16,5 +17,11 @@
},
"messages": {
"infoText": "Available variables:\n\n{optOutUrl} &#8211; URL for an unsubsbribe link};\n\n{optOutLink} &#8211; an unsubscribe link."
},
"tooltips": {
"oneOff": "Check if you are going to use this template only once. E.g. for Mass Email."
},
"presetFilters": {
"actual": "Actual"
}
}
@@ -16,7 +16,10 @@
"link": "Link",
"labelForeign": "Foreign Label",
"sortBy": "Default Order (field)",
"sortDirection": "Default Order (direction)"
"sortDirection": "Default Order (direction)",
"relationName": "Middle Table Name",
"linkMultipleField": "Link Multiple Field",
"linkMultipleFieldForeign": "Foreign Link Multiple Field"
},
"options": {
"type": {
@@ -210,7 +210,8 @@
"streamPostInfo": "Type <strong>@username</strong> to mention users in the post.\n\nAvailable markdown syntax:\n`<code>code</code>`\n**<strong>strong text</strong>**\n*<em>emphasized text</em>*\n~<del>deleted text</del>~\n> blockquote\n(url)[link]",
"writeYourCommentHere": "Write your comment here",
"writeMessageToUser": "Write a message to {user}",
"writeMessageToSelf": "Write a message to self"
"writeMessageToSelf": "Write a message to self",
"typeAndPressEnter": "Type & press enter"
},
"boolFilters": {
"onlyMy": "Only My",
@@ -430,12 +431,15 @@
"searchRanges": {
"is": "Is",
"isEmpty": "Is Empty",
"isNotEmpty": "Is Not Empty"
"isNotEmpty": "Is Not Empty",
"isOneOf": "Is One Of"
},
"varcharSearchRanges": {
"equals": "Equals",
"startsWith": "Starts With",
"contains": "Contains"
"contains": "Contains",
"isEmpty": "Is Empty",
"isNotEmpty": "Is Not Empty"
},
"intSearchRanges": {
"equals": "Equals",
@@ -20,7 +20,8 @@
"replyToAddress": "Reply To Address",
"replyFromName": "Reply From Name",
"targetUserPosition": "Target User Position",
"fetchSince": "Fetch Since"
"fetchSince": "Fetch Since",
"addAllTeamUsers": "For all team users"
},
"tooltips": {
"reply": "Notify email senders that their emails has been received.\n\n Only one email will be sent to a particular recipient during some period of time to prevent looping.",
@@ -29,7 +30,8 @@
"caseDistribution": "How cases will be assigned to. Assigned directly to the user or among the team.",
"assignToUser": "User emails/cases will be assigned to.",
"team": "Team emails/cases will be related to.",
"targetUserPosition": "Define the position of users which will be destributed with cases."
"targetUserPosition": "Define the position of users which will be destributed with cases.",
"addAllTeamUsers": "Emails will appear in Inbox of all users of a specified team."
},
"links": {
"filters": "Filters"
@@ -16,7 +16,8 @@
"Cleanup": "Clean-up",
"CheckInboundEmails": "Check Group Email Accounts",
"CheckEmailAccounts": "Check Personal Email Accounts",
"SendEmailReminders": "Send Email Reminders"
"SendEmailReminders": "Send Email Reminders",
"AuthTokenControl": "Auth Token Control"
},
"cronSetup": {
"linux": "Note: Add this line to the crontab file to run Espo Scheduled Jobs:",
@@ -59,7 +59,9 @@
"massEmailMaxPerHourCount": "Max count of emails sent per hour",
"personalEmailMaxPortionSize": "Max email portion size for a personal account fetching",
"inboundEmailMaxPortionSize": "Max email portion size for a group account fetching",
"maxEmailAccountCount": "Max count of personal email accounts per user"
"maxEmailAccountCount": "Max count of personal email accounts per user",
"authTokenLifetime": "Auth Token Lifetime (hours)",
"authTokenMaxIdleTime": "Auth Token Max Idle Time (hours)"
},
"options": {
"weekStart": {
@@ -71,7 +73,9 @@
"recordsPerPageSmall": "Count of records in relatinship panels.",
"outboundEmailIsShared": "Allow users to sent emails via this SMTP.",
"followCreatedEntities": "Users will automatically follow records they created.",
"emailMessageMaxSize": "All inbound emails exceeding a specified size will be skipped."
"emailMessageMaxSize": "All inbound emails exceeding a specified size will be skipped.",
"authTokenLifetime": "Defines how long tokens can exist.\n0 - means no expiration.",
"authTokenMaxIdleTime": "Defines how long since a last access tokens can exist.\n0 - means no expiration."
},
"labels": {
"System": "System",
@@ -94,7 +94,8 @@
"rangeInt": "Rango de Enteros",
"rangeFloat": "Rango de Flotantes",
"rangeCurrency": "Rango de Moneda",
"wysiwyg": "Wysiwyg"
"wysiwyg": "Wysiwyg",
"map": "Mapa"
},
"fields": {
"type": "Tipo",
@@ -119,7 +120,8 @@
"isSorted": "Esta ordenado (alfabeticamente)",
"audited": "Auditada",
"trim": "recortado",
"height": "Altura (px)"
"height": "Altura (px)",
"provider": "Proveedor"
},
"messages": {
"upgradeVersion": "Su EspoCRM será actualizado a la versión <strong>{version}</strong>. Tomará algún tiempo.",
@@ -24,18 +24,25 @@
"replied": "Respondió",
"replies": "Respuestas",
"isRead": "Es leído",
"isImportant": "Es Importante"
"isImportant": "Es Importante",
"isUsers": "Es del Usuario",
"inTrash": "En Papelera",
"Move to Trash": "Mover a la Papelera",
"Retrieve from Trash": "Recuperar de la Papelera"
},
"links": {
"replied": "Respondió",
"replies": "Respuestas"
},
"options": {
"Draft": "Borrador",
"Sending": "Enviando",
"Sent": "Enviados",
"Archived": "Archivado",
"Received": "Recibido"
"status": {
"Draft": "Borrador",
"Sending": "Enviando",
"Sent": "Enviado",
"Archived": "Archivado",
"Received": "Recibido",
"Failed": "Falló"
}
},
"labels": {
"Create Email": "Archivar Correo",
@@ -67,15 +74,17 @@
"savedAsDraft": "Guardado como borrador"
},
"presetFilters": {
"sent": "Enviados",
"sent": "Enviado",
"archived": "Archivado",
"inbox": "Bandeja de Entrada",
"drafts": "Borradores"
"drafts": "Borradores",
"trash": "Papelera"
},
"massActions": {
"markAsRead": "Marcar como Leído",
"markAsRead": "Mark as Read",
"markAsNotRead": "Marcar como No Leído",
"markAsImportant": "Marcar como Importante",
"markAsNotImportant": "Marcar como No Importante"
"markAsNotImportant": "Marcar como No Importante",
"moveToTrash": "Mover a la Papelera"
}
}
@@ -6,7 +6,8 @@
"body": "Cuerpo",
"subject": "Asunto",
"attachments": "Adjuntos",
"insertField": ""
"insertField": "Insertar Campo",
"oneOff": "Único"
},
"links": {
},
@@ -16,5 +17,11 @@
},
"messages": {
"infoText": "Available variables:\n\n{optOutUrl} &#8211; URL for an unsubsbribe link};\n\n{optOutLink} &#8211; an unsubscribe link."
},
"tooltips": {
"oneOff": "Compruebe si usted va a utilizar esta plantilla sólo una vez. Por ejemplo: para Correo Masivo."
},
"presetFilters": {
"actual": "Actuales"
}
}
@@ -8,7 +8,7 @@
"type": "Tipo",
"labelSingular": "Etiqueta en Singular",
"labelPlural": "Etiqueta en Plural",
"stream": "Stream",
"stream": "Actividad",
"label": "Etiqueta",
"linkType": "Tipo de enlace",
"entityForeign": "Entidad Foránea",
@@ -16,7 +16,10 @@
"link": "Enlace",
"labelForeign": "Etiqueta Foránea",
"sortBy": "Orden por defecto (campo)",
"sortDirection": "Orden por defecto (dirección)"
"sortDirection": "Orden por defecto (dirección)",
"relationName": "Nombre de la Tabla Intermedia",
"linkMultipleField": "Enlaza Múltiples Campos",
"linkMultipleFieldForeign": "Enlaza Múltiples Campos foráneos"
},
"options": {
"type": {
@@ -11,9 +11,9 @@
"ScheduledJob": "Tarea Programada",
"ExternalAccount": "Cuenta Externa",
"Extension": "Extension",
"Dashboard": "Dashboard",
"Dashboard": "Escritorio",
"InboundEmail": "Correo Entrante",
"Stream": "Stream",
"Stream": "Actividad",
"Import": "Importar",
"Template": "Plantilla",
"Job": "Trabajo",
@@ -31,9 +31,9 @@
"ScheduledJob": "Tareas Programadas",
"ExternalAccount": "Cuentas Externas",
"Extension": "Extensiones",
"Dashboard": "Dashboard",
"Dashboard": "Escritorio",
"InboundEmail": "Grupo de Cuentas de Correo",
"Stream": "Stream",
"Stream": "Actividad",
"Import": "Importar Resultados",
"Template": "Plantillas",
"Job": "Trabajos",
@@ -82,9 +82,9 @@
"Overview": "Vista",
"Details": "Detalles",
"Add Field": "Añadir Campo",
"Add Dashlet": "Añadir Dashlet",
"Add Dashlet": "Añadir Cuadro",
"Filter": "Filtro",
"Edit Dashboard": "Editar Dashboard",
"Edit Dashboard": "Editar Escritorio",
"Add": "Añadir",
"Add Item": "Agregar item",
"Reset": "Resetear",
@@ -130,9 +130,9 @@
"Inactive": "Inactivo",
"Write your comment here": "Escriba su comentario aquí",
"Post": "Entrada",
"Stream": "Stream",
"Stream": "Actividad",
"Show more": "Mostrar mas",
"Dashlet Options": "Opciones Dashlet",
"Dashlet Options": "Opciones de Cuadros",
"Full Form": "Formulario Completo",
"Insert": "Insertar",
"Person": "Persona",
@@ -263,7 +263,7 @@
"children": "Hijos"
},
"dashlets": {
"Stream": "Stream"
"Stream": "Actividad"
},
"notificationMessages": {
"assign": "{entityType} {entity} ha sido asignado a usted",
@@ -430,12 +430,15 @@
"searchRanges": {
"is": "Es",
"isEmpty": "Está vacío",
"isNotEmpty": "No Está Vacío"
"isNotEmpty": "No Está Vacío",
"isOneOf": "Es Uno De"
},
"varcharSearchRanges": {
"equals": "Equivale",
"startsWith": "Comienza con",
"contains": "Contiene"
"contains": "Contiene",
"isEmpty": "Está vacío",
"isNotEmpty": "No Está Vacío"
},
"intSearchRanges": {
"equals": "Equivale",
@@ -20,7 +20,8 @@
"replyToAddress": "Responder a la Dirección",
"replyFromName": "Respuesta de Nombre",
"targetUserPosition": "Objetivo Posición Usuario",
"fetchSince": "Traer Desde"
"fetchSince": "Traer Desde",
"addAllTeamUsers": "Para todos los usuarios del equipo"
},
"tooltips": {
"reply": "Notifique a los remitentes de correo que han recibido sus mensajes.\n\n Sólo un correo será enviado a un destinatario particular durante un período de tiempo para evitar bucles.",
@@ -29,7 +30,8 @@
"caseDistribution": "¿Cómo serán asignados a los casos? Asignados directamente a un usuario o al equipo.",
"assignToUser": "Correos de usuarios/Casos serán asignados a.",
"team": "Correo del equipo/Casos relacionados con.",
"targetUserPosition": "Definir la posición de los usuarios que se distribuirán con los casos."
"targetUserPosition": "Definir la posición de los usuarios que se distribuirán con los casos.",
"addAllTeamUsers": "Los correos van a aparecer en la Bandeja de Entrada de todos los usuarios de un equipo específico."
},
"links": {
"filters": "Filtros"
@@ -43,6 +43,6 @@
"Locale": "Localización"
},
"tooltips": {
"autoFollowEntityTypeList": "El usuario seguirá automáticamente todos los nuevos registros de los tipos de entidades seleccionadas, verá la información en el stream y recibirá notificaciones."
"autoFollowEntityTypeList": "El usuario seguirá automáticamente todos los nuevos registros de los tipos de entidades seleccionadas, verá la información en las actividades y recibirá notificaciones."
}
}
@@ -11,7 +11,7 @@
},
"tooltips": {
"assignmentPermission": "Allows to restrict an ability for users to assign records to other users.\n\nall - no restriction\n\nteam - can assign to users from own teams\n\nno - can assign only to self",
"userPermission": "Allows users to view activities of other users.\n\nall - can view all\n\nteam - can view user's activities from own teams\n\nno - can't view"
"userPermission": "Allows to restrict an ability for users to view activities, calendar and stream of other users.\n\nall - can view all\n\nteam - can view activities of teammates only\n\nno - can't view"
},
"labels": {
"Access": "Acceso",
@@ -16,7 +16,8 @@
"Cleanup": "Limpiar",
"CheckInboundEmails": "Comprobar Correos Entrantes",
"CheckEmailAccounts": "Compruebe cuentas de correo personales",
"SendEmailReminders": "Enviar Recordatorios por Email"
"SendEmailReminders": "Enviar Recordatorios por Email",
"AuthTokenControl": "Control del Token de Autenticación"
},
"cronSetup": {
"linux": "Nota: Añada esta línea al archivo crontab para ejecutar trabajos Espo programados:",
@@ -59,7 +59,9 @@
"massEmailMaxPerHourCount": "mäximo número de mensajes de correo enviado por hora",
"personalEmailMaxPortionSize": "Máximo tamaño de la porción de correo para recuperar de una cuenta personal",
"inboundEmailMaxPortionSize": "Máximo tamaño de la porción de correo para recuperar de una cuenta de grupo",
"maxEmailAccountCount": "Máximo número de cuentas de correo personal por usuario"
"maxEmailAccountCount": "Máximo número de cuentas de correo personal por usuario",
"authTokenLifetime": "Vida del Token de Autenticación (horas)",
"authTokenMaxIdleTime": "Máximo tiempo de inactividad del Token de Autenticación (horas)"
},
"options": {
"weekStart": {
@@ -71,7 +73,9 @@
"recordsPerPageSmall": "Contador de registros en los paneles de relación",
"outboundEmailIsShared": "Permitir a los usuarios enviar mensajes a través de esta SMTP.",
"followCreatedEntities": "Los usuarios seguirán automáticamente los registros que ellos crearon.",
"emailMessageMaxSize": "Todos los correos entrantes que superen un tamaño especificado se omitirán."
"emailMessageMaxSize": "Todos los correos entrantes que superen un tamaño especificado se omitirán.",
"authTokenLifetime": "Define cuanto tiempo de vida tienen los tokens.\n0 - significa que no caduca.",
"authTokenMaxIdleTime": "Define cuándo caduca el Token luego del último acceso.\n0 - significa que no caduca."
},
"labels": {
"System": "Sistema",
@@ -6,7 +6,7 @@
"body": "Inhoud",
"subject": "Onderwerp",
"attachments": "Bijlagen",
"insertField": "Project-Id-Version: \nPOT-Creation-Date: \nPO-Revision-Date: \nLast-Translator: \nLanguage-Team: EspoCRM <infobox@espocrm.com>\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nLanguage: nl_NL\n"
"insertField": "Insert Field"
},
"links": {
},
@@ -386,13 +386,15 @@
},
"searchRanges": {
"is": "Is",
"isEmpty": "Is Empty",
"isNotEmpty": "Is Not Empty"
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто"
},
"varcharSearchRanges": {
"equals": "Дорівнює",
"startsWith": "Starts With",
"contains": "Contains"
"contains": "Contains",
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто"
},
"intSearchRanges": {
"equals": "Дорівнює",
@@ -2,6 +2,6 @@
{"name":"personStringData","width":16,"notSortable": true, "customLabel": ""},
{"name":"subject","width":35,"link":true,"notSortable": true},
{"name":"status","notSortable": true, "width":10},
{"name":"parent","notSortable": true},
{"name":"parent","notSortable": true, "width": 16},
{"name":"dateSent","view": "Fields.DatetimeShort", "notSortable": true, "width":10, "align": "right"}
]
@@ -6,7 +6,9 @@
{
"name":"name"
},
false
{
"name":"oneOff"
}
],
[{"name":"subject","fullWidth":true}],
[
@@ -2,8 +2,8 @@
{
"label":"",
"rows":[
[{"name":"name"}],
[{"name":"subject"}],
[{"name":"name"}, {"name":"oneOff"}],
[{"name":"subject","fullWidth":true}],
[{"name":"body","fullWidth":true}],
[{"name":"attachments"},{"name":"isHtml"}]
]

Some files were not shown because too many files have changed in this diff Show More