mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-09 10:47:00 +00:00
Compare commits
246 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858658768d | ||
|
|
86421010b9 | ||
|
|
8675db36f3 | ||
|
|
f36a98a824 | ||
|
|
3c71ff05f3 | ||
|
|
6039939308 | ||
|
|
1ddea9938b | ||
|
|
5d3b515ef1 | ||
|
|
31b7db4033 | ||
|
|
6a7d59ba20 | ||
|
|
0ab0e46d67 | ||
|
|
2777fe49d9 | ||
|
|
1e246e8197 | ||
|
|
d8eec1068d | ||
|
|
0021e16962 | ||
|
|
b046da1d79 | ||
|
|
2ca8f3af6f | ||
|
|
b180ac2314 | ||
|
|
9cc646e3b8 | ||
|
|
a2fee68732 | ||
|
|
dc0d150000 | ||
|
|
76cc4a8d2c | ||
|
|
6e4c097dee | ||
|
|
af398cb5d4 | ||
|
|
3865bba926 | ||
|
|
3ad39d3337 | ||
|
|
1dfe1eb9bf | ||
|
|
7885a9cac3 | ||
|
|
bb63751332 | ||
|
|
083770f705 | ||
|
|
a65862325f | ||
|
|
9465ca46b2 | ||
|
|
62f05f370f | ||
|
|
8e408d93cb | ||
|
|
7c1d587ae0 | ||
|
|
cb42bfe8e7 | ||
|
|
32a340afe0 | ||
|
|
0ff8246a00 | ||
|
|
c9c58d373a | ||
|
|
af7aea720b | ||
|
|
dee488a9de | ||
|
|
88ec71400a | ||
|
|
10ad5a804a | ||
|
|
45f948d96c | ||
|
|
e5e6a32ce5 | ||
|
|
cf5202a065 | ||
|
|
f37a49ddaf | ||
|
|
4858f964c8 | ||
|
|
5b50215242 | ||
|
|
461e356419 | ||
|
|
7146f3d3aa | ||
|
|
43d289bc56 | ||
|
|
65c843b581 | ||
|
|
d08704c95c | ||
|
|
fb9100efdc | ||
|
|
ee2182638a | ||
|
|
8ba9fb4693 | ||
|
|
b427d8cade | ||
|
|
89de19fd2e | ||
|
|
2f4df63281 | ||
|
|
e12eade143 | ||
|
|
15b0d147d9 | ||
|
|
4901b8c5a0 | ||
|
|
8c4f3b1103 | ||
|
|
2225906eac | ||
|
|
895b3aa7c9 | ||
|
|
67b359ff18 | ||
|
|
62200d393e | ||
|
|
166522e4e6 | ||
|
|
cc3d7a9f20 | ||
|
|
81ce479293 | ||
|
|
afa6591903 | ||
|
|
e89cb2547e | ||
|
|
7b982acc3e | ||
|
|
e24fc19314 | ||
|
|
0d9473b1e9 | ||
|
|
db4ccbe590 | ||
|
|
f085d2140c | ||
|
|
4f2117453d | ||
|
|
919fff6cd5 | ||
|
|
b77f0a705e | ||
|
|
73699894be | ||
|
|
4282d1766b | ||
|
|
3ebb9ea8fc | ||
|
|
5635fddf3d | ||
|
|
9501e116cb | ||
|
|
c0997f1d97 | ||
|
|
106961ff03 | ||
|
|
fd2b9cc818 | ||
|
|
d90853ff73 | ||
|
|
a8bcd1dc2f | ||
|
|
1f4ae0f07c | ||
|
|
cf0a9fd808 | ||
|
|
036e10cbf7 | ||
|
|
0a22510566 | ||
|
|
44f14c1d29 | ||
|
|
e6796390db | ||
|
|
c11d52cef6 | ||
|
|
33e8b8b0c1 | ||
|
|
04dbe9129a | ||
|
|
8e1a4ba368 | ||
|
|
920b5244a8 | ||
|
|
cd57f6c600 | ||
|
|
7e6a32ab06 | ||
|
|
ee0dc257b2 | ||
|
|
a8e6421517 | ||
|
|
1c5d2bd756 | ||
|
|
1fc59b28aa | ||
|
|
ed603c5f98 | ||
|
|
0b7743419e | ||
|
|
81ff01e9be | ||
|
|
4845622949 | ||
|
|
122cd9c56d | ||
|
|
379562b0d7 | ||
|
|
c72d6d928a | ||
|
|
b09c1366e3 | ||
|
|
00e0427fdd | ||
|
|
85f888d60b | ||
|
|
f22856dd91 | ||
|
|
5673d253e4 | ||
|
|
27ce836d4a | ||
|
|
a490ded36a | ||
|
|
bd54195403 | ||
|
|
21d8e291e4 | ||
|
|
b4fa62b403 | ||
|
|
1f3d7063d2 | ||
|
|
e5709571e4 | ||
|
|
a80d847f44 | ||
|
|
9de7105cde | ||
|
|
cd7cc188e1 | ||
|
|
e68a5f1979 | ||
|
|
c3e16a896e | ||
|
|
eafd0c3840 | ||
|
|
d5b5bb31c7 | ||
|
|
eaab96732a | ||
|
|
0dfed7279e | ||
|
|
b0e973d8ba | ||
|
|
1d809c10b0 | ||
|
|
e0279f403f | ||
|
|
fa75da658c | ||
|
|
d23689aab7 | ||
|
|
d4cc305265 | ||
|
|
695d4b0cda | ||
|
|
2107d93cd2 | ||
|
|
fb256ca29f | ||
|
|
557bcb4ecd | ||
|
|
50a272473b | ||
|
|
0eb9544371 | ||
|
|
620fa607d4 | ||
|
|
304d216b0e | ||
|
|
ce2fe9d50c | ||
|
|
eadf7835db | ||
|
|
048f65c8f6 | ||
|
|
c30c4163a6 | ||
|
|
8d6db72516 | ||
|
|
71864056bc | ||
|
|
e375105a5f | ||
|
|
e066f4c6b0 | ||
|
|
b032aa4d8d | ||
|
|
342a18e0ac | ||
|
|
e27e9de701 | ||
|
|
b14b199d43 | ||
|
|
f77a7c32d5 | ||
|
|
9f84d3f233 | ||
|
|
be82244c6b | ||
|
|
7fed72d391 | ||
|
|
54d615b64d | ||
|
|
2dbbd1e23d | ||
|
|
cba8867389 | ||
|
|
32d47db15a | ||
|
|
2c65c9ff9f | ||
|
|
fa5d63253b | ||
|
|
b7ae252d3d | ||
|
|
f07b43abda | ||
|
|
276db37baf | ||
|
|
6adad84a29 | ||
|
|
f137298c49 | ||
|
|
f8839518d5 | ||
|
|
1ae39d1a40 | ||
|
|
94f920324e | ||
|
|
dbc3f7c7d6 | ||
|
|
94c00e2901 | ||
|
|
7de988104b | ||
|
|
03bc90968c | ||
|
|
a5401a22b9 | ||
|
|
4c1375d8b2 | ||
|
|
bab4a3b0d6 | ||
|
|
33e135f227 | ||
|
|
56b9d8d5c1 | ||
|
|
38ec88e302 | ||
|
|
10be208d8d | ||
|
|
38d227a948 | ||
|
|
1a8e4435e2 | ||
|
|
12febbdff5 | ||
|
|
ebea168350 | ||
|
|
704519f80b | ||
|
|
5f1000ddd3 | ||
|
|
2ee7e6cf4a | ||
|
|
2989975829 | ||
|
|
a4c068427d | ||
|
|
e1a76b9924 | ||
|
|
1f2efcd716 | ||
|
|
576dfe068f | ||
|
|
f76860cdfd | ||
|
|
56f8e68599 | ||
|
|
e8d75808ef | ||
|
|
bc8bf89d6e | ||
|
|
91385cc3f0 | ||
|
|
ad21511c72 | ||
|
|
938a24f102 | ||
|
|
43b041ed6c | ||
|
|
5e3f048795 | ||
|
|
3aec3ef6d2 | ||
|
|
712a450734 | ||
|
|
5675b6721b | ||
|
|
a4e23b8adb | ||
|
|
9d49f418c8 | ||
|
|
ea9f70dffa | ||
|
|
b624accbe8 | ||
|
|
12f974635e | ||
|
|
cfce68eb7e | ||
|
|
db8fbd1ed3 | ||
|
|
7c549556ac | ||
|
|
06395f3ff5 | ||
|
|
4065ec5477 | ||
|
|
31d45a0583 | ||
|
|
696faa4468 | ||
|
|
625876f123 | ||
|
|
974cd05276 | ||
|
|
fb5db991a3 | ||
|
|
3dafb7e922 | ||
|
|
ee640273a1 | ||
|
|
b92de17f72 | ||
|
|
abc48ca76f | ||
|
|
46443aae7e | ||
|
|
a88c3283d6 | ||
|
|
2b9653ee0b | ||
|
|
2cf79abdb1 | ||
|
|
20a000a46c | ||
|
|
95bfd5bace | ||
|
|
a6b5f38aed | ||
|
|
da736008f1 | ||
|
|
1de98d9616 | ||
|
|
31a6143cc3 | ||
|
|
8502a84a3f | ||
|
|
2234678728 |
@@ -28,6 +28,7 @@ module.exports = function (grunt) {
|
||||
'client/lib/handlebars.js',
|
||||
'client/lib/base64.js',
|
||||
'client/lib/jquery-ui.min.js',
|
||||
'client/lib/jquery.ui.touch-punch.min.js',
|
||||
'client/lib/moment.min.js',
|
||||
'client/lib/moment-timezone-with-data.min.js',
|
||||
'client/lib/jquery.timepicker.min.js',
|
||||
|
||||
@@ -34,6 +34,7 @@ use \Espo\ORM\Entity;
|
||||
|
||||
class Email extends \Espo\Core\Acl\Base
|
||||
{
|
||||
protected $ownerUserIdAttribute = 'usersIds';
|
||||
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
@@ -118,4 +119,3 @@ class Email extends \Espo\Core\Acl\Base
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
53
application/Espo/Acl/Import.php
Normal file
53
application/Espo/Acl/Import.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Import extends \Espo\Core\Acl\Base
|
||||
{
|
||||
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) return true;
|
||||
if ($user->id === $entity->get('createdById')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntityDelete(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) return true;
|
||||
if ($user->id === $entity->get('createdById')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ use \Espo\ORM\Entity;
|
||||
|
||||
class Note extends \Espo\Core\Acl\Base
|
||||
{
|
||||
protected $deleteThresholdPeriod = '1 month';
|
||||
|
||||
protected $editThresholdPeriod = '7 days';
|
||||
|
||||
public function checkIsOwner(EntityUser $user, Entity $entity)
|
||||
{
|
||||
if ($entity->get('type') === 'Post' && $user->id === $entity->get('createdById')) {
|
||||
@@ -41,5 +45,60 @@ class Note extends \Espo\Core\Acl\Base
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function checkEntityEdit(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->checkEntity($user, $entity, $data, 'edit')) {
|
||||
if ($this->checkIsOwner($user, $entity)) {
|
||||
$createdAt = $entity->get('createdAt');
|
||||
if ($createdAt) {
|
||||
$noteEditThresholdPeriod = '-' . $this->getConfig()->get('noteEditThresholdPeriod', $this->editThresholdPeriod);
|
||||
$dt = new \DateTime();
|
||||
$dt->modify($noteEditThresholdPeriod);
|
||||
try {
|
||||
if ($dt->format('U') > (new \DateTime($createdAt))->format('U')) {
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkEntityDelete(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->checkEntity($user, $entity, $data, 'delete')) {
|
||||
if ($this->checkIsOwner($user, $entity)) {
|
||||
$createdAt = $entity->get('createdAt');
|
||||
if ($createdAt) {
|
||||
$deleteThresholdPeriod = '-' . $this->getConfig()->get('noteDeleteThresholdPeriod', $this->deleteThresholdPeriod);
|
||||
$dt = new \DateTime();
|
||||
$dt->modify($deleteThresholdPeriod);
|
||||
try {
|
||||
if ($dt->format('U') > (new \DateTime($createdAt))->format('U')) {
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use \Espo\ORM\Entity;
|
||||
|
||||
class Email extends \Espo\Core\AclPortal\Base
|
||||
{
|
||||
protected $ownerUserIdAttribute = 'usersIds';
|
||||
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
|
||||
@@ -34,5 +34,11 @@ use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Attachment extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
public function actionList($params, $data, $request)
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
return parent::actionList($params, $data, $request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
if (!$this->getAcl()->check('Import')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
@@ -90,10 +90,9 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
$attachment->set('type', 'text/csv');
|
||||
$attachment->set('role', 'Import File');
|
||||
$attachment->set('name', 'import-file.csv');
|
||||
$attachment->set('contents', $contents);
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
$this->getFileStorageManager()->putContents($attachment, $contents);
|
||||
|
||||
return array(
|
||||
'attachmentId' => $attachment->id
|
||||
);
|
||||
@@ -127,7 +126,7 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data->fieldDelimiter)) {
|
||||
if (!isset($data->delimiter)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
@@ -167,7 +166,7 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data->fields)) {
|
||||
if (!isset($data->attributeList)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
@@ -178,7 +177,7 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
|
||||
$importParams = array(
|
||||
'headerRow' => !empty($data->headerRow),
|
||||
'fieldDelimiter' => $data->fieldDelimiter,
|
||||
'delimiter' => $data->delimiter,
|
||||
'textQualifier' => $data->textQualifier,
|
||||
'dateFormat' => $data->dateFormat,
|
||||
'timeFormat' => $data->timeFormat,
|
||||
@@ -202,7 +201,7 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return $this->getService('Import')->import($data->entityType, $data->fields, $attachmentId, $importParams);
|
||||
return $this->getService('Import')->import($data->entityType, $data->attributeList, $attachmentId, $importParams);
|
||||
}
|
||||
|
||||
public function postActionUnmarkAsDuplicate($params, $data)
|
||||
|
||||
51
application/Espo/Controllers/LeadCapture.php
Normal file
51
application/Espo/Controllers/LeadCapture.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class LeadCapture extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
public function postActionLeadCapture($params, $data, $request)
|
||||
{
|
||||
if (empty($params['apiKey'])) throw new BadRequest('No API key provided.');
|
||||
if (empty($data)) throw new BadRequest('No payload provided.');
|
||||
|
||||
return $this->getRecordService()->leadCapture($params['apiKey'], $data);
|
||||
}
|
||||
|
||||
public function postActionGenerateNewApiKey($params, $data, $request)
|
||||
{
|
||||
if (empty($data->id)) throw new BadRequest();
|
||||
|
||||
return $this->getRecordService()->generateNewApiKeyForEntity($data->id)->getValueMap();
|
||||
}
|
||||
}
|
||||
38
application/Espo/Controllers/LeadCaptureLogRecord.php
Normal file
38
application/Espo/Controllers/LeadCaptureLogRecord.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class LeadCaptureLogRecord extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class Preferences extends \Espo\Core\Controllers\Base
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if ($this->getAcl()->getLevel('Preferences', 'read') === 'no') {
|
||||
if ($this->getAcl()->getLevel('Preferences', 'edit') === 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -142,5 +142,45 @@ class Preferences extends \Espo\Core\Controllers\Base
|
||||
|
||||
return $entity->getValueMap();
|
||||
}
|
||||
}
|
||||
|
||||
public function postActionResetDashboard($params, $data)
|
||||
{
|
||||
if (empty($data->id)) throw new BadRequest();
|
||||
|
||||
$userId = $data->id;
|
||||
|
||||
$this->handleUserAccess($userId);
|
||||
|
||||
$user = $this->getEntityManager()->getEntity('User', $userId);
|
||||
$preferences = $this->getEntityManager()->getEntity('Preferences', $userId);
|
||||
if (!$user) throw new NotFound();
|
||||
if (!$preferences) throw new NotFound();
|
||||
|
||||
if ($user->isPortal()) throw new Forbidden();
|
||||
|
||||
if ($this->getAcl()->getLevel('Preferences', 'edit') === 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$forbiddenAttributeList = $this->getAcl()->getScopeForbiddenAttributeList('Preferences', 'edit');
|
||||
|
||||
if (in_array('dashboardLayout', $forbiddenAttributeList)) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$dashboardLayout = $this->getConfig()->get('dashboardLayout');
|
||||
$dashletsOptions = $this->getConfig()->get('dashletsOptions');
|
||||
|
||||
$preferences->set([
|
||||
'dashboardLayout' => $dashboardLayout,
|
||||
'dashletsOptions' => $dashletsOptions
|
||||
]);
|
||||
|
||||
$this->getEntityManager()->saveEntity($preferences);
|
||||
|
||||
return (object) [
|
||||
'dashboardLayout' => $preferences->get('dashboardLayout'),
|
||||
'dashletsOptions' => $preferences->get('dashletsOptions')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ class Base implements Injectable
|
||||
|
||||
protected $injections = array();
|
||||
|
||||
protected $ownerUserIdAttribute = null;
|
||||
|
||||
public function inject($name, $object)
|
||||
{
|
||||
$this->injections[$name] = $object;
|
||||
@@ -281,5 +283,23 @@ class Base implements Injectable
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getOwnerUserIdAttribute(Entity $entity)
|
||||
{
|
||||
if ($this->ownerUserIdAttribute) {
|
||||
return $this->ownerUserIdAttribute;
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
return 'assignedUsersIds';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('assignedUserId')) {
|
||||
return 'assignedUserId';
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
return 'createdById';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ class Application
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->getContainer()->get('config');
|
||||
}
|
||||
|
||||
public function run($name = 'default')
|
||||
{
|
||||
$this->routeHooks();
|
||||
@@ -134,6 +139,11 @@ class Application
|
||||
|
||||
public function runCron()
|
||||
{
|
||||
if ($this->getConfig()->get('cronDisabled')) {
|
||||
$GLOBALS['log']->warning("Cron is not run because it's disabled with 'cronDisabled' param.");
|
||||
return;
|
||||
}
|
||||
|
||||
$auth = $this->createAuth();
|
||||
$auth->useNoAuth();
|
||||
|
||||
@@ -155,7 +165,7 @@ class Application
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
$config = $this->getContainer()->get('config');
|
||||
$config = $this->getConfig();
|
||||
|
||||
if (file_exists($config->getConfigPath()) && $config->get('isInstalled')) {
|
||||
return true;
|
||||
@@ -244,7 +254,7 @@ class Application
|
||||
|
||||
protected function getRouteList()
|
||||
{
|
||||
$routes = new \Espo\Core\Utils\Route($this->getContainer()->get('config'), $this->getMetadata(), $this->getContainer()->get('fileManager'));
|
||||
$routes = new \Espo\Core\Utils\Route($this->getConfig(), $this->getMetadata(), $this->getContainer()->get('fileManager'));
|
||||
|
||||
|
||||
return $routes->getAll();
|
||||
@@ -252,7 +262,7 @@ class Application
|
||||
|
||||
protected function initRoutes()
|
||||
{
|
||||
$crudList = array_keys($this->getContainer()->get('config')->get('crud'));
|
||||
$crudList = array_keys($this->getConfig()->get('crud'));
|
||||
|
||||
foreach ($this->getRouteList() as $route) {
|
||||
$method = strtolower($route['method']);
|
||||
@@ -273,7 +283,7 @@ class Application
|
||||
|
||||
protected function initAutoloads()
|
||||
{
|
||||
$autoload = new \Espo\Core\Utils\Autoload($this->getContainer()->get('config'), $this->getMetadata(), $this->getContainer()->get('fileManager'));
|
||||
$autoload = new \Espo\Core\Utils\Autoload($this->getConfig(), $this->getMetadata(), $this->getContainer()->get('fileManager'));
|
||||
|
||||
try {
|
||||
$autoloadList = $autoload->getAll();
|
||||
|
||||
@@ -178,7 +178,7 @@ class Container
|
||||
|
||||
protected function loadMailSender()
|
||||
{
|
||||
$className = $this->getServiceClassName('mailSernder', '\\Espo\\Core\\Mail\\Sender');
|
||||
$className = $this->getServiceClassName('mailSender', '\\Espo\\Core\\Mail\\Sender');
|
||||
return new $className(
|
||||
$this->get('config'),
|
||||
$this->get('entityManager')
|
||||
@@ -247,6 +247,14 @@ class Container
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadInternalAclManager()
|
||||
{
|
||||
$className = $this->getServiceClassName('acl', '\\Espo\\Core\\AclManager');
|
||||
return new $className(
|
||||
$this->get('container')
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadAcl()
|
||||
{
|
||||
$className = $this->getServiceClassName('acl', '\\Espo\\Core\\Acl');
|
||||
|
||||
36
application/Espo/Core/Exceptions/ServiceUnavailable.php
Normal file
36
application/Espo/Core/Exceptions/ServiceUnavailable.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Core\Exceptions;
|
||||
|
||||
class ServiceUnavailable extends \Exception
|
||||
{
|
||||
protected $code = 503;
|
||||
|
||||
}
|
||||
@@ -48,6 +48,6 @@ class ContainsType extends \Espo\Core\Formula\Functions\Base
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($string, $needle) !== false;
|
||||
return mb_strpos($string, $needle) !== false;
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,6 @@ class LowerCaseType extends \Espo\Core\Formula\Functions\Base
|
||||
$value = strval($value);
|
||||
}
|
||||
|
||||
return strtolower($value);
|
||||
return mb_strtolower($value);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
@@ -52,9 +52,9 @@ class SubstringType extends \Espo\Core\Formula\Functions\Base
|
||||
|
||||
if (count($item->value) > 2) {
|
||||
$length = $this->evaluate($item->value[2]);
|
||||
return substr($string, $start, $length);
|
||||
return mb_substr($string, $start, $length);
|
||||
} else {
|
||||
return substr($string, $start);
|
||||
return mb_substr($string, $start);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,6 @@ class UpperCaseType extends \Espo\Core\Formula\Functions\Base
|
||||
$value = strval($value);
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
return mb_strtoupper($value);
|
||||
}
|
||||
}
|
||||
@@ -176,6 +176,8 @@ class Parser
|
||||
|
||||
$this->processStrings($expression, $modifiedExpression, $splitterIndexList, true);
|
||||
|
||||
$expressionOutOfBraceList = [];
|
||||
|
||||
for ($i = 0; $i < strlen($modifiedExpression); $i++) {
|
||||
if ($modifiedExpression[$i] === '(') {
|
||||
$braceCounter++;
|
||||
@@ -186,6 +188,11 @@ class Parser
|
||||
if ($braceCounter === 0 && $i < strlen($modifiedExpression) - 1) {
|
||||
$hasExcessBraces = false;
|
||||
}
|
||||
if ($braceCounter === 0) {
|
||||
$expressionOutOfBraceList[] = true;
|
||||
} else {
|
||||
$expressionOutOfBraceList[] = false;
|
||||
}
|
||||
}
|
||||
if ($braceCounter !== 0) {
|
||||
throw new Error('Incorrect round brackets in expression ' . $expression . '.');
|
||||
@@ -226,7 +233,13 @@ class Parser
|
||||
|
||||
foreach ($this->priorityList as $operationList) {
|
||||
foreach ($operationList as $operator) {
|
||||
$index = strpos($expression, $operator, 1);
|
||||
$startFrom = 1;
|
||||
while (true) {
|
||||
$index = strpos($expression, $operator, $startFrom);
|
||||
if ($index === false) break;
|
||||
if ($expressionOutOfBraceList[$index]) break;
|
||||
$startFrom = $index + 1;
|
||||
}
|
||||
if ($index !== false) {
|
||||
$possibleRightOperator = null;
|
||||
if (strlen($operator) === 1) {
|
||||
|
||||
@@ -137,7 +137,7 @@ class Entity extends \Espo\ORM\Entity
|
||||
}
|
||||
|
||||
$this->set($idsAttribute, $ids);
|
||||
if (!$this->hasFetched($idsAttribute)) {
|
||||
if (!$this->isNew() && !$this->hasFetched($idsAttribute)) {
|
||||
$this->setFetched($idsAttribute, $ids);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,13 @@ class Entity extends \Espo\ORM\Entity
|
||||
$entityName = $entity->get('name');
|
||||
}
|
||||
|
||||
$this->set($field . 'Id', $entityId);
|
||||
$idAttribute = $field . 'Id';
|
||||
|
||||
if (!$this->isNew() && !$this->hasFetched($idAttribute)) {
|
||||
$this->setFetched($idAttribute, $entityId);
|
||||
}
|
||||
|
||||
$this->set($idAttribute, $entityId);
|
||||
$this->set($field . 'Name', $entityName);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected $processFieldsBeforeSaveDisabled = false;
|
||||
|
||||
protected $processFieldsAfterRemoveDisabled = false;
|
||||
|
||||
protected function addDependency($name)
|
||||
{
|
||||
$this->dependencies[] = $name;
|
||||
@@ -124,20 +126,17 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
return;
|
||||
}
|
||||
|
||||
$defs = $metadata->get('entityDefs.' . $entityType);
|
||||
$defs = $metadata->get(['entityDefs', $entityType]);
|
||||
|
||||
foreach ($defs['fields'] as $field => $d) {
|
||||
if (isset($d['type']) && $d['type'] == 'currency') {
|
||||
if (!empty($d['notStorable'])) {
|
||||
continue;
|
||||
}
|
||||
if (empty($params['customJoin'])) {
|
||||
$params['customJoin'] = '';
|
||||
}
|
||||
$alias = Util::toUnderScore($field) . "_currency_alias";
|
||||
$params['customJoin'] .= "
|
||||
LEFT JOIN currency AS `{$alias}` ON {$alias}.id = ".Util::toUnderScore($entityType).".".Util::toUnderScore($field)."_currency
|
||||
";
|
||||
if (!empty($d['notStorable'])) continue;
|
||||
if (empty($params['leftJoins'])) $params['leftJoins'] = [];
|
||||
$alias = $field . 'CurrencyRate';
|
||||
|
||||
$params['leftJoins'][] = ['Currency', $alias, [
|
||||
$alias . '.id:' => $field . 'Currency'
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,45 +144,23 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected function handleEmailAddressParams(&$params)
|
||||
{
|
||||
$entityType = $this->entityType;
|
||||
|
||||
$defs = $this->getEntityManager()->getMetadata()->get($entityType);
|
||||
$defs = $this->getEntityManager()->getMetadata()->get($this->entityType);
|
||||
if (!empty($defs['relations']) && array_key_exists('emailAddresses', $defs['relations'])) {
|
||||
if (empty($params['leftJoins'])) {
|
||||
$params['leftJoins'] = array();
|
||||
}
|
||||
if (empty($params['whereClause'])) {
|
||||
$params['whereClause'] = array();
|
||||
}
|
||||
if (empty($params['joinConditions'])) {
|
||||
$params['joinConditions'] = array();
|
||||
}
|
||||
$params['leftJoins'][] = 'emailAddresses';
|
||||
$params['joinConditions']['emailAddresses'] = array(
|
||||
if (empty($params['leftJoins'])) $params['leftJoins'] = [];
|
||||
$params['leftJoins'][] = ['emailAddresses', null, [
|
||||
'primary' => 1
|
||||
);
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
protected function handlePhoneNumberParams(&$params)
|
||||
{
|
||||
$entityType = $this->entityType;
|
||||
|
||||
$defs = $this->getEntityManager()->getMetadata()->get($entityType);
|
||||
$defs = $this->getEntityManager()->getMetadata()->get($this->entityType);
|
||||
if (!empty($defs['relations']) && array_key_exists('phoneNumbers', $defs['relations'])) {
|
||||
if (empty($params['leftJoins'])) {
|
||||
$params['leftJoins'] = array();
|
||||
}
|
||||
if (empty($params['whereClause'])) {
|
||||
$params['whereClause'] = array();
|
||||
}
|
||||
if (empty($params['joinConditions'])) {
|
||||
$params['joinConditions'] = array();
|
||||
}
|
||||
$params['leftJoins'][] = 'phoneNumbers';
|
||||
$params['joinConditions']['phoneNumbers'] = array(
|
||||
if (empty($params['leftJoins'])) $params['leftJoins'] = [];
|
||||
$params['leftJoins'][] = ['phoneNumbers', null, [
|
||||
'primary' => 1
|
||||
);
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +183,11 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
protected function afterRemove(Entity $entity, array $options = array())
|
||||
{
|
||||
parent::afterRemove($entity, $options);
|
||||
|
||||
if (!$this->processFieldsAfterRemoveDisabled) {
|
||||
$this->processArrayFieldsRemove($entity);
|
||||
}
|
||||
|
||||
if (!$this->hooksDisabled && empty($options['skipHooks'])) {
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
|
||||
}
|
||||
@@ -287,6 +269,8 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$this->processPhoneNumberSave($entity);
|
||||
$this->processSpecifiedRelationsSave($entity);
|
||||
$this->processFileFieldsSave($entity);
|
||||
$this->processArrayFieldsSave($entity);
|
||||
$this->processWysiwygFieldsSave($entity);
|
||||
}
|
||||
|
||||
if (!$this->hooksDisabled && empty($options['skipHooks'])) {
|
||||
@@ -405,6 +389,57 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
}
|
||||
}
|
||||
|
||||
protected function processArrayFieldsSave(Entity $entity)
|
||||
{
|
||||
foreach ($entity->getAttributes() as $attribute => $defs) {
|
||||
if (!isset($defs['type']) || $defs['type'] !== Entity::JSON_ARRAY) continue;
|
||||
if (!$entity->has($attribute)) continue;
|
||||
if (!$entity->isAttributeChanged($attribute)) continue;
|
||||
if (!$entity->getAttributeParam($attribute, 'storeArrayValues')) continue;
|
||||
if ($entity->getAttributeParam($attribute, 'notStorable')) continue;
|
||||
$this->getEntityManager()->getRepository('ArrayValue')->storeEntityAttribute($entity, $attribute);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processWysiwygFieldsSave(Entity $entity)
|
||||
{
|
||||
if (!$entity->isNew()) return;
|
||||
|
||||
$fieldsDefs = $this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields'], []);
|
||||
foreach ($fieldsDefs as $field => $defs) {
|
||||
if (!empty($defs['type']) && $defs['type'] === 'wysiwyg') {
|
||||
$content = $entity->get($field);
|
||||
if (!$content) continue;
|
||||
if (preg_match_all("/\?entryPoint=attachment&id=([^&=\"']+)/", $content, $matches)) {
|
||||
if (!empty($matches[1]) && is_array($matches[1])) {
|
||||
foreach ($matches[1] as $id) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $id);
|
||||
if ($attachment) {
|
||||
if (!$attachment->get('relatedId') && !$attachment->get('sourceId')) {
|
||||
$attachment->set([
|
||||
'relatedId' => $entity->id,
|
||||
'relatedType' => $entity->getEntityType()
|
||||
]);
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function processArrayFieldsRemove(Entity $entity)
|
||||
{
|
||||
foreach ($entity->getAttributes() as $attribute => $defs) {
|
||||
if (!isset($defs['type']) || $defs['type'] !== Entity::JSON_ARRAY) continue;
|
||||
if (!$entity->getAttributeParam($attribute, 'storeArrayValues')) continue;
|
||||
if ($entity->getAttributeParam($attribute, 'notStorable')) continue;
|
||||
$this->getEntityManager()->getRepository('ArrayValue')->deleteEntityAttribute($entity, $attribute);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processEmailAddressSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('emailAddresses') && $entity->hasAttribute('emailAddress')) {
|
||||
@@ -465,17 +500,21 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$data->$columnName = $foreignEntity->get($columnField);
|
||||
}
|
||||
$existingColumnsData->$foreignId = $data;
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
if (!$entity->isNew()) {
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->has($fieldName)) {
|
||||
$entity->setFetched($fieldName, $existingIds);
|
||||
}
|
||||
if ($entity->has($columnsFieldsName) && !empty($columns)) {
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
if (!$entity->isNew()) {
|
||||
if ($entity->has($fieldName)) {
|
||||
$entity->setFetched($fieldName, $existingIds);
|
||||
}
|
||||
if ($entity->has($columnsFieldsName) && !empty($columns)) {
|
||||
$entity->setFetched($columnsFieldsName, $existingColumnsData);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($existingIds as $id) {
|
||||
@@ -540,13 +579,17 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$where[$foreignKey] = $entity->id;
|
||||
$previousForeignEntity = $this->getEntityManager()->getRepository($foreignEntityType)->where($where)->findOne();
|
||||
if ($previousForeignEntity) {
|
||||
$entity->setFetched($idFieldName, $previousForeignEntity->id);
|
||||
if (!$entity->isNew()) {
|
||||
$entity->setFetched($idFieldName, $previousForeignEntity->id);
|
||||
}
|
||||
if ($previousForeignEntity->id !== $entity->get($idFieldName)) {
|
||||
$previousForeignEntity->set($foreignKey, null);
|
||||
$this->getEntityManager()->saveEntity($previousForeignEntity);
|
||||
}
|
||||
} else {
|
||||
$entity->setFetched($idFieldName, null);
|
||||
if (!$entity->isNew()) {
|
||||
$entity->setFetched($idFieldName, null);
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->get($idFieldName)) {
|
||||
|
||||
@@ -61,10 +61,15 @@ class Event extends \Espo\Core\ORM\Repositories\RDB
|
||||
$pdo->query($sql);
|
||||
}
|
||||
|
||||
protected function afterSave(Entity $entity, array $options = array())
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterSave($entity, $options);
|
||||
$this->processReminderAfterSave($entity, $options);
|
||||
|
||||
parent::afterSave($entity, $options);
|
||||
}
|
||||
|
||||
protected function processReminderAfterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if (
|
||||
$entity->isNew() ||
|
||||
$entity->isAttributeChanged('assignedUserId') ||
|
||||
|
||||
@@ -161,6 +161,9 @@ class Base
|
||||
if ($desc) {
|
||||
$list = array_reverse($list);
|
||||
}
|
||||
foreach ($list as $i => $listItem) {
|
||||
$list[$i] = str_replace(',', '_COMMA_', $listItem);
|
||||
}
|
||||
$result['orderBy'] = 'LIST:' . $sortBy . ':' . implode(',', $list);
|
||||
return;
|
||||
}
|
||||
@@ -392,7 +395,8 @@ class Base
|
||||
protected function q($params, &$result)
|
||||
{
|
||||
if (isset($params['q']) && $params['q'] !== '') {
|
||||
$this->textFilter($params['q'], $result);
|
||||
$textFilter = $params['q'];
|
||||
$this->textFilter($textFilter, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,7 +972,7 @@ class Base
|
||||
|
||||
protected function getWherePart($item, &$result = null)
|
||||
{
|
||||
$part = array();
|
||||
$part = [];
|
||||
|
||||
$attribute = null;
|
||||
if (!empty($item['field'])) { // for backward compatibility
|
||||
@@ -1000,165 +1004,199 @@ class Base
|
||||
if (!array_key_exists('value', $item)) {
|
||||
$item['value'] = null;
|
||||
}
|
||||
$value = $item['value'];
|
||||
|
||||
if (!empty($item['type'])) {
|
||||
switch ($item['type']) {
|
||||
$type = $item['type'];
|
||||
|
||||
switch ($type) {
|
||||
case 'or':
|
||||
case 'and':
|
||||
case 'not':
|
||||
if (is_array($item['value'])) {
|
||||
$arr = array();
|
||||
foreach ($item['value'] as $i) {
|
||||
if (is_array($value)) {
|
||||
$arr = [];
|
||||
foreach ($value as $i) {
|
||||
$a = $this->getWherePart($i, $result);
|
||||
foreach ($a as $left => $right) {
|
||||
if (!empty($right) || is_null($right) || $right === '' || $right === 0 || $right === false) {
|
||||
$arr[] = array($left => $right);
|
||||
$arr[] = [$left => $right];
|
||||
}
|
||||
}
|
||||
}
|
||||
$part[strtoupper($item['type'])] = $arr;
|
||||
$part[strtoupper($type)] = $arr;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'like':
|
||||
$part[$attribute . '*'] = $item['value'];
|
||||
$part[$attribute . '*'] = $value;
|
||||
break;
|
||||
|
||||
case 'notLike':
|
||||
$part[$attribute . '!*'] = $item['value'];
|
||||
$part[$attribute . '!*'] = $value;
|
||||
break;
|
||||
|
||||
case 'equals':
|
||||
case 'on':
|
||||
$part[$attribute . '='] = $item['value'];
|
||||
$part[$attribute . '='] = $value;
|
||||
break;
|
||||
|
||||
case 'startsWith':
|
||||
$part[$attribute . '*'] = $item['value'] . '%';
|
||||
$part[$attribute . '*'] = $value . '%';
|
||||
break;
|
||||
|
||||
case 'endsWith':
|
||||
$part[$attribute . '*'] = '%' . $item['value'];
|
||||
$part[$attribute . '*'] = '%' . $value;
|
||||
break;
|
||||
|
||||
case 'contains':
|
||||
$part[$attribute . '*'] = '%' . $item['value'] . '%';
|
||||
$part[$attribute . '*'] = '%' . $value . '%';
|
||||
break;
|
||||
|
||||
case 'notContains':
|
||||
$part[$attribute . '!*'] = '%' . $item['value'] . '%';
|
||||
$part[$attribute . '!*'] = '%' . $value . '%';
|
||||
break;
|
||||
|
||||
case 'notEquals':
|
||||
case 'notOn':
|
||||
$part[$attribute . '!='] = $item['value'];
|
||||
$part[$attribute . '!='] = $value;
|
||||
break;
|
||||
|
||||
case 'greaterThan':
|
||||
case 'after':
|
||||
$part[$attribute . '>'] = $item['value'];
|
||||
$part[$attribute . '>'] = $value;
|
||||
break;
|
||||
|
||||
case 'lessThan':
|
||||
case 'before':
|
||||
$part[$attribute . '<'] = $item['value'];
|
||||
$part[$attribute . '<'] = $value;
|
||||
break;
|
||||
|
||||
case 'greaterThanOrEquals':
|
||||
$part[$attribute . '>='] = $item['value'];
|
||||
$part[$attribute . '>='] = $value;
|
||||
break;
|
||||
|
||||
case 'lessThanOrEquals':
|
||||
$part[$attribute . '<='] = $item['value'];
|
||||
$part[$attribute . '<='] = $value;
|
||||
break;
|
||||
|
||||
case 'in':
|
||||
$part[$attribute . '='] = $item['value'];
|
||||
$part[$attribute . '='] = $value;
|
||||
break;
|
||||
|
||||
case 'notIn':
|
||||
$part[$attribute . '!='] = $item['value'];
|
||||
$part[$attribute . '!='] = $value;
|
||||
break;
|
||||
|
||||
case 'isNull':
|
||||
$part[$attribute . '='] = null;
|
||||
break;
|
||||
|
||||
case 'isNotNull':
|
||||
case 'ever':
|
||||
$part[$attribute . '!='] = null;
|
||||
break;
|
||||
|
||||
case 'isTrue':
|
||||
$part[$attribute . '='] = true;
|
||||
break;
|
||||
|
||||
case 'isFalse':
|
||||
$part[$attribute . '='] = false;
|
||||
break;
|
||||
|
||||
case 'today':
|
||||
$part[$attribute . '='] = date('Y-m-d');
|
||||
break;
|
||||
|
||||
case 'past':
|
||||
$part[$attribute . '<'] = date('Y-m-d');
|
||||
break;
|
||||
|
||||
case 'future':
|
||||
$part[$attribute . '>='] = date('Y-m-d');
|
||||
break;
|
||||
|
||||
case 'lastSevenDays':
|
||||
$dt1 = new \DateTime();
|
||||
$dt2 = clone $dt1;
|
||||
$dt2->modify('-7 days');
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt2->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt1->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'lastXDays':
|
||||
$dt1 = new \DateTime();
|
||||
$dt2 = clone $dt1;
|
||||
$number = strval(intval($item['value']));
|
||||
$number = strval(intval($value));
|
||||
|
||||
$dt2->modify('-'.$number.' days');
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt2->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt1->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'nextXDays':
|
||||
$dt1 = new \DateTime();
|
||||
$dt2 = clone $dt1;
|
||||
$number = strval(intval($item['value']));
|
||||
$number = strval(intval($value));
|
||||
$dt2->modify('+'.$number.' days');
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt1->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt2->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'olderThanXDays':
|
||||
$dt1 = new \DateTime();
|
||||
$number = strval(intval($item['value']));
|
||||
$number = strval(intval($value));
|
||||
$dt1->modify('-'.$number.' days');
|
||||
$part[$attribute . '<'] = $dt1->format('Y-m-d');
|
||||
break;
|
||||
|
||||
case 'afterXDays':
|
||||
$dt1 = new \DateTime();
|
||||
$number = strval(intval($item['value']));
|
||||
$number = strval(intval($value));
|
||||
$dt1->modify('+'.$number.' days');
|
||||
$part[$attribute . '>'] = $dt1->format('Y-m-d');
|
||||
break;
|
||||
|
||||
case 'currentMonth':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->modify('first day of this month')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'lastMonth':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->modify('first day of last month')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'nextMonth':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->modify('first day of next month')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'currentQuarter':
|
||||
$dt = new \DateTime();
|
||||
$quarter = ceil($dt->format('m') / 3);
|
||||
$dt->modify('first day of January this year');
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'lastQuarter':
|
||||
$dt = new \DateTime();
|
||||
$quarter = ceil($dt->format('m') / 3);
|
||||
@@ -1168,33 +1206,37 @@ class Base
|
||||
$quarter = 4;
|
||||
$dt->modify('-1 year');
|
||||
}
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'currentYear':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'lastYear':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
);
|
||||
];
|
||||
break;
|
||||
|
||||
case 'between':
|
||||
if (is_array($item['value'])) {
|
||||
$part['AND'] = array(
|
||||
$attribute . '>=' => $item['value'][0],
|
||||
$attribute . '<=' => $item['value'][1],
|
||||
);
|
||||
if (is_array($value)) {
|
||||
$part['AND'] = [
|
||||
$attribute . '>=' => $value[0],
|
||||
$attribute . '<=' => $value[1],
|
||||
];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'columnLike':
|
||||
case 'columnIn':
|
||||
case 'columnIsNull':
|
||||
@@ -1204,30 +1246,30 @@ class Base
|
||||
$alias = $link . 'Filter' . strval(rand(10000, 99999));
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin([$link, $alias], $result);
|
||||
$value = $item['value'];
|
||||
$columnKey = $alias . 'Middle.' . $column;
|
||||
if ($item['type'] === 'columnIn') {
|
||||
if ($type === 'columnIn') {
|
||||
$part[$columnKey] = $value;
|
||||
} else if ($item['type'] === 'columnNotIn') {
|
||||
} else if ($type === 'columnNotIn') {
|
||||
$part[$columnKey . '!='] = $value;
|
||||
} else if ($item['type'] === 'columnIsNull') {
|
||||
} else if ($type === 'columnIsNull') {
|
||||
$part[$columnKey] = null;
|
||||
} else if ($item['type'] === 'columnIsNotNull') {
|
||||
} else if ($type === 'columnIsNotNull') {
|
||||
$part[$columnKey . '!='] = null;
|
||||
} else if ($item['type'] === 'columnLike') {
|
||||
} else if ($type === 'columnLike') {
|
||||
$part[$columnKey . '*'] = $value;
|
||||
} else if ($item['type'] === 'columnStartsWith') {
|
||||
} else if ($type === 'columnStartsWith') {
|
||||
$part[$columnKey . '*'] = $value . '%';
|
||||
} else if ($item['type'] === 'columnEndsWith') {
|
||||
} else if ($type === 'columnEndsWith') {
|
||||
$part[$columnKey . '*'] = '%' . $value;
|
||||
} else if ($item['type'] === 'columnContains') {
|
||||
} else if ($type === 'columnContains') {
|
||||
$part[$columnKey . '*'] = '%' . $value . '%';
|
||||
} else if ($item['type'] === 'columnEquals') {
|
||||
} else if ($type === 'columnEquals') {
|
||||
$part[$columnKey . '='] = $value;
|
||||
} else if ($item['type'] === 'columnNotEquals') {
|
||||
} else if ($type === 'columnNotEquals') {
|
||||
$part[$columnKey . '!='] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'isNotLinked':
|
||||
if (!$result) break;
|
||||
$alias = $attribute . 'IsNotLinkedFilter' . strval(rand(10000, 99999));
|
||||
@@ -1235,6 +1277,7 @@ class Base
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin([$attribute, $alias], $result);
|
||||
break;
|
||||
|
||||
case 'isLinked':
|
||||
if (!$result) break;
|
||||
$alias = $attribute . 'IsLinkedFilter' . strval(rand(10000, 99999));
|
||||
@@ -1242,6 +1285,7 @@ class Base
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin([$attribute, $alias], $result);
|
||||
break;
|
||||
|
||||
case 'linkedWith':
|
||||
$seed = $this->getSeed();
|
||||
$link = $attribute;
|
||||
@@ -1249,9 +1293,7 @@ class Base
|
||||
|
||||
$alias = $link . 'Filter' . strval(rand(10000, 99999));
|
||||
|
||||
$value = $item['value'];
|
||||
|
||||
if (is_null($value)) break;
|
||||
if (is_null($value) || !$value && !is_array($value)) break;
|
||||
|
||||
$relationType = $seed->getRelationType($link);
|
||||
|
||||
@@ -1280,13 +1322,12 @@ class Base
|
||||
}
|
||||
$this->setDistinct(true, $result);
|
||||
break;
|
||||
|
||||
case 'notLinkedWith':
|
||||
$seed = $this->getSeed();
|
||||
$link = $attribute;
|
||||
if (!$seed->hasRelation($link)) break;
|
||||
|
||||
$value = $item['value'];
|
||||
|
||||
if (is_null($value)) break;
|
||||
|
||||
$relationType = $seed->getRelationType($link);
|
||||
@@ -1312,12 +1353,64 @@ class Base
|
||||
$part[$key . '!='] = $value;
|
||||
}
|
||||
} else if ($relationType == 'hasOne') {
|
||||
$this->addLeftJoin([$link, alias], $result);
|
||||
$this->addLeftJoin([$link, $alias], $result);
|
||||
$part[$alias . '.id!='] = $value;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
$this->setDistinct(true, $result);
|
||||
break;
|
||||
|
||||
case 'arrayAnyOf':
|
||||
case 'arrayNoneOf':
|
||||
case 'arrayIsEmpty':
|
||||
case 'arrayIsNotEmpty':
|
||||
$arrayValueAlias = 'arrayFilter' . strval(rand(10000, 99999));
|
||||
$arrayAttribute = $attribute;
|
||||
$arrayEntityType = $this->getEntityType();
|
||||
$idPart = 'id';
|
||||
|
||||
if (strpos($attribute, '.') > 0) {
|
||||
list($arrayAttributeLink, $arrayAttribute) = explode('.', $attribute);
|
||||
$seed = $this->getSeed();
|
||||
$arrayEntityType = $seed->getRelationParam($arrayAttributeLink, 'entity');
|
||||
$idPart = $arrayAttributeLink . '.id';
|
||||
}
|
||||
|
||||
if ($type === 'arrayAnyOf') {
|
||||
if (is_null($value) || !$value && !is_array($value)) break;
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
$arrayValueAlias . '.entityType' => $arrayEntityType,
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.value'] = $value;
|
||||
} else if ($type === 'arrayNoneOf') {
|
||||
if (is_null($value) || !$value && !is_array($value)) break;
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
$arrayValueAlias . '.entityType' => $arrayEntityType,
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute,
|
||||
$arrayValueAlias . '.value=' => $value
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id'] = null;
|
||||
} else if ($type === 'arrayIsEmpty') {
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
$arrayValueAlias . '.entityType' => $arrayEntityType,
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id'] = null;
|
||||
} else if ($type === 'arrayIsNotEmpty') {
|
||||
$this->addLeftJoin(['ArrayValue', $arrayValueAlias, [
|
||||
$arrayValueAlias . '.entityId:' => $idPart,
|
||||
$arrayValueAlias . '.entityType' => $arrayEntityType,
|
||||
$arrayValueAlias . '.attribute' => $arrayAttribute
|
||||
]], $result);
|
||||
$part[$arrayValueAlias . '.id!='] = null;
|
||||
}
|
||||
|
||||
$this->setDistinct(true, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1535,7 +1628,8 @@ class Base
|
||||
if (!$fullTextSearchMinLength) {
|
||||
$fullTextSearchMinLength = 0;
|
||||
}
|
||||
if (mb_strlen($textFilter) >= $fullTextSearchMinLength) {
|
||||
$textFilterWoWildcards = str_replace('*', '', $textFilter);
|
||||
if (mb_strlen($textFilterWoWildcards) >= $fullTextSearchMinLength) {
|
||||
$useFullTextSearch = true;
|
||||
}
|
||||
}
|
||||
@@ -1560,9 +1654,17 @@ class Base
|
||||
$useFullTextSearch = false;
|
||||
}
|
||||
|
||||
if ($isAuxiliaryUse) {
|
||||
if (mb_strpos($textFilter, '@') !== false) {
|
||||
$useFullTextSearch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($useFullTextSearch) {
|
||||
$textFilter = str_replace(['(', ')'], '', $textFilter);
|
||||
|
||||
if (
|
||||
$isAuxiliaryUse
|
||||
$isAuxiliaryUse && mb_strpos($textFilter, '*') === false
|
||||
||
|
||||
mb_strpos($textFilter, ' ') === false
|
||||
&&
|
||||
@@ -1577,6 +1679,19 @@ class Base
|
||||
$function = 'MATCH_BOOLEAN';
|
||||
}
|
||||
|
||||
$textFilter = str_replace('"*', '"', $textFilter);
|
||||
$textFilter = str_replace('*"', '"', $textFilter);
|
||||
|
||||
while (strpos($textFilter, '**')) {
|
||||
$textFilter = str_replace('**', '*', $textFilter);
|
||||
$textFilter = trim($textFilter);
|
||||
}
|
||||
|
||||
while (mb_substr($textFilter, -2) === ' *') {
|
||||
$textFilter = mb_substr($textFilter, 0, mb_strlen($textFilter) - 2);
|
||||
$textFilter = trim($textFilter);
|
||||
}
|
||||
|
||||
$fullTextSearchColumnSanitizedList = [];
|
||||
$query = $this->getEntityManager()->getQuery();
|
||||
foreach ($fullTextSearchColumnList as $i => $field) {
|
||||
@@ -1617,15 +1732,34 @@ class Base
|
||||
$forceFullTextSearch = true;
|
||||
}
|
||||
|
||||
$textFilterForFullTextSearch = $textFilter;
|
||||
|
||||
$skipWidlcards = false;
|
||||
if (!$useFullTextSearch) {
|
||||
if (mb_strpos($textFilter, '*') !== false) {
|
||||
$skipWidlcards = true;
|
||||
$textFilter = str_replace('*', '%', $textFilter);
|
||||
|
||||
if (mb_strpos($textFilter, '*') !== false) {
|
||||
$skipWidlcards = true;
|
||||
$textFilter = str_replace('*', '%', $textFilter);
|
||||
} else {
|
||||
if (!$useFullTextSearch) {
|
||||
$textFilterForFullTextSearch .= '*';
|
||||
}
|
||||
}
|
||||
|
||||
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilter, !$useFullTextSearch);
|
||||
$textFilterForFullTextSearch = str_replace('%', '*', $textFilterForFullTextSearch);
|
||||
|
||||
$skipFullTextSearch = false;
|
||||
if (!$forceFullTextSearch) {
|
||||
if (mb_strpos($textFilterForFullTextSearch, '*') === 0) {
|
||||
$skipFullTextSearch = true;
|
||||
} else if (mb_strpos($textFilterForFullTextSearch, ' *') !== false) {
|
||||
$skipFullTextSearch = true;
|
||||
}
|
||||
}
|
||||
|
||||
$fullTextSearchData = null;
|
||||
if (!$skipFullTextSearch) {
|
||||
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilterForFullTextSearch, !$useFullTextSearch);
|
||||
}
|
||||
|
||||
$fullTextGroup = [];
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"type": "text"
|
||||
},
|
||||
"website": {
|
||||
"type": "url"
|
||||
"type": "url",
|
||||
"strip": true
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"website": "Webseite"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"dateStart": "Startdatum",
|
||||
"dateEnd": "Enddatum",
|
||||
"duration": "Dauer",
|
||||
"status": "Status",
|
||||
"reminders": "Erinnerungen"
|
||||
},
|
||||
"links": {
|
||||
@@ -20,7 +19,6 @@
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen",
|
||||
"Schedule {entityType}": "{entityTypeTranslated} planen",
|
||||
"Log {entityType}": "Log {entityTypeTranslated}",
|
||||
"Set Held": "Auf durchgeführt setzen",
|
||||
"Set Not Held": "Auf nicht durchgeführt setzen"
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"address": "Adresse"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"links": {
|
||||
"meetings": "Juntas",
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"website": "Sitio Web"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Juntas",
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas"
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"address": "Dirección"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Juntas",
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas"
|
||||
},
|
||||
|
||||
@@ -631,7 +631,13 @@ abstract class Base
|
||||
|
||||
protected function systemRebuild()
|
||||
{
|
||||
return $this->getContainer()->get('dataManager')->rebuild();
|
||||
try {
|
||||
return $this->getContainer()->get('dataManager')->rebuild();
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Database rebuild failure, details: '.$e->getMessage().'.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,6 +180,10 @@ class Auth
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() && $this->getConfig()->get('maintenanceMode')) {
|
||||
throw new \Espo\Core\Exceptions\ServiceUnavailable("Application is in maintenance mode.");
|
||||
}
|
||||
|
||||
if (!$user->isActive()) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login as user '".$user->get('userName')."' which is not active.");
|
||||
$this->logDenied($authLogRecord, 'INACTIVE_USER');
|
||||
|
||||
@@ -25,16 +25,19 @@
|
||||
*
|
||||
* 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\Core\Utils\Database\DBAL\Driver\PDOMySql;
|
||||
|
||||
class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver
|
||||
class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver
|
||||
{
|
||||
|
||||
public function getDatabasePlatform()
|
||||
{
|
||||
return new \Espo\Core\Utils\Database\DBAL\Platforms\MySqlPlatform();
|
||||
}
|
||||
|
||||
|
||||
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
|
||||
{
|
||||
return new \Espo\Core\Utils\Database\DBAL\Schema\MySqlSchemaManager($conn);
|
||||
}
|
||||
}
|
||||
@@ -452,5 +452,17 @@ class MySqlPlatform extends \Doctrine\DBAL\Platforms\MySqlPlatform
|
||||
|
||||
return 'MEDIUMTEXT';
|
||||
}
|
||||
|
||||
public function getColumnDeclarationListSQL(array $fields)
|
||||
{
|
||||
$queryFields = array();
|
||||
|
||||
foreach ($fields as $fieldName => $field) {
|
||||
$quotedFieldName = $this->espoQuote($fieldName);
|
||||
$queryFields[] = $this->getColumnDeclarationSQL($quotedFieldName, $field);
|
||||
}
|
||||
|
||||
return implode(', ', $queryFields);
|
||||
}
|
||||
//end: ESPO
|
||||
}
|
||||
82
application/Espo/Core/Utils/Database/DBAL/Schema/Index.php
Normal file
82
application/Espo/Core/Utils/Database/DBAL/Schema/Index.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Core\Utils\Database\DBAL\Schema;
|
||||
|
||||
use Doctrine\DBAL\Schema\Index as DBALIndex;
|
||||
|
||||
class Index extends \Doctrine\DBAL\Schema\Index
|
||||
{
|
||||
public function addFlag($flag)
|
||||
{
|
||||
$this->_flags[strtolower($flag)] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasFlag($flag)
|
||||
{
|
||||
return isset($this->_flags[strtolower($flag)]);
|
||||
}
|
||||
|
||||
public function removeFlag($flag)
|
||||
{
|
||||
unset($this->_flags[strtolower($flag)]);
|
||||
}
|
||||
|
||||
public function isFullfilledBy(DBALIndex $other)
|
||||
{
|
||||
if (count($other->getColumns()) != count($this->getColumns())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sameColumns = $this->spansColumns($other->getColumns());
|
||||
|
||||
if ($sameColumns) {
|
||||
$flags = $this->getFlags();
|
||||
$otherFlags = $other->getFlags();
|
||||
|
||||
if ( ! $this->isUnique() && !$this->isPrimary() && $flags === $otherFlags) {
|
||||
return true;
|
||||
} else if ($other->isPrimary() != $this->isPrimary()) {
|
||||
return false;
|
||||
} else if ($other->isUnique() != $this->isUnique()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($flags) != count($otherFlags) || array_diff($flags, $otherFlags) !== array_diff($otherFlags, $flags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Core\Utils\Database\DBAL\Schema;
|
||||
|
||||
use Doctrine\DBAL\Events;
|
||||
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
|
||||
|
||||
class MySqlSchemaManager extends \Doctrine\DBAL\Schema\MySqlSchemaManager
|
||||
{
|
||||
public function createSchema()
|
||||
{
|
||||
$sequences = array();
|
||||
if ($this->_platform->supportsSequences()) {
|
||||
$sequences = $this->listSequences();
|
||||
}
|
||||
$tables = $this->listTables();
|
||||
|
||||
return new Schema($tables, $sequences, $this->createSchemaConfig());
|
||||
}
|
||||
|
||||
public function listTables()
|
||||
{
|
||||
$tableNames = $this->listTableNames();
|
||||
|
||||
$tables = array();
|
||||
foreach ($tableNames as $tableName) {
|
||||
$tables[] = $this->listTableDetails($tableName);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
public function listTableDetails($tableName)
|
||||
{
|
||||
$columns = $this->listTableColumns($tableName);
|
||||
$foreignKeys = array();
|
||||
if ($this->_platform->supportsForeignKeyConstraints()) {
|
||||
$foreignKeys = $this->listTableForeignKeys($tableName);
|
||||
}
|
||||
$indexes = $this->listTableIndexes($tableName);
|
||||
|
||||
return new Table($tableName, $columns, $indexes, $foreignKeys, false, array());
|
||||
}
|
||||
|
||||
public function listTableIndexes($table)
|
||||
{
|
||||
$sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
|
||||
|
||||
$tableIndexes = $this->_conn->fetchAll($sql);
|
||||
|
||||
return $this->_getPortableTableIndexesList($tableIndexes, $table);
|
||||
}
|
||||
|
||||
protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
|
||||
{
|
||||
foreach($tableIndexes as $k => $v) {
|
||||
$v = array_change_key_case($v, CASE_LOWER);
|
||||
if($v['key_name'] == 'PRIMARY') {
|
||||
$v['primary'] = true;
|
||||
} else {
|
||||
$v['primary'] = false;
|
||||
}
|
||||
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
|
||||
$v['flags'] = array('FULLTEXT');
|
||||
}
|
||||
$tableIndexes[$k] = $v;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach($tableIndexes as $tableIndex) {
|
||||
|
||||
$indexName = $keyName = $tableIndex['key_name'];
|
||||
if ($tableIndex['primary']) {
|
||||
$keyName = 'primary';
|
||||
}
|
||||
$keyName = strtolower($keyName);
|
||||
|
||||
if (!isset($result[$keyName])) {
|
||||
$result[$keyName] = array(
|
||||
'name' => $indexName,
|
||||
'columns' => array($tableIndex['column_name']),
|
||||
'unique' => $tableIndex['non_unique'] ? false : true,
|
||||
'primary' => $tableIndex['primary'],
|
||||
'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(),
|
||||
);
|
||||
} else {
|
||||
$result[$keyName]['columns'][] = $tableIndex['column_name'];
|
||||
}
|
||||
}
|
||||
|
||||
$eventManager = $this->_platform->getEventManager();
|
||||
|
||||
$indexes = array();
|
||||
foreach($result as $indexKey => $data) {
|
||||
$index = null;
|
||||
$defaultPrevented = false;
|
||||
|
||||
if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
|
||||
$eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
|
||||
$eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
|
||||
|
||||
$defaultPrevented = $eventArgs->isDefaultPrevented();
|
||||
$index = $eventArgs->getIndex();
|
||||
}
|
||||
|
||||
if ( ! $defaultPrevented) {
|
||||
$index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags']);
|
||||
}
|
||||
|
||||
if ($index) {
|
||||
$indexes[$indexKey] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
return $indexes;
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\DBAL\Schema;
|
||||
|
||||
class Schema extends \Doctrine\DBAL\Schema\Schema
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a new table.
|
||||
*
|
||||
|
||||
@@ -28,11 +28,12 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\DBAL\Schema;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
|
||||
class Table extends \Doctrine\DBAL\Schema\Table
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $columnName
|
||||
* @param string $typeName
|
||||
@@ -49,4 +50,35 @@ class Table extends \Doctrine\DBAL\Schema\Table
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function addIndex(array $columnNames, $indexName = null, array $flags = array())
|
||||
{
|
||||
if($indexName == null) {
|
||||
$indexName = $this->_generateIdentifierName(
|
||||
array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->_createIndex($columnNames, $indexName, false, false, $flags);
|
||||
}
|
||||
|
||||
private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array())
|
||||
{
|
||||
if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) {
|
||||
throw SchemaException::indexNameInvalid($indexName);
|
||||
}
|
||||
|
||||
foreach ($columnNames as $columnName => $indexColOptions) {
|
||||
if (is_numeric($columnName) && is_string($indexColOptions)) {
|
||||
$columnName = $indexColOptions;
|
||||
}
|
||||
|
||||
if ( ! $this->hasColumn($columnName)) {
|
||||
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags));
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ class Converter
|
||||
'select' => 'select',
|
||||
'orderBy' => 'orderBy',
|
||||
'where' => 'where',
|
||||
'storeArrayValues' => 'storeArrayValues'
|
||||
);
|
||||
|
||||
protected $idParams = array(
|
||||
|
||||
@@ -39,28 +39,28 @@ class Currency extends Base
|
||||
|
||||
$currencyColumnName = Util::toUnderScore($fieldName);
|
||||
|
||||
$alias = Util::toUnderScore($fieldName) . "_currency_alias";
|
||||
$alias = $fieldName . 'CurrencyRate';
|
||||
|
||||
$d = array(
|
||||
$entityName => array(
|
||||
'fields' => array(
|
||||
$fieldName => array(
|
||||
"type" => "float",
|
||||
"orderBy" => $converedFieldName . " {direction}"
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
$d = [
|
||||
$entityName => [
|
||||
'fields' => [
|
||||
$fieldName => [
|
||||
'type' => 'float',
|
||||
'orderBy' => $converedFieldName . ' {direction}'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$params = $this->getFieldParams($fieldName);
|
||||
if (!empty($params['notStorable'])) {
|
||||
$d[$entityName]['fields'][$fieldName]['notStorable'] = true;
|
||||
} else {
|
||||
$d[$entityName]['fields'][$fieldName . 'Converted'] = array(
|
||||
$d[$entityName]['fields'][$fieldName . 'Converted'] = [
|
||||
'type' => 'float',
|
||||
'select' => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate" ,
|
||||
'where' =>
|
||||
array (
|
||||
[
|
||||
"=" => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate = {value}",
|
||||
">" => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate > {value}",
|
||||
"<" => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate < {value}",
|
||||
@@ -68,11 +68,11 @@ class Currency extends Base
|
||||
"<=" => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate <= {value}",
|
||||
"<>" => Util::toUnderScore($entityName) . "." . $currencyColumnName . " * {$alias}.rate <> {value}",
|
||||
"IS NULL" => Util::toUnderScore($entityName) . "." . $currencyColumnName . ' IS NULL',
|
||||
"IS NOT NULL" => Util::toUnderScore($entityName) . "." . $currencyColumnName . ' IS NOT NULL',
|
||||
),
|
||||
"IS NOT NULL" => Util::toUnderScore($entityName) . "." . $currencyColumnName . ' IS NOT NULL'
|
||||
],
|
||||
'notStorable' => true,
|
||||
'orderBy' => $converedFieldName . " {direction}"
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
return $d;
|
||||
|
||||
@@ -88,7 +88,16 @@ class Email extends Base
|
||||
$fieldName .'IsOptedOut' => array(
|
||||
'type' => 'bool',
|
||||
'notStorable' => true,
|
||||
'select' => 'emailAddresses.opt_out'
|
||||
'select' => 'emailAddresses.opt_out',
|
||||
'where' => [
|
||||
'= TRUE' => [
|
||||
'sql' => 'emailAddresses.opt_out = true AND emailAddresses.opt_out IS NOT NULL'
|
||||
],
|
||||
'= FALSE' => [
|
||||
'sql' => 'emailAddresses.opt_out = false OR emailAddresses.opt_out IS NULL'
|
||||
]
|
||||
],
|
||||
'orderBy' => 'emailAddresses.opt_out {direction}'
|
||||
)
|
||||
),
|
||||
'relations' => array(
|
||||
|
||||
@@ -33,28 +33,29 @@ class LinkMultiple extends Base
|
||||
{
|
||||
protected function load($fieldName, $entityName)
|
||||
{
|
||||
$data = array(
|
||||
$entityName => array (
|
||||
'fields' => array(
|
||||
$fieldName.'Ids' => array(
|
||||
$data = [
|
||||
$entityName => [
|
||||
'fields' => [
|
||||
$fieldName.'Ids' => [
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleIdList' => true,
|
||||
'relation' => $fieldName
|
||||
),
|
||||
$fieldName.'Names' => array(
|
||||
'relation' => $fieldName,
|
||||
'isUnordered' => true
|
||||
],
|
||||
$fieldName.'Names' => [
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleNameMap' => true
|
||||
)
|
||||
)
|
||||
),
|
||||
'unset' => array(
|
||||
$entityName => array(
|
||||
'fields.'.$fieldName
|
||||
)
|
||||
)
|
||||
);
|
||||
]
|
||||
]
|
||||
],
|
||||
'unset' => [
|
||||
$entityName => [
|
||||
'fields.' . $fieldName
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$fieldParams = $this->getFieldParams();
|
||||
|
||||
@@ -67,10 +68,10 @@ class LinkMultiple extends Base
|
||||
|
||||
$columns = $this->getMetadata()->get("entityDefs.{$entityName}.fields.{$fieldName}.columns");
|
||||
if (!empty($columns)) {
|
||||
$data[$entityName]['fields'][$fieldName . 'Columns'] = array(
|
||||
$data[$entityName]['fields'][$fieldName . 'Columns'] = [
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
@@ -43,7 +43,8 @@ class LinkParent extends Base
|
||||
$fieldName.'Type' => array(
|
||||
'type' => 'foreignType',
|
||||
'notNull' => false,
|
||||
'index' => $fieldName
|
||||
'index' => $fieldName,
|
||||
'len' => 100
|
||||
),
|
||||
$fieldName.'Name' => array(
|
||||
'type' => 'varchar',
|
||||
|
||||
@@ -85,6 +85,50 @@ class Phone extends Base
|
||||
'type' => 'text',
|
||||
'notStorable' => true
|
||||
),
|
||||
$fieldName . 'Numeric' => [
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true,
|
||||
'where' => [
|
||||
'LIKE' => \Espo\Core\Utils\Util::toUnderScore($entityName) . ".id IN (
|
||||
SELECT entity_id
|
||||
FROM entity_phone_number
|
||||
JOIN phone_number ON phone_number.id = entity_phone_number.phone_number_id
|
||||
WHERE
|
||||
entity_phone_number.deleted = 0 AND entity_phone_number.entity_type = '{$entityName}' AND
|
||||
phone_number.deleted = 0 AND phone_number.numeric LIKE {value}
|
||||
)",
|
||||
'=' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric = {value}',
|
||||
'distinct' => true
|
||||
],
|
||||
'<>' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric <> {value}',
|
||||
'distinct' => true
|
||||
],
|
||||
'IN' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric IN {value}',
|
||||
'distinct' => true
|
||||
],
|
||||
'NOT IN' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric NOT IN {value}',
|
||||
'distinct' => true
|
||||
],
|
||||
'IS NULL' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric IS NULL',
|
||||
'distinct' => true
|
||||
],
|
||||
'IS NOT NULL' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersNumericMultiple']],
|
||||
'sql' => 'phoneNumbersNumericMultiple.numeric IS NOT NULL',
|
||||
'distinct' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
),
|
||||
'relations' => array(
|
||||
'phoneNumbers' => array(
|
||||
|
||||
@@ -44,7 +44,7 @@ class HasChildren extends Base
|
||||
'notStorable' => true,
|
||||
),
|
||||
$linkName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -582,6 +582,9 @@ class EntityManager
|
||||
} else {
|
||||
$relationName = lcfirst($entity) . $entityForeign;
|
||||
}
|
||||
if (strlen($relationName) > 100) {
|
||||
throw new Error('Relation name should not be longer than 100.');
|
||||
}
|
||||
if ($this->getMetadata()->get(['scopes', ucfirst($relationName)])) {
|
||||
throw new Conflict("Entity with the same name '{$relationName}' exists.");
|
||||
}
|
||||
@@ -594,8 +597,8 @@ class EntityManager
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($link) > 255 || strlen($linkForeign) > 255) {
|
||||
throw new Error('Link name should not be longer than 255.');
|
||||
if (strlen($link) > 100 || strlen($linkForeign) > 100) {
|
||||
throw new Error('Link name should not be longer than 100.');
|
||||
}
|
||||
|
||||
if (is_numeric($link[0]) || is_numeric($linkForeign[0])) {
|
||||
@@ -662,7 +665,6 @@ class EntityManager
|
||||
$link => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleField,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleField,
|
||||
"noLoad" => !$linkMultipleField,
|
||||
"importDisabled" => !$linkMultipleField,
|
||||
@@ -727,7 +729,6 @@ class EntityManager
|
||||
$linkForeign => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
|
||||
"noLoad" => !$linkMultipleFieldForeign,
|
||||
"importDisabled" => !$linkMultipleFieldForeign,
|
||||
@@ -751,7 +752,6 @@ class EntityManager
|
||||
$link => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleField,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleField,
|
||||
"importDisabled" => !$linkMultipleField,
|
||||
"noLoad" => !$linkMultipleField,
|
||||
@@ -774,7 +774,6 @@ class EntityManager
|
||||
$linkForeign => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
|
||||
"importDisabled" => !$linkMultipleFieldForeign,
|
||||
"noLoad" => !$linkMultipleFieldForeign,
|
||||
@@ -848,7 +847,6 @@ class EntityManager
|
||||
$link => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleField,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleField,
|
||||
"noLoad" => !$linkMultipleField,
|
||||
"importDisabled" => !$linkMultipleField,
|
||||
@@ -873,7 +871,6 @@ class EntityManager
|
||||
$linkForeign => array(
|
||||
"type" => "linkMultiple",
|
||||
"layoutDetailDisabled" => !$linkMultipleFieldForeign,
|
||||
"layoutListDisabled" => true,
|
||||
"layoutMassUpdateDisabled" => !$linkMultipleFieldForeign,
|
||||
"noLoad" => !$linkMultipleFieldForeign,
|
||||
"importDisabled" => !$linkMultipleFieldForeign,
|
||||
|
||||
@@ -98,8 +98,8 @@ class FieldManager
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($name) > 255) {
|
||||
throw new Error('Field name should not be longer than 255.');
|
||||
if (strlen($name) > 100) {
|
||||
throw new Error('Field name should not be longer than 100.');
|
||||
}
|
||||
|
||||
if (is_numeric($name[0])) {
|
||||
|
||||
@@ -589,7 +589,7 @@ class Manager
|
||||
}
|
||||
}
|
||||
|
||||
if ($removeWithDir) {
|
||||
if ($removeWithDir && $this->isDirEmpty($dirPath)) {
|
||||
$result &= $this->rmdir($dirPath);
|
||||
}
|
||||
|
||||
@@ -908,4 +908,3 @@ class Manager
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,11 @@ class Util
|
||||
return static::fromCamelCase($name, '_');
|
||||
}
|
||||
|
||||
public static function camelCaseToUnderscore($value)
|
||||
{
|
||||
return static::toUnderScore($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge arrays recursively (default PHP function is not suitable)
|
||||
*
|
||||
|
||||
@@ -174,6 +174,8 @@ return array (
|
||||
'massPrintPdfMaxCount' => 50,
|
||||
'emailKeepParentTeamsEntityList' => ['Case'],
|
||||
'recordListMaxSizeLimit' => 200,
|
||||
'noteDeleteThresholdPeriod' => '1 month',
|
||||
'noteEditThresholdPeriod' => '7 days',
|
||||
'isInstalled' => false
|
||||
);
|
||||
|
||||
|
||||
37
application/Espo/Entities/ArrayValue.php
Normal file
37
application/Espo/Entities/ArrayValue.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Entities;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class ArrayValue extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
35
application/Espo/Entities/Currency.php
Normal file
35
application/Espo/Entities/Currency.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Entities;
|
||||
|
||||
class Currency extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
35
application/Espo/Entities/LeadCapture.php
Normal file
35
application/Espo/Entities/LeadCapture.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Entities;
|
||||
|
||||
class LeadCapture extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
35
application/Espo/Entities/LeadCaptureLogRecord.php
Normal file
35
application/Espo/Entities/LeadCaptureLogRecord.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Entities;
|
||||
|
||||
class LeadCaptureLogRecord extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
67
application/Espo/EntryPoints/ConfirmOptIn.php
Normal file
67
application/Espo/EntryPoints/ConfirmOptIn.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\EntryPoints;
|
||||
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class ConfirmOptIn extends \Espo\Core\EntryPoints\Base
|
||||
{
|
||||
public static $authRequired = false;
|
||||
|
||||
public function run()
|
||||
{
|
||||
if (empty($_GET['id'])) throw new BadRequest();
|
||||
|
||||
$id = $_GET['id'];
|
||||
|
||||
$data = $this->getServiceFactory()->create('LeadCapture')->confirmOptIn($id);
|
||||
|
||||
if ($data->status === 'success') {
|
||||
$action = 'optInConfirmationSuccess';
|
||||
} else if ($data->status === 'expired') {
|
||||
$action = 'optInConfirmationExpired';
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$runScript = "
|
||||
Espo.require('controllers/lead-capture-opt-in-confirmation', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('".$action."', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ class Image extends \Espo\Core\EntryPoints\Base
|
||||
}
|
||||
|
||||
if (!empty($size)) {
|
||||
$fileName = $sourceId . '_' . $size . '.jpg';
|
||||
$fileName = $size . '-' . $attachment->get('name');
|
||||
} else {
|
||||
$fileName = $attachment->get('name');
|
||||
}
|
||||
@@ -197,9 +197,10 @@ class Image extends \Espo\Core\EntryPoints\Base
|
||||
break;
|
||||
}
|
||||
|
||||
$targetImage = imagerotate($targetImage, array_values([0, 0, 0, 180, 0, 0, -90, 0, 90])[@exif_read_data($filePath)['Orientation'] ?: 0], 0);
|
||||
if (function_exists('exif_read_data')) {
|
||||
$targetImage = imagerotate($targetImage, array_values([0, 0, 0, 180, 0, 0, -90, 0, 90])[@exif_read_data($filePath)['Orientation'] ?: 0], 0);
|
||||
}
|
||||
|
||||
return $targetImage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,18 +58,32 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
|
||||
|
||||
foreach ($userIdList as $userId) {
|
||||
if (in_array($userId, $fetchedAssignedUserIdList)) continue;
|
||||
if ($this->getUser()->id === $userId) continue;
|
||||
if (!$this->isNotSelfAssignment($entity, $userId)) continue;
|
||||
$this->createJob($entity, $userId);
|
||||
}
|
||||
} else {
|
||||
$userId = $entity->get('assignedUserId');
|
||||
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isAttributeChanged('assignedUserId')) {
|
||||
if (!empty($userId) && $entity->isAttributeChanged('assignedUserId') && $this->isNotSelfAssignment($entity, $userId)) {
|
||||
$this->createJob($entity, $userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function isNotSelfAssignment(Entity $entity, $assignedUserId)
|
||||
{
|
||||
if ($entity->hasAttribute('createdById') && $entity->hasAttribute('modifiedById')) {
|
||||
if ($entity->isNew()) {
|
||||
$isNotSelfAssignment = $assignedUserId !== $entity->get('createdById');
|
||||
} else {
|
||||
$isNotSelfAssignment = $assignedUserId !== $entity->get('modifiedById');
|
||||
}
|
||||
} else {
|
||||
$isNotSelfAssignment = $assignedUserId !== $this->getUser()->id;
|
||||
}
|
||||
return $isNotSelfAssignment;
|
||||
}
|
||||
|
||||
protected function createJob(Entity $entity, $userId)
|
||||
{
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
|
||||
@@ -178,7 +178,7 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
return $userIdList;
|
||||
}
|
||||
|
||||
public function afterSave(Entity $entity, array $options = array())
|
||||
public function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
|
||||
124
application/Espo/Hooks/Common/StreamNotesAcl.php
Normal file
124
application/Espo/Hooks/Common/StreamNotesAcl.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 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\Hooks\Common;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class StreamNotesAcl extends \Espo\Core\Hooks\Base
|
||||
{
|
||||
protected $streamService = null;
|
||||
|
||||
public static $order = 10;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('serviceFactory');
|
||||
$this->addDependency('aclManager');
|
||||
}
|
||||
|
||||
protected function getServiceFactory()
|
||||
{
|
||||
return $this->getInjection('serviceFactory');
|
||||
}
|
||||
|
||||
protected function getAclManager()
|
||||
{
|
||||
return $this->getInjection('aclManager');
|
||||
}
|
||||
|
||||
public function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if (!empty($options['noStream'])) return;
|
||||
if (!empty($options['silent'])) return;
|
||||
|
||||
if ($entity->isNew()) return;
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
if (in_array($entityType, ['Note', 'User', 'Team', 'Role', 'Portal', 'PortalRole'])) return;
|
||||
|
||||
if (!$this->getMetadata()->get(['scopes', $entityType, 'acl'])) return;
|
||||
if (!$this->getMetadata()->get(['scopes', $entityType, 'object'])) return;
|
||||
|
||||
$ownerUserIdAttribute = $this->getAclManager()->getImplementation($entityType)->getOwnerUserIdAttribute($entity);
|
||||
|
||||
$usersAttributeIsChanged = false;
|
||||
$teamsAttributeIsChanged = false;
|
||||
|
||||
if ($ownerUserIdAttribute) {
|
||||
if ($entity->isAttributeChanged($ownerUserIdAttribute)) {
|
||||
$usersAttributeIsChanged = true;
|
||||
if ($entity->getAttributeParam($ownerUserIdAttribute, 'isLinkMultipleIdList')) {
|
||||
$userIdList = $entity->get($ownerUserIdAttribute);
|
||||
} else {
|
||||
$userId = $entity->get($ownerUserIdAttribute);
|
||||
if ($userId) {
|
||||
$userIdList = [$userId];
|
||||
} else {
|
||||
$userIdList = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('teams') && $entity->isAttributeChanged('teamsIds')) {
|
||||
$teamsAttributeIsChanged = true;
|
||||
$teamIdList = $entity->get('teamsIds');
|
||||
}
|
||||
|
||||
if ($usersAttributeIsChanged || $teamsAttributeIsChanged) {
|
||||
$noteList = $this->getEntityManager()->getRepository('Note')->where([
|
||||
'OR' => [
|
||||
[
|
||||
'relatedId' => $entity->id,
|
||||
'relatedType' => $entityType
|
||||
],
|
||||
[
|
||||
'parentId' => $entity->id,
|
||||
'parentType' => $entityType,
|
||||
'superParentId!=' => null,
|
||||
'relatedId' => null
|
||||
]
|
||||
]
|
||||
])->select(['id'])->find();
|
||||
|
||||
foreach ($noteList as $note) {
|
||||
if ($teamsAttributeIsChanged) {
|
||||
$note->set('teamsIds', $teamIdList);
|
||||
}
|
||||
if ($usersAttributeIsChanged) {
|
||||
$note->set('usersIds', $userIdList);
|
||||
}
|
||||
$this->getEntityManager()->saveEntity($note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('serviceFactory');
|
||||
$this->addDependency('container');
|
||||
}
|
||||
|
||||
protected function getServiceFactory()
|
||||
@@ -47,6 +48,16 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
return $this->getInjection('serviceFactory');
|
||||
}
|
||||
|
||||
protected function getInternalAclManager()
|
||||
{
|
||||
return $this->getInjection('container')->get('internalAclManager');
|
||||
}
|
||||
|
||||
protected function getPortalAclManager()
|
||||
{
|
||||
return $this->getInjection('container')->get('portalAclManager');
|
||||
}
|
||||
|
||||
protected function getMentionedUserIdList($entity)
|
||||
{
|
||||
$mentionedUserList = array();
|
||||
@@ -60,7 +71,7 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
return $mentionedUserList;
|
||||
}
|
||||
|
||||
protected function getSubscriberIdList($parentType, $parentId, $isInternal = false)
|
||||
protected function getSubscriberList($parentType, $parentId, $isInternal = false)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
@@ -80,15 +91,14 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
user.is_portal_user = 0
|
||||
";
|
||||
}
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
$userIdList = [];
|
||||
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if ($this->getUser()->id != $row['userId']) {
|
||||
$userIdList[] = $row['userId'];
|
||||
}
|
||||
}
|
||||
return $userIdList;
|
||||
|
||||
$userList = $this->getEntityManager()->getRepository('User')->where([
|
||||
'isActive' => true
|
||||
])->select(['id', 'isPortalUser', 'isAdmin'])->find([
|
||||
'customWhere' => "AND user.id IN (".$sql.")"
|
||||
]);
|
||||
|
||||
return $userList;
|
||||
}
|
||||
|
||||
public function afterSave(Entity $entity)
|
||||
@@ -99,13 +109,77 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
$superParentType = $entity->get('superParentType');
|
||||
$superParentId = $entity->get('superParentId');
|
||||
|
||||
$userIdList = [];
|
||||
$notifyUserIdList = [];
|
||||
|
||||
if ($parentType && $parentId) {
|
||||
$userIdList = array_merge($userIdList, $this->getSubscriberIdList($parentType, $parentId, $entity->get('isInternal')));
|
||||
if ($superParentType && $superParentId) {
|
||||
$userIdList = array_merge($userIdList, $this->getSubscriberIdList($superParentType, $superParentId, $entity->get('isInternal')));
|
||||
$userList = $this->getSubscriberList($parentType, $parentId, $entity->get('isInternal'));
|
||||
$userIdMetList = [];
|
||||
foreach ($userList as $user) {
|
||||
$userIdMetList[] = $user->id;
|
||||
}
|
||||
if ($superParentType && $superParentId) {
|
||||
$additionalUserList = $this->getSubscriberList($superParentType, $superParentId, $entity->get('isInternal'));
|
||||
foreach ($additionalUserList as $user) {
|
||||
if ($user->isPortal()) continue;
|
||||
if (in_array($user->id, $userIdMetList)) continue;
|
||||
$userIdMetList[] = $user->id;
|
||||
$userList[] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->get('relatedType')) {
|
||||
$targetType = $entity->get('relatedType');
|
||||
} else {
|
||||
$targetType = $parentType;
|
||||
}
|
||||
|
||||
$teamIdList = $entity->getLinkMultipleIdList('teams');
|
||||
$userIdList = $entity->getLinkMultipleIdList('users');
|
||||
|
||||
foreach ($userList as $user) {
|
||||
if ($user->isAdmin()) {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
if ($entity->get('relatedType')) {
|
||||
continue;
|
||||
} else {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$level = $this->getInternalAclManager()->getLevel($user, $targetType, 'read');
|
||||
|
||||
if ($level === 'all') {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
continue;
|
||||
} else if ($level === 'team') {
|
||||
if (in_array($user->id, $userIdList)) {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($teamIdList)) {
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
foreach ($teamIdList as $teamId) {
|
||||
if (in_array($teamId, $userTeamIdList)) {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if ($level === 'own') {
|
||||
if (in_array($user->id, $userIdList)) {
|
||||
$notifyUserIdList[] = $user->id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$targetType = $entity->get('targetType');
|
||||
if ($targetType === 'users') {
|
||||
@@ -113,8 +187,8 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
if (is_array($targetUserIdList)) {
|
||||
foreach ($targetUserIdList as $userId) {
|
||||
if ($userId === $this->getUser()->id) continue;
|
||||
if (in_array($userId, $userIdList)) continue;
|
||||
$userIdList[] = $userId;
|
||||
if (in_array($userId, $notifyUserIdList)) continue;
|
||||
$notifyUserIdList[] = $userId;
|
||||
}
|
||||
}
|
||||
} else if ($targetType === 'teams') {
|
||||
@@ -130,8 +204,8 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
));
|
||||
foreach ($targetUserList as $user) {
|
||||
if ($user->id === $this->getUser()->id) continue;
|
||||
if (in_array($user->id, $userIdList)) continue;
|
||||
$userIdList[] = $user->id;
|
||||
if (in_array($user->id, $notifyUserIdList)) continue;
|
||||
$notifyUserIdList[] = $user->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,8 +222,8 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
));
|
||||
foreach ($targetUserList as $user) {
|
||||
if ($user->id === $this->getUser()->id) continue;
|
||||
if (in_array($user->id, $userIdList)) continue;
|
||||
$userIdList[] = $user->id;
|
||||
if (in_array($user->id, $notifyUserIdList)) continue;
|
||||
$notifyUserIdList[] = $user->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,22 +236,22 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
));
|
||||
foreach ($targetUserList as $user) {
|
||||
if ($user->id === $this->getUser()->id) continue;
|
||||
$userIdList[] = $user->id;
|
||||
$notifyUserIdList[] = $user->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$userIdList = array_unique($userIdList);
|
||||
$notifyUserIdList = array_unique($notifyUserIdList);
|
||||
|
||||
foreach ($userIdList as $i => $userId) {
|
||||
foreach ($notifyUserIdList as $i => $userId) {
|
||||
if ($entity->isUserIdNotified($userId)) {
|
||||
unset($userIdList[$i]);
|
||||
unset($notifyUserIdList[$i]);
|
||||
}
|
||||
}
|
||||
$userIdList = array_values($userIdList);
|
||||
$notifyUserIdList = array_values($notifyUserIdList);
|
||||
|
||||
if (!empty($userIdList)) {
|
||||
$this->getNotificationService()->notifyAboutNote($userIdList, $entity);
|
||||
if (!empty($notifyUserIdList)) {
|
||||
$this->getNotificationService()->notifyAboutNote($notifyUserIdList, $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,4 +264,3 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
return $this->notificationService;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
|
||||
protected $cleanupRemovedNotesPeriod = '2 months';
|
||||
|
||||
protected $cleanupAttachmentsPeriod = '1 month';
|
||||
protected $cleanupAttachmentsPeriod = '15 days';
|
||||
|
||||
protected $cleanupAttachmentsFromPeriod = '3 months';
|
||||
|
||||
@@ -53,6 +53,8 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
|
||||
protected $cleanupBackupPeriod = '2 month';
|
||||
|
||||
protected $cleanupDeletedRecordsPeriod = '3 months';
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->cleanupJobs();
|
||||
@@ -66,6 +68,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$this->cleanupAuthLog();
|
||||
$this->cleanupUpgradeBackups();
|
||||
$this->cleanupUniqueIds();
|
||||
$this->cleanupDeletedRecords();
|
||||
}
|
||||
|
||||
protected function cleanupJobs()
|
||||
@@ -195,7 +198,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
)
|
||||
),
|
||||
'createdAt<' => $datetime->format('Y-m-d H:i:s')
|
||||
))->limit(0, 1000)->find();
|
||||
))->limit(0, 5000)->find();
|
||||
|
||||
foreach ($collection as $e) {
|
||||
$this->getEntityManager()->removeEntity($e);
|
||||
@@ -220,7 +223,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
),
|
||||
'createdAt<' => $datetime->format('Y-m-d H:i:s'),
|
||||
'createdAt>' => '2017-05-10 00:00:00'
|
||||
))->limit(0, 1000)->find();
|
||||
))->limit(0, 5000)->find();
|
||||
|
||||
foreach ($collection as $e) {
|
||||
$this->getEntityManager()->removeEntity($e);
|
||||
@@ -252,25 +255,30 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
}
|
||||
if (!$hasAttachmentField) continue;
|
||||
|
||||
$deletedEntityList = $this->getEntityManager()->getRepository($scope)->where([
|
||||
if (!$this->getEntityManager()->hasRepository($scope)) continue;
|
||||
$repository = $this->getEntityManager()->getRepository($scope);
|
||||
if (!method_exists($repository, 'find')) continue;
|
||||
if (!method_exists($repository, 'where')) continue;
|
||||
|
||||
$deletedEntityList = $repository->where([
|
||||
'deleted' => 1,
|
||||
'modifiedAt<' => $datetime->format('Y-m-d H:i:s'),
|
||||
'modifiedAt>' => $datetimeFrom->format('Y-m-d H:i:s'),
|
||||
|
||||
])->find(['withDeleted' => true]);
|
||||
foreach ($deletedEntityList as $deletedEntity) {
|
||||
$attachmentToRemoveList = $this->getEntityManager()->getRepository('Attachment')->where(array(
|
||||
'OR' => array(
|
||||
array(
|
||||
$attachmentToRemoveList = $this->getEntityManager()->getRepository('Attachment')->where([
|
||||
'OR' => [
|
||||
[
|
||||
'relatedType' => $scope,
|
||||
'relatedId' => $deletedEntity->id
|
||||
),
|
||||
array(
|
||||
],
|
||||
[
|
||||
'parentType' => $scope,
|
||||
'parentId' => $deletedEntity->id
|
||||
)
|
||||
)
|
||||
))->find();
|
||||
]
|
||||
]
|
||||
])->find();
|
||||
|
||||
foreach ($attachmentToRemoveList as $attachmentToRemove) {
|
||||
$this->getEntityManager()->removeEntity($attachmentToRemove);
|
||||
@@ -369,4 +377,34 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanupDeletedRecords()
|
||||
{
|
||||
if (!$this->getConfig()->get('cleanupDeletedRecords')) return;
|
||||
$datetime = new \DateTime('-' . $this->cleanupDeletedRecordsPeriod);
|
||||
|
||||
$scopeList = array_keys($this->getMetadata()->get(['scopes']));
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->getMetadata()->get(['scopes', $scope, 'entity'])) continue;
|
||||
if ($scope === 'Attachment') continue;
|
||||
if (!$this->getMetadata()->get(['entityDefs', $scope, 'fields', 'modifiedAt'])) continue;
|
||||
|
||||
if (!$this->getEntityManager()->hasRepository($scope)) continue;
|
||||
$repository = $this->getEntityManager()->getRepository($scope);
|
||||
if (!$repository) continue;
|
||||
if (!method_exists($repository, 'find')) continue;
|
||||
if (!method_exists($repository, 'where')) continue;
|
||||
if (!method_exists($repository, 'select')) continue;
|
||||
if (!method_exists($repository, 'deleteFromDb')) continue;
|
||||
|
||||
$deletedEntityList = $repository->select(['id', 'deleted'])->where([
|
||||
'deleted' => 1,
|
||||
'modifiedAt<' => $datetime->format('Y-m-d H:i:s')
|
||||
])->find(['withDeleted' => true]);
|
||||
foreach ($deletedEntityList as $e) {
|
||||
if (!$e->get('deleted')) continue;
|
||||
$repository->deleteFromDb($e->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
*
|
||||
* 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\Controllers;
|
||||
|
||||
@@ -33,4 +33,11 @@ class CaseObj extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
protected $name = 'Case';
|
||||
|
||||
public function getActionEmailAddressList($params, $data, $request)
|
||||
{
|
||||
if (!$request->get('id')) throw new BadRequest();
|
||||
if (!$this->getAcl()->checkScope($this->name, 'read')) throw new Forbidden();
|
||||
|
||||
return $this->getRecordService()->getEmailAddressList($request->get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +113,12 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
|
||||
return $this->getRecordService()->massConvertCurrency($data->field, $data->targetCurrency, $params, $data->baseCurrency, $data->currencyRates);
|
||||
}
|
||||
|
||||
public function getActionEmailAddressList($params, $data, $request)
|
||||
{
|
||||
if (!$request->get('id')) throw new BadRequest();
|
||||
if (!$this->getAcl()->checkScope($this->name, 'read')) throw new Forbidden();
|
||||
|
||||
return $this->getRecordService()->getEmailAddressList($request->get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"emailAddress": "E-Mail",
|
||||
"website": "Webseite",
|
||||
"phoneNumber": "Telefon",
|
||||
@@ -14,7 +13,8 @@
|
||||
"campaign": "Kampagne",
|
||||
"targetLists": "Kontaktlisten",
|
||||
"targetList": "Kontaktliste",
|
||||
"originalLead": "Ursprünglicher Interessent"
|
||||
"originalLead": "Ursprünglicher Interessent",
|
||||
"contactIsInactive": "Inaktiv"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Kontakte",
|
||||
@@ -34,65 +34,63 @@
|
||||
"options": {
|
||||
"type": {
|
||||
"Customer": "Kunde",
|
||||
"Investor": "Investor",
|
||||
"Partner": "Partner",
|
||||
"Reseller": "Wiederverkäufer"
|
||||
},
|
||||
"industry": {
|
||||
"Aerospace": "Luft- und Raumfahrt",
|
||||
"Agriculture": "Landwirtschaft",
|
||||
"Advertising": "Werbewirtschaft",
|
||||
"Apparel & Accessories": "Bekleidungsindustrie",
|
||||
"Architecture": "Architekten",
|
||||
"Automotive": "Automobilindustrie",
|
||||
"Banking": "Bankwesen",
|
||||
"Biotechnology": "Biotechnologie",
|
||||
"Building Materials & Equipment": "Baumaterial & -ausstattung",
|
||||
"Chemical": "Chemieindustrie",
|
||||
"Construction": "Konstruktion",
|
||||
"Computer": "Informationstechnologie",
|
||||
"Defense": "Verteidigung",
|
||||
"Creative": "Kreative",
|
||||
"Culture": "Kultur",
|
||||
"Education": "Bildungswesen",
|
||||
"Electronics": "Elektronik",
|
||||
"Electric Power": "Elektroenergie",
|
||||
"Energy": "Energieerzeuger",
|
||||
"Entertainment & Leisure": "Freizeit- und Unterhaltungsindustrie",
|
||||
"Finance": "Finanzsektor",
|
||||
"Food & Beverage": "Speisen und Getränke",
|
||||
"Grocery": "Einzelhandel",
|
||||
"Hospitality": "Gastronomie",
|
||||
"Healthcare": "Gesundheitswesen",
|
||||
"Insurance": "Versicherung",
|
||||
"Legal": "Rechtswesen",
|
||||
"Manufacturing": "Produktion",
|
||||
"Mass Media": "Massenmedien",
|
||||
"Mining": "Bergbau",
|
||||
"Music": "Musik",
|
||||
"Marketing": "Marketing",
|
||||
"Publishing": "Medien",
|
||||
"Petroleum": "Öl Industrie",
|
||||
"Real Estate": "Immobilien",
|
||||
"Retail": "Verkauf",
|
||||
"Shipping": "Versand und Transport",
|
||||
"Service": "Service",
|
||||
"Support": "Service",
|
||||
"Sports": "Sport",
|
||||
"Software": "Software",
|
||||
"Technology": "Technologie",
|
||||
"Telecommunications": "Telekommunikation",
|
||||
"Television": "Fernsehen",
|
||||
"Testing, Inspection & Certification": "Test, Inspektion & Zertifizierung",
|
||||
"Transportation": "Transportwesen",
|
||||
"Venture Capital": "Risikokapital",
|
||||
"Aerospace": "Luft- und Raumfahrt",
|
||||
"Architecture": "Architekten",
|
||||
"Construction": "Konstruktion",
|
||||
"Defense": "Verteidigung",
|
||||
"Creative": "Kreative",
|
||||
"Culture": "Kultur",
|
||||
"Consulting": "Beratung",
|
||||
"Electric Power": "Elektroenergie",
|
||||
"Hospitality": "Gastronomie",
|
||||
"Mass Media": "Massenmedien",
|
||||
"Mining": "Bergbau",
|
||||
"Music": "Musik",
|
||||
"Petroleum": "Öl Industrie",
|
||||
"Retail": "Verkauf",
|
||||
"Shipping": "Versand und Transport",
|
||||
"Support": "Service",
|
||||
"Testing, Inspection & Certification": "Test, Inspektion & Zertifizierung",
|
||||
"Wholesale": "Großhandel",
|
||||
"Water": "Wasserwesen"
|
||||
"Water": "Wasserwesen",
|
||||
"Travel": "Reisen"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Account": "Firma erstellen",
|
||||
"Copy Billing": "Rechnungsadresse kopieren"
|
||||
"Copy Billing": "Rechnungsadresse kopieren",
|
||||
"Set Primary": "Primär setzen"
|
||||
},
|
||||
"presetFilters": {
|
||||
"customers": "Kunden",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"layouts": {
|
||||
"detailConvert": "Interessent umwandeln",
|
||||
"listForAccount": "Liste (für Firma)"
|
||||
"listForAccount": "Liste (für Firma)",
|
||||
"listForContact": "Liste (für Kontakt)"
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
"current": "aktuell",
|
||||
"time": "Zeit",
|
||||
"User List": "Benutzerliste",
|
||||
"Manage Users": "Benutzer verwalten"
|
||||
"Manage Users": "Benutzer verwalten",
|
||||
"View Calendar": "Kalender anzeigen",
|
||||
"Create Shared View": "Gemeinsame Ansicht erstellen"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"parent": "Bezieht sich auf",
|
||||
"status": "Status",
|
||||
"dateStart": "Startdatum",
|
||||
"dateEnd": "Enddatum",
|
||||
"direction": "Richtung",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"description": "Beschreibung",
|
||||
"status": "Status",
|
||||
"type": "Typ",
|
||||
"startDate": "Startdatum",
|
||||
"endDate": "Enddatum",
|
||||
@@ -18,8 +16,13 @@
|
||||
"leadCreatedCount": "Erstellte Interessenten",
|
||||
"revenue": "Umsatz",
|
||||
"revenueConverted": "Umsatz (umgerechnet)",
|
||||
"budget": "Budget",
|
||||
"budgetConverted": "Budget (umgerechnet)"
|
||||
"budgetConverted": "Budget (umgerechnet)",
|
||||
"contactsTemplate": "Kontaktvorlage",
|
||||
"leadsTemplate": "Interessentenvorlage",
|
||||
"accountsTemplate": "Kontenvorlage",
|
||||
"usersTemplate": "Benutzervorlage",
|
||||
"mailMergeOnlyWithAddress": "Datensätze ohne gefüllte Adresse überspringen",
|
||||
"optedInCount": "Opt-In gesetzt"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Kontaktlisten",
|
||||
@@ -30,16 +33,15 @@
|
||||
"opportunities": "Verkaufschancen",
|
||||
"campaignLogRecords": "Protokoll",
|
||||
"massEmails": "Massen E-Mails",
|
||||
"trackingUrls": "Tracking URLs"
|
||||
"contactsTemplate": "Kontaktvorlage",
|
||||
"leadsTemplate": "Interessentenvorlage",
|
||||
"accountsTemplate": "Kontenvorlage",
|
||||
"usersTemplate": "Benutzervorlage"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Email": "E-Mail",
|
||||
"Web": "Web",
|
||||
"Television": "Fernsehen",
|
||||
"Radio": "Radio",
|
||||
"Newsletter": "Newsletter",
|
||||
"Mail": "Mail"
|
||||
"Television": "Fernsehen"
|
||||
},
|
||||
"status": {
|
||||
"Planning": "Planung",
|
||||
@@ -59,7 +61,9 @@
|
||||
"Email Templates": "E-Mail Vorlagen",
|
||||
"Unsubscribe again": "Abmelden",
|
||||
"Subscribe again": "Anmelden",
|
||||
"Create Target List": "Interessentenliste erstellen"
|
||||
"Create Target List": "Interessentenliste erstellen",
|
||||
"Mail Merge": "Serienbrief",
|
||||
"Generate Mail Merge PDF": "Serienbrief-PDF generieren"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Aktiv"
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
"application": "Applikation",
|
||||
"queueItem": "Warteschlangeneintrag",
|
||||
"stringData": "String Daten",
|
||||
"stringAdditionalData": "String zusätzliche Daten"
|
||||
"stringAdditionalData": "String zusätzliche Daten",
|
||||
"isTest": "Ist Test"
|
||||
},
|
||||
"links": {
|
||||
"queueItem": "Warteschlangeneintrag",
|
||||
"parent": "Bezieht sich auf",
|
||||
"object": "Objekt"
|
||||
"object": "Objekt",
|
||||
"campaign": "Kampagne"
|
||||
},
|
||||
"options": {
|
||||
"action": {
|
||||
@@ -23,7 +25,8 @@
|
||||
"Opted Out": "Opt-Out gesetzt",
|
||||
"Bounced": "Nicht zustellbar",
|
||||
"Clicked": "Geklickt",
|
||||
"Lead Created": "Interessent erstellt"
|
||||
"Lead Created": "Interessent erstellt",
|
||||
"Opted In": "Opt-In gesetzt"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
@@ -35,6 +38,7 @@
|
||||
"optedOut": "Opt-Out gesetzt",
|
||||
"bounced": "Nicht zustellbar",
|
||||
"clicked": "Geklickt",
|
||||
"leadCreated": "Interessent erstellt"
|
||||
"leadCreated": "Interessent erstellt",
|
||||
"optedIn": "Opt-In gesetzt"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"url": "URL",
|
||||
"urlToUse": "Code zum Einfügen anstelle einer URL",
|
||||
"campaign": "Kampagne"
|
||||
},
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"number": "Nummer",
|
||||
"status": "Status",
|
||||
"account": "Firma",
|
||||
"contact": "Kontakt",
|
||||
"contacts": "Kontakte",
|
||||
"priority": "Priorität",
|
||||
"type": "Typ",
|
||||
"description": "Beschreibung",
|
||||
"inboundEmail": "Eingehende E-Mail",
|
||||
"lead": "Interessent",
|
||||
"attachments": "Anhänge"
|
||||
"attachments": "Anhänge",
|
||||
"inboundEmail": "Gruppen E-Mail Konto"
|
||||
},
|
||||
"links": {
|
||||
"inboundEmail": "Eingehende E-Mail",
|
||||
"account": "Firma",
|
||||
"contact": "Kontakt (Primär)",
|
||||
"Contacts": "Kontakte",
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben",
|
||||
"emails": "E-Mails",
|
||||
"articles": "Wissensbasis Artikel",
|
||||
"lead": "Interessent",
|
||||
"attachments": "Anhänge"
|
||||
"attachments": "Anhänge",
|
||||
"inboundEmail": "Gruppen E-Mail Konto"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -37,14 +34,12 @@
|
||||
},
|
||||
"priority": {
|
||||
"Low": "Niedrig",
|
||||
"Normal": "Normal",
|
||||
"High": "Hoch",
|
||||
"Urgent": "Dringend"
|
||||
},
|
||||
"type": {
|
||||
"Question": "Frage",
|
||||
"Incident": "Vorfall",
|
||||
"Problem": "Problem"
|
||||
"Incident": "Vorfall"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"emailAddress": "E-Mail",
|
||||
"title": "Funktion",
|
||||
"accountRole": "Funktion",
|
||||
@@ -16,7 +15,11 @@
|
||||
"targetLists": "Kontaktlisten",
|
||||
"targetList": "Kontaktliste",
|
||||
"portalUser": "Portal Benutzer",
|
||||
"originalLead": "Ursprünglicher Interessent"
|
||||
"originalLead": "Ursprünglicher Interessent",
|
||||
"acceptanceStatus": "Akzeptanzstatus",
|
||||
"accountIsInactive": "Inaktives Konto",
|
||||
"acceptanceStatusMeetings": "Akzeptanzstatus (Meetings)",
|
||||
"acceptanceStatusCalls": "Akzeptanzstatus (Anrufe)"
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Verkaufschancen",
|
||||
@@ -29,7 +32,8 @@
|
||||
"casesPrimary": "Fälle (Primär)",
|
||||
"portalUser": "Portal Benutzer",
|
||||
"originalLead": "Ursprünglicher Interessent",
|
||||
"documents": "Dokumente"
|
||||
"documents": "Dokumente",
|
||||
"tasksPrimary": "Aufgaben (erweitert)"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Kontakt erstellen"
|
||||
@@ -44,6 +48,7 @@
|
||||
},
|
||||
"presetFilters": {
|
||||
"portalUsers": "Portal Benutzer",
|
||||
"notPortalUsers": "Keine Portal Benutzer"
|
||||
"notPortalUsers": "Keine Portal Benutzer",
|
||||
"accountActive": "Aktiv"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"futureDays": "Nächste X Tage",
|
||||
"useLastStage": "Gruppierung nach der zuletzt erreichten Verkaufsphase"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create Document": "Dokument erstellen",
|
||||
"Details": "Details"
|
||||
"Create Document": "Dokument erstellen"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"status": "Status",
|
||||
"file": "Datei",
|
||||
"type": "Typ",
|
||||
"publishDate": "Veröffentlichungsdatum",
|
||||
@@ -31,8 +28,6 @@
|
||||
"type": {
|
||||
"": "Kein(e)",
|
||||
"Contract": "Vertrag",
|
||||
"NDA": "NDA",
|
||||
"EULA": "EULA",
|
||||
"License Agreement": "Lizenzvereinbarung"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"Create Lead": "Interessent erstellen",
|
||||
"Create Contact": "Kontakt erstellen",
|
||||
"Create Task": "Neue Aufgabe",
|
||||
"Create Case": "Fall erstellen"
|
||||
"Create Case": "Fall erstellen",
|
||||
"Add to Contact": "als Kontakt hinzufügen",
|
||||
"Add to Lead": "als Interessent hinzufügen"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"status": "Status",
|
||||
"target": "Ziel",
|
||||
"sentAt": "Sendedatum",
|
||||
"attemptCount": "Versuche",
|
||||
@@ -17,7 +15,8 @@
|
||||
"status": {
|
||||
"Pending": "Schwebend",
|
||||
"Sent": "Gesendet",
|
||||
"Failed": "Fehlgeschlagen"
|
||||
"Failed": "Fehlgeschlagen",
|
||||
"Sending": "Wird gesendet"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"contacts": "Kontakte",
|
||||
"opportunities": "Verkaufschancen",
|
||||
"leads": "Interessenten",
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben",
|
||||
"emails": "E-Mails",
|
||||
@@ -21,7 +20,6 @@
|
||||
"Lead": "Interessent",
|
||||
"Target": "Ziel",
|
||||
"Opportunity": "Verkaufschance",
|
||||
"Meeting": "Meeting",
|
||||
"Calendar": "Kalender",
|
||||
"Call": "Anruf",
|
||||
"Task": "Aufgabe",
|
||||
@@ -32,7 +30,6 @@
|
||||
"TargetList": "Kontaktliste",
|
||||
"MassEmail": "Massen E-Mails",
|
||||
"EmailQueueItem": "E-Mail Warteschlangeneintrag",
|
||||
"CampaignTrackingUrl": "Tracking URL",
|
||||
"Activities": "Aktivitäten",
|
||||
"KnowledgeBaseArticle": "Wissensbasis Artikel",
|
||||
"KnowledgeBaseCategory": "Wissensbasis Kategorie",
|
||||
@@ -44,7 +41,6 @@
|
||||
"Lead": "Interessenten",
|
||||
"Target": "Zielkontakte",
|
||||
"Opportunity": "Verkaufschancen",
|
||||
"Meeting": "Meetings",
|
||||
"Calendar": "Kalender",
|
||||
"Call": "Anrufe",
|
||||
"Task": "Aufgaben",
|
||||
@@ -55,7 +51,6 @@
|
||||
"TargetList": "Kontaktlisten",
|
||||
"MassEmail": "Massen E-Mails",
|
||||
"EmailQueueItem": "E-Mail Warteschlangeneinträge",
|
||||
"CampaignTrackingUrl": "Tracking URLs",
|
||||
"Activities": "Aktivitäten",
|
||||
"KnowledgeBaseArticle": "Wissensbasis",
|
||||
"KnowledgeBaseCategory": "Wissensbasis Kategorien",
|
||||
@@ -111,7 +106,6 @@
|
||||
},
|
||||
"options": {
|
||||
"reminderTypes": {
|
||||
"Popup": "Popup",
|
||||
"Email": "E-Mail"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
"Move to Bottom": "zum Ende"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"status": "Status",
|
||||
"type": "Typ",
|
||||
"attachments": "Anhänge",
|
||||
"publishDate": "Veröffentlichungsdatum",
|
||||
@@ -38,9 +36,10 @@
|
||||
"Article": "Artikel"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
},
|
||||
"presetFilters": {
|
||||
"published": "Publiziert"
|
||||
},
|
||||
"tooltips": {
|
||||
"portals": "Artikel wird nur in bestimmten Portalen verfügbar sein."
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
"convert": "umwandeln"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"emailAddress": "E-Mail",
|
||||
"title": "Funktion",
|
||||
"website": "Webseite",
|
||||
@@ -14,7 +13,6 @@
|
||||
"accountName": "Firmenname",
|
||||
"doNotCall": "Anrufe unerwünscht",
|
||||
"address": "Adresse",
|
||||
"status": "Status",
|
||||
"source": "Quelle",
|
||||
"opportunityAmount": "Verkaufschance Volumen",
|
||||
"opportunityAmountConverted": "Verkaufschance Volumen (umgerechnet)",
|
||||
@@ -25,7 +23,11 @@
|
||||
"campaign": "Kampagne",
|
||||
"targetLists": "Kontaktlisten",
|
||||
"targetList": "Kontaktliste",
|
||||
"industry": "Industrie"
|
||||
"industry": "Industrie",
|
||||
"acceptanceStatus": "Akzeptanzstatus",
|
||||
"opportunityAmountCurrency": "Währungsbetrag der Verkaufschance",
|
||||
"acceptanceStatusMeetings": "Akzeptanzstatus (Meetings)",
|
||||
"acceptanceStatusCalls": "Akzeptanzstatus (Anrufe)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Kontaktlisten",
|
||||
@@ -51,8 +53,6 @@
|
||||
"Call": "Anruf",
|
||||
"Email": "E-Mail",
|
||||
"Existing Customer": "Bestandskunde",
|
||||
"Partner": "Partner",
|
||||
"Public Relations": "Public Relations",
|
||||
"Web Site": "Webseite",
|
||||
"Campaign": "Kampagne",
|
||||
"Other": "Andere"
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"status": "Status",
|
||||
"storeSentEmails": "Gesendete E-Mails speichern",
|
||||
"startAt": "Startdatum",
|
||||
"fromAddress": "Absenderadresse",
|
||||
@@ -13,7 +11,8 @@
|
||||
"inboundEmail": "E-Mail Konto",
|
||||
"targetLists": "Kontaktlisten",
|
||||
"excludingTargetLists": "Kontaktlisten ausschließen",
|
||||
"optOutEntirely": "Vollständiger Opt-Out"
|
||||
"optOutEntirely": "Vollständiger Opt-Out",
|
||||
"smtpAccount": "SMTP-Konto"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Kontaktlisten",
|
||||
@@ -35,7 +34,10 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create MassEmail": "Massen E-Mail erstellen",
|
||||
"Send Test": "Test senden"
|
||||
"Send Test": "Test senden",
|
||||
"System SMTP": "SMTP-System",
|
||||
"system": "System",
|
||||
"group": "Gruppe"
|
||||
},
|
||||
"messages": {
|
||||
"selectAtLeastOneTarget": "Zumindest ein Ziel auswählen",
|
||||
@@ -44,7 +46,8 @@
|
||||
"tooltips": {
|
||||
"optOutEntirely": "Für E-Mail Adressen von Empfängern, die sich abgemeldet haben, wird ein Opt-out gesetzt. Diese werden keine Massenaussendungen mehr erhalten.",
|
||||
"targetLists": "Zielkontakte die Nachrichten empfangen sollen",
|
||||
"excludingTargetLists": "Zielkontakte die keine Nachrichten empfangen sollen"
|
||||
"excludingTargetLists": "Zielkontakte die keine Nachrichten empfangen sollen",
|
||||
"storeSentEmails": "E-Mails werden in CRM gespeichert."
|
||||
},
|
||||
"presetFilters": {
|
||||
"actual": "Aktuell",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"parent": "Bezieht sich auf",
|
||||
"status": "Status",
|
||||
"dateStart": "Startdatum",
|
||||
"dateEnd": "Enddatum",
|
||||
"duration": "Dauer",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"account": "Firma",
|
||||
"stage": "Verkaufsphase",
|
||||
"amount": "Betrag",
|
||||
@@ -13,7 +12,10 @@
|
||||
"amountConverted": "Betrag (umgerechnet)",
|
||||
"amountWeightedConverted": "Betrag gewichtet",
|
||||
"campaign": "Kampagne",
|
||||
"originalLead": "Ursprünglicher Interessent"
|
||||
"originalLead": "Ursprünglicher Interessent",
|
||||
"amountCurrency": "Währungsbetrag",
|
||||
"contactRole": "Kontaktrolle",
|
||||
"lastStage": "Letzte Verkaufsphase"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Kontakte",
|
||||
@@ -25,8 +27,6 @@
|
||||
"stage": {
|
||||
"Prospecting": "Identifikation",
|
||||
"Qualification": "Qualifikation",
|
||||
"Proposal": "Angebot",
|
||||
"Negotiation": "Verhandlung",
|
||||
"Needs Analysis": "Bedarfserhebung",
|
||||
"Value Proposition": "Richtangebot",
|
||||
"Id. Decision Makers": "Identifikation der Entscheider",
|
||||
@@ -34,7 +34,9 @@
|
||||
"Proposal/Price Quote": "Angebot/Kostenvoranschlag",
|
||||
"Negotiation/Review": "Verhandlung/Überarbeitung",
|
||||
"Closed Won": "Gewonnen",
|
||||
"Closed Lost": "Verloren"
|
||||
"Closed Lost": "Verloren",
|
||||
"Proposal": "Angebot",
|
||||
"Negotiation": "Verhandlung"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"description": "Beschreibung",
|
||||
"entryCount": "Eingabezähler",
|
||||
"campaigns": "Kampagnen",
|
||||
"endDate": "Enddatum",
|
||||
"targetLists": "Kontaktlisten",
|
||||
"includingActionList": "Inklusive",
|
||||
"excludingActionList": "Exklusive"
|
||||
"excludingActionList": "Exklusive",
|
||||
"optedOutCount": "gesetzte Opt-Out Anzahl",
|
||||
"targetStatus": "Zielstatus",
|
||||
"isOptedOut": "Ist Opt-Out gesetzt"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Firmen",
|
||||
@@ -19,16 +21,16 @@
|
||||
"options": {
|
||||
"type": {
|
||||
"Email": "E-Mail",
|
||||
"Web": "Web",
|
||||
"Television": "Fernsehen",
|
||||
"Radio": "Radio",
|
||||
"Newsletter": "Newsletter"
|
||||
"Television": "Fernsehen"
|
||||
},
|
||||
"targetStatus": {
|
||||
"Opted Out": "Opt-Out gesetzt",
|
||||
"Listed": "aufgelistet"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create TargetList": "Kontaktliste erstellen",
|
||||
"Opted Out": "Opt-Out gesetzt",
|
||||
"Cancel Opt-Out": "Opt-Out zurücksetzen",
|
||||
"Opt-Out": "Opt-Out"
|
||||
"Cancel Opt-Out": "Opt-Out zurücksetzen"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"parent": "Bezieht sich auf",
|
||||
"status": "Status",
|
||||
"dateStart": "Startdatum",
|
||||
"dateEnd": "Fällig am",
|
||||
"dateStartDate": "Startdatum (ganztägig)",
|
||||
@@ -13,10 +11,13 @@
|
||||
"account": "Firma",
|
||||
"dateCompleted": "Datum erledigt",
|
||||
"attachments": "Anhänge",
|
||||
"reminders": "Erinnerungen"
|
||||
"reminders": "Erinnerungen",
|
||||
"contact": "Kontakt"
|
||||
},
|
||||
"links": {
|
||||
"attachments": "Anhänge"
|
||||
"attachments": "Anhänge",
|
||||
"account": "Firma",
|
||||
"contact": "Kontakt"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -28,7 +29,6 @@
|
||||
},
|
||||
"priority": {
|
||||
"Low": "Niedrig",
|
||||
"Normal": "Normal",
|
||||
"High": "Hoch",
|
||||
"Urgent": "Dringend"
|
||||
}
|
||||
@@ -40,8 +40,8 @@
|
||||
"presetFilters": {
|
||||
"actual": "Aktuell",
|
||||
"completed": "Abgeschlossen",
|
||||
"deferred": "Zurückgestellt",
|
||||
"todays": "Heutige",
|
||||
"overdue": "Überfällig"
|
||||
"overdue": "Überfällig",
|
||||
"deferred": "Zurückgestellt"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"links": {
|
||||
"targetLists": "Kontaktlisten"
|
||||
},
|
||||
"fields": {
|
||||
"acceptanceStatus": "Akzeptanzstatus",
|
||||
"acceptanceStatusMeetings": "Akzeptanzstatus (Meetings)",
|
||||
"acceptanceStatusCalls": "Akzeptanzstatus (Anrufe)"
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
"clickedCount": "Clicked",
|
||||
"optedOutCount": "Opted Out",
|
||||
"bouncedCount": "Bounced",
|
||||
"optedInCount": "Opted In",
|
||||
"hardBouncedCount": "Hard Bounced",
|
||||
"softBouncedCount": "Soft Bounced",
|
||||
"leadCreatedCount": "Leads Created",
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"Opted Out": "Opted Out",
|
||||
"Bounced": "Bounced",
|
||||
"Clicked": "Clicked",
|
||||
"Lead Created": "Lead Created"
|
||||
"Lead Created": "Lead Created",
|
||||
"Opted In": "Opted In"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
@@ -35,6 +36,7 @@
|
||||
"sent": "Sent",
|
||||
"opened": "Opened",
|
||||
"optedOut": "Opted Out",
|
||||
"optedIn": "Opted In",
|
||||
"bounced": "Bounced",
|
||||
"clicked": "Clicked",
|
||||
"leadCreated": "Lead Created"
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
"type": "Tipo",
|
||||
"contactRole": "Título",
|
||||
"campaign": "Campaña",
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetList": "Lista de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"targetList": "Lista de Intereses",
|
||||
"originalLead": "Referencia Original",
|
||||
"contactIsInactive": "Inactivo"
|
||||
},
|
||||
@@ -22,11 +22,11 @@
|
||||
"opportunities": "Oportunidades",
|
||||
"cases": "Casos",
|
||||
"documents": "Documentos",
|
||||
"meetingsPrimary": "Juntas (ampliado)",
|
||||
"meetingsPrimary": "Presentaciones (ampliado)",
|
||||
"callsPrimary": "Llamadas (ampliado)",
|
||||
"tasksPrimary": "Tareas (ampliado)",
|
||||
"emailsPrimary": "Correos (ampliado)",
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"campaignLogRecords": "Historial de Campañas",
|
||||
"campaign": "Campaña",
|
||||
"portalUsers": "Usuarios del Portal",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"type": "Tipo",
|
||||
"startDate": "Fecha de Inicio",
|
||||
"endDate": "Fecha de Fin",
|
||||
"targetLists": "Lista de Objetivos",
|
||||
"excludingTargetLists": "Listas de Objetivos Excluidas",
|
||||
"targetLists": "Lista de Intereses",
|
||||
"excludingTargetLists": "Listas de Intereses Excluidas",
|
||||
"sentCount": "Enviado",
|
||||
"openedCount": "Abierto",
|
||||
"clickedCount": "Leídos",
|
||||
@@ -19,18 +19,27 @@
|
||||
"revenue": "Ingresos",
|
||||
"revenueConverted": "ingresos (convertido)",
|
||||
"budget": "Presupuesto",
|
||||
"budgetConverted": "Presupuesto (convertido)"
|
||||
"budgetConverted": "Presupuesto (convertido)",
|
||||
"contactsTemplate": "Formato de Contactos",
|
||||
"leadsTemplate": "Formato de Referencias",
|
||||
"accountsTemplate": "Formato de Cuentas",
|
||||
"usersTemplate": "Formato de Usuarios",
|
||||
"mailMergeOnlyWithAddress": "Saltar registros sin dirección capturada"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"excludingTargetLists": "Listas de Objetivos Excluidas",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"excludingTargetLists": "Listas de Intereses Excluidas",
|
||||
"accounts": "Cuentas",
|
||||
"contacts": "Contactos",
|
||||
"leads": "Referencias",
|
||||
"opportunities": "Oportunidades",
|
||||
"campaignLogRecords": "Historial",
|
||||
"massEmails": "Correos Masivos",
|
||||
"trackingUrls": "Seguimiento a URLs"
|
||||
"trackingUrls": "Seguimiento a URLs",
|
||||
"contactsTemplate": "Formato de Contactos",
|
||||
"leadsTemplate": "Formato de Referencias",
|
||||
"accountsTemplate": "Formato de Cuentas",
|
||||
"usersTemplate": "Formato de Usuarios"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
@@ -48,7 +57,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create Campaign": "Crear Campaña",
|
||||
"Target Lists": "Listas de Objetivos",
|
||||
"Target Lists": "Listas de Intereses",
|
||||
"Statistics": "Estadísticas",
|
||||
"hard": "duro",
|
||||
"soft": "suave",
|
||||
@@ -57,7 +66,9 @@
|
||||
"Email Templates": "Correo Modelo",
|
||||
"Unsubscribe again": "Cancelar otra vez la suscripción",
|
||||
"Subscribe again": "Volverse a suscribir",
|
||||
"Create Target List": "Crear Lista de Obejtivos"
|
||||
"Create Target List": "Crear Lista de Obejtivos",
|
||||
"Mail Merge": "Generar Correos",
|
||||
"Generate Mail Merge PDF": "Generar PDF para Correos"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Activo"
|
||||
@@ -67,7 +78,7 @@
|
||||
"subscribedAgain": "Usted se ha vuelto a suscribir."
|
||||
},
|
||||
"tooltips": {
|
||||
"targetLists": "Los objetivos que deben recibir los mensajes.",
|
||||
"excludingTargetLists": "Los objetivos que no deben recibir mensajes."
|
||||
"targetLists": "Intereses que deben recibir mensajes.",
|
||||
"excludingTargetLists": "Los intereses que no deben recibir mensajes."
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"actionDate": "Fecha",
|
||||
"data": "Datos",
|
||||
"campaign": "Campaña",
|
||||
"parent": "Objetivo",
|
||||
"parent": "Interés",
|
||||
"object": "Objeto",
|
||||
"application": "Aplicacion",
|
||||
"queueItem": "Item de la Lista",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"account": "Cuenta",
|
||||
"contact": "Contacto (Primario)",
|
||||
"Contacts": "Contactos",
|
||||
"meetings": "Juntas",
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas",
|
||||
"emails": "Correos",
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
"opportunityRole": "Rol de Oportunidad",
|
||||
"description": "Descripción",
|
||||
"campaign": "Campaña",
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetList": "Lista de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"targetList": "Lista de Intereses",
|
||||
"portalUser": "Usuario del Portal",
|
||||
"originalLead": "Referencia Original",
|
||||
"acceptanceStatus": "Estatus de Aprobación",
|
||||
"accountIsInactive": "Cuenta Inactiva",
|
||||
"acceptanceStatusMeetings": "Estátus de Aceptación (Juntas)",
|
||||
"acceptanceStatusMeetings": "Estatus de Aceptación (Presentaciones)",
|
||||
"acceptanceStatusCalls": "Estátus de Aceptación (Llamadas)"
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Oportunidades",
|
||||
"cases": "Casos",
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"campaignLogRecords": "Registrar Campaña",
|
||||
"campaign": "Campaña",
|
||||
"account": "Cuenta (Primaria)",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"fields": {
|
||||
"futureDays": "Siguientes 'n' Días"
|
||||
"futureDays": "Siguientes 'n' Días",
|
||||
"useLastStage": "Agrupar por la última etapa lograda"
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Nombre",
|
||||
"status": "Estátus",
|
||||
"target": "Objetivo",
|
||||
"target": "Interés",
|
||||
"sentAt": "Enviado",
|
||||
"attemptCount": "Intentos",
|
||||
"emailAddress": "Correo Electrónico",
|
||||
@@ -10,7 +10,7 @@
|
||||
"isTest": "Es una prueba"
|
||||
},
|
||||
"links": {
|
||||
"target": "Objetivo",
|
||||
"target": "Interés",
|
||||
"massEmail": "Correo Masivo"
|
||||
},
|
||||
"options": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"contacts": "Contactos",
|
||||
"opportunities": "Oportunidades",
|
||||
"leads": "Referencias",
|
||||
"meetings": "Juntas",
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas",
|
||||
"emails": "Correos",
|
||||
@@ -19,9 +19,9 @@
|
||||
"Account": "Cuenta",
|
||||
"Contact": "Contacto",
|
||||
"Lead": "Referencia",
|
||||
"Target": "Objetivo",
|
||||
"Target": "Interés",
|
||||
"Opportunity": "Oportunidad",
|
||||
"Meeting": "Junta",
|
||||
"Meeting": "Presentación",
|
||||
"Calendar": "Calendario",
|
||||
"Call": "Llamada",
|
||||
"Task": "Tarea",
|
||||
@@ -29,7 +29,7 @@
|
||||
"Document": "Documento",
|
||||
"DocumentFolder": "Carpeta de Documento",
|
||||
"Campaign": "Campaña",
|
||||
"TargetList": "Lista de Objetivos",
|
||||
"TargetList": "Lista de Intereses",
|
||||
"MassEmail": "Correo Masivo",
|
||||
"EmailQueueItem": "Item en Cola de Correo",
|
||||
"CampaignTrackingUrl": "Seguimiento de URLs",
|
||||
@@ -42,9 +42,9 @@
|
||||
"Account": "Cuentas",
|
||||
"Contact": "Contactos",
|
||||
"Lead": "Referencias",
|
||||
"Target": "Objetivos",
|
||||
"Target": "Intereses",
|
||||
"Opportunity": "Oportunidades",
|
||||
"Meeting": "Juntas",
|
||||
"Meeting": "Presentaciones",
|
||||
"Calendar": "Calendario",
|
||||
"Call": "Llamadas",
|
||||
"Task": "Tareas",
|
||||
@@ -52,7 +52,7 @@
|
||||
"Document": "Documentos",
|
||||
"DocumentFolder": "Carpetas de Documentos",
|
||||
"Campaign": "Campañas",
|
||||
"TargetList": "Listas de Objetivos",
|
||||
"TargetList": "Listas de Intereses",
|
||||
"MassEmail": "Correos Masivos",
|
||||
"EmailQueueItem": "Items en Cola de Correo",
|
||||
"CampaignTrackingUrl": "URLs de Seguimiento",
|
||||
@@ -68,7 +68,7 @@
|
||||
"Cases": "Mis Casos",
|
||||
"Calendar": "Calendario",
|
||||
"Calls": "Mis Llamadas",
|
||||
"Meetings": "Mis Juntas",
|
||||
"Meetings": "Mis Presentaciones",
|
||||
"OpportunitiesByStage": "Oportunidades por Etapa",
|
||||
"OpportunitiesByLeadSource": "Oportunidades de Fuente de Referencias",
|
||||
"SalesByMonth": "Ventas por Mes",
|
||||
@@ -80,10 +80,10 @@
|
||||
"Activities": "Actividades",
|
||||
"History": "Historial",
|
||||
"Attendees": "Asistentes",
|
||||
"Schedule Meeting": "Junta Programada",
|
||||
"Schedule Call": "Llamada Programada",
|
||||
"Schedule Meeting": "Agendar Presentación",
|
||||
"Schedule Call": "Agendar LLamada",
|
||||
"Compose Email": "Escribir Correo",
|
||||
"Log Meeting": "Registrar Junta",
|
||||
"Log Meeting": "Registrar Presentación",
|
||||
"Log Call": "Registrar Llamada",
|
||||
"Archive Email": "Archivar Correo",
|
||||
"Create Task": "Crear Tarea",
|
||||
|
||||
@@ -23,16 +23,16 @@
|
||||
"createdContact": "Contacto",
|
||||
"createdOpportunity": "Oportunidad",
|
||||
"campaign": "Campaña",
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetList": "Lista de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"targetList": "Lista de Intereses",
|
||||
"industry": "Industria",
|
||||
"acceptanceStatus": "Estatus de Aprobación",
|
||||
"opportunityAmountCurrency": "Importe de la Oportunidad",
|
||||
"acceptanceStatusMeetings": "Estátus de Aceptación (Juntas)",
|
||||
"acceptanceStatusMeetings": "Estatus de Aceptación (Presentaciones)",
|
||||
"acceptanceStatusCalls": "Estátus de Aceptación (Llamadas)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"campaignLogRecords": "Registrar Campaña",
|
||||
"campaign": "Campaña",
|
||||
"createdAccount": "Cuenta",
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
"campaign": "Campaña",
|
||||
"emailTemplate": "Plantilla de Correo",
|
||||
"inboundEmail": "Cuenta de correo",
|
||||
"targetLists": "Lista de Objetivos",
|
||||
"excludingTargetLists": "Listas de Objetivos Excluídas",
|
||||
"targetLists": "Lista de Intereses",
|
||||
"excludingTargetLists": "Listas de Intereses Excluídos",
|
||||
"optOutEntirely": "Confirmación Completada",
|
||||
"smtpAccount": "Cuenta SMTP"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Listas de Objetivos",
|
||||
"excludingTargetLists": "Listas de Objetivos Excluídas",
|
||||
"targetLists": "Listas de Intereses",
|
||||
"excludingTargetLists": "Listas de Intereses Excluídos",
|
||||
"queueItems": "Items en cola",
|
||||
"campaign": "Campaña",
|
||||
"emailTemplate": "Plantilla de Correo",
|
||||
@@ -42,13 +42,13 @@
|
||||
"group": "grupo"
|
||||
},
|
||||
"messages": {
|
||||
"selectAtLeastOneTarget": "Seleccione al menos un objetivo.",
|
||||
"selectAtLeastOneTarget": "Seleccione al menos un interés",
|
||||
"testSent": "Correo(s) de prueba que se enviarán"
|
||||
},
|
||||
"tooltips": {
|
||||
"optOutEntirely": "Los correos de destinatarios que cancelaron su suscripción serán marcados como rechazados y ya no recibirán correos masivos.",
|
||||
"targetLists": "Los objetivos que deben recibir los mensajes.",
|
||||
"excludingTargetLists": "Los objetivos que no deben recibir mensajes.",
|
||||
"targetLists": "Los intereses que deben recibir los mensajes.",
|
||||
"excludingTargetLists": "Los intereses que no deben recibir mensajes.",
|
||||
"storeSentEmails": "Los correos se guardarán en el CRM."
|
||||
},
|
||||
"presetFilters": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"name": "Nombre",
|
||||
"parent": "Padre",
|
||||
"status": "Estátus",
|
||||
"status": "Estatus",
|
||||
"dateStart": "Fecha de Comienzo",
|
||||
"dateEnd": "Fecha de Finalización",
|
||||
"duration": "Duración",
|
||||
@@ -32,7 +32,7 @@
|
||||
"setNotHeld": "Marcar como Pendiente"
|
||||
},
|
||||
"labels": {
|
||||
"Create Meeting": "Crear Junta",
|
||||
"Create Meeting": "Crear Presentación",
|
||||
"Set Held": "Marcar como Retenida",
|
||||
"Set Not Held": "Marcar como Pendiente",
|
||||
"Send Invitations": "Enviar Invitaciones",
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"campaign": "Campaña",
|
||||
"originalLead": "Referencia original",
|
||||
"amountCurrency": "Importe en Moneda",
|
||||
"contactRole": "Rol del Contacto"
|
||||
"contactRole": "Rol del Contacto",
|
||||
"lastStage": "Última Etapa"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Contactos",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user