Compare commits

...

176 Commits
4.7.2 ... 4.8.2

Author SHA1 Message Date
yuri
19f1ef75fd fix application set 2017-09-12 16:51:54 +03:00
yuri
b29172ad18 it_IT lang fixes 2017-09-12 10:45:21 +03:00
yuri
8f201944a4 email inline attachment cleanable 2017-09-08 11:02:06 +03:00
yuri
6c9a350e43 fix typo 2017-09-06 12:09:13 +03:00
yuri
6f1be59b4c remove attachment if file/image changed 2017-09-06 12:03:46 +03:00
yuri
5a15fcbbb5 cleanup attachment improvements 2017-09-06 11:45:05 +03:00
yuri
9084e6b679 fix excel 2017-09-05 11:18:40 +03:00
yuri
ccb4fb13a9 fix formula parser 2017-09-04 10:56:57 +03:00
yuri
b07c3b62cf fix recotd stop listen window 2017-08-31 16:08:34 +03:00
yuri
d4805acef7 layout noLabel param 2017-08-31 15:37:14 +03:00
yuri
0637304a4f fix empty varchar and text 2017-08-31 11:39:22 +03:00
yuri
8f9089f0a3 fix lang 2017-08-30 16:31:22 +03:00
yuri
83ccec26ee fix lang 2017-08-30 15:29:33 +03:00
yuri
db427c0bf1 version 2017-08-28 17:02:24 +03:00
yuri
0785ab9ee1 bottom loaded with middle 2017-08-28 16:57:17 +03:00
yuri
767dcd1e9f Merge branch 'hotfix/4.8.2' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.8.2 2017-08-23 13:53:22 +03:00
Taras Machyshyn
7e997b224b Fixed undefined Log class 2017-08-23 12:47:11 +03:00
yuri
1c11442aea lang portal permission fix 2017-08-23 11:08:25 +03:00
Taras Machyshyn
7a08eea518 Changed news URL 2017-08-22 16:48:35 +03:00
Taras Machyshyn
cb14ca4627 installer: load values from existing data/config.php 2017-08-22 15:54:24 +03:00
Taras Machyshyn
7a6a8bc707 installer: added possibility to load values from existing data/config.php 2017-08-22 14:19:55 +03:00
yuri
cc3ba89ab3 calendar dashlet month title 2017-08-21 15:07:21 +03:00
yuri
88135f93fe calendar dashlet next/previous buttons and some css fixes 2017-08-21 13:00:51 +03:00
yuri
1148e391a6 panel tpl fix 2017-08-21 11:52:22 +03:00
yuri
f7c5706004 calendar dashlet view link 2017-08-21 11:25:01 +03:00
yuri
3120afb55e fix date time filter ranges 2017-08-21 10:55:16 +03:00
yuri
e991241ad2 remove target lang 2017-08-18 11:22:30 +03:00
Sebastien DOIDO
22e7756083 Fix clear cache typo update fr lang (#623)
* Update fr_FR lang.

* Fix clear cache typo.
2017-08-18 11:22:17 +03:00
yuri
f2e48df7ea fix lang sr_RS 2017-08-18 11:10:08 +03:00
yuri
3b4dc45984 dont show auth error message 2017-08-16 16:30:55 +03:00
yuri
d9b1418f36 fix check personal email account job 2017-08-16 15:51:29 +03:00
yuri
443016790a fix menu check access 2017-08-16 14:45:23 +03:00
yuri
ca9253ecd5 email account assigned user required 2017-08-16 14:39:42 +03:00
yuri
395b11474e lang fix 2017-08-15 17:20:54 +03:00
yuri
1c7fb79b33 more db ssl options 2017-08-15 12:54:49 +03:00
yuri
c6c16cd488 ssl database 2017-08-15 12:39:05 +03:00
yuri
a56502b814 show loading if email address in not loaded 2017-08-14 16:38:33 +03:00
yuri
24137df013 lang 2017-08-14 11:51:38 +03:00
yuri
b945473422 fix array field 2017-08-11 15:05:23 +03:00
yuri
ab7db9d084 color picker field 2017-08-11 14:57:14 +03:00
yuri
c36007f216 fix naming 2017-08-11 14:49:13 +03:00
yuri
1d8cc18411 fix record tpl 2017-08-11 14:40:00 +03:00
yuri
723fc52d1c fix cache 2017-08-11 11:37:53 +03:00
yuri
073aedc18b strikethrough inactive account 2017-08-10 14:35:20 +03:00
yuri
035abf03cb field manager label upper case first 2017-08-10 13:36:55 +03:00
yuri
50dbc60a5c file/image should create link 2017-08-10 13:32:24 +03:00
yuri
dbdc83c544 fix stream acl 2017-08-10 12:58:45 +03:00
yuri
1d743afd2a Merge branch 'hotfix/4.8.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.8.1 2017-08-10 12:31:44 +03:00
yuri
094bf69fab contacts panel filters 2017-08-10 12:31:13 +03:00
Taras Machyshyn
706226298a Merge branch 'hotfix/4.8.1' of ssh://172.20.0.1/var/git/espo/backend into hotfix/4.8.1 2017-08-09 17:38:17 +03:00
Taras Machyshyn
322bbb5d95 Schema converter optimization 2017-08-09 17:37:58 +03:00
yuri
5c12c6133f new text filters 2017-08-09 16:12:27 +03:00
yuri
417367838c fix array length 2017-08-09 14:30:46 +03:00
yuri
6f1cc78329 array\length function 2017-08-09 14:18:02 +03:00
yuri
a2faab036c file audited 2017-08-09 14:07:41 +03:00
yuri
a32441543c image converter orm 2017-08-09 14:06:44 +03:00
yuri
f4497402ca noIndex and file metadata converter 2017-08-09 13:13:33 +03:00
tanyalei
ef6548bc82 Import fix: find related person by name (#605) 2017-08-08 17:30:36 +03:00
yuri
f1bf0b3dee fix model:getLinkMultipleColumn 2017-08-08 15:18:13 +03:00
yuri
ec23cae38d fix user teams load error 2017-08-08 14:01:36 +03:00
yuri
771e3237cf system user is admin 2017-08-08 13:47:43 +03:00
yuri
ae72146d14 avatar requiring auth 2017-08-08 13:44:09 +03:00
yuri
20d6865323 version 2017-08-08 12:49:14 +03:00
yuri
89e13b0bac next number fix 2017-08-04 17:59:07 +03:00
yuri
8fa36ac14c fix next number unlock table 2017-08-04 17:45:46 +03:00
yuri
e557782c64 fix notice 2017-08-04 14:05:28 +03:00
yuri
7cc7f3fbcf week function 2017-08-03 12:45:53 +03:00
yuri
3f293a6d23 orm: week function 2017-08-03 12:07:06 +03:00
yuri
309b09f6ce Merge branch 'master' of github.com:espocrm/espocrm 2017-08-02 12:26:58 +03:00
yuri
0b7e9f3d62 disableHooks property 2017-08-02 11:51:36 +03:00
tanyalei
58c17ffd5c beforeSave for Event fix (#602) 2017-08-01 15:18:44 +03:00
yuri
7d3ba23a9f fix labal manager 2017-07-31 16:41:04 +03:00
yuri
ca2e4f5b37 fix duplicateIgnore 2017-07-31 16:17:47 +03:00
yuri
d82174fec5 fix excel export 2017-07-31 13:00:55 +03:00
yuri
033c65b79b acl strict mode 2017-07-31 12:15:01 +03:00
yuri
dbc8e5d9cc entity manager: common reserved word 2017-07-31 11:28:48 +03:00
yuri
42407935d5 duplicateIgnore 2017-07-31 11:23:52 +03:00
yuri
e7d558b0bf version 2017-07-31 11:14:48 +03:00
yuri
290b530b92 Merge branch 'master' of github.com:espocrm/espocrm 2017-07-18 15:23:24 +03:00
yuri
18726bdccd empty reminder if event is not actual 2017-07-18 15:23:09 +03:00
Dmitry Danilson
06b0498abb Fix #592: Replace undefined with (#593) 2017-07-14 13:57:40 +03:00
yuri
7a7627e931 update mail-mime-parser 2017-07-12 12:03:37 +03:00
yuri
c6038e9cee email account: unset recent flag 2017-07-12 11:32:10 +03:00
yuri
0e05596155 beforeSave repositoty order fix 2017-07-12 11:28:59 +03:00
yuri
0ca212f911 fix warning 2017-07-11 11:08:24 +03:00
yuri
94c2b9deda fix link search 2017-07-07 11:42:46 +03:00
yuri
2016a1bb0d relationship panel link in defs 2017-07-07 11:05:06 +03:00
yuri
6841ba9c85 side panel fieldList 2017-07-07 11:04:19 +03:00
yuri
d5988f2c04 fix after twice after remove hook 2017-07-05 15:38:11 +03:00
yuri
a56c9c3a46 fix complexText link 2017-07-04 17:28:51 +03:00
yuri
9d0bbdcd4d sr_RS lang 2017-07-04 15:47:57 +03:00
yuri
4bfaac3414 layout changes 2017-07-03 17:11:54 +03:00
yuri
d54c044fd9 Merge branch 'hotfix/4.7.3' 2017-07-03 11:27:08 +03:00
yuri
1def66bb77 fix team clear roles cache 2017-07-03 11:26:25 +03:00
yuri
75041db2e9 currency setting changes 2017-06-28 14:37:45 +03:00
yuri
356948cdf7 fix preferences edit 2017-06-28 14:04:33 +03:00
yuri
db92abf3ee Merge branch 'hotfix/4.7.3' 2017-06-28 12:45:20 +03:00
yuri
d5d59abf13 fix notice 2017-06-28 12:45:11 +03:00
yuri
69c1e3e8e0 email load bcc name 2017-06-28 12:22:08 +03:00
yuri
963c4e3e2b fix typo 2017-06-26 12:43:22 +03:00
yuri
cc4c735a27 entity manager: dont allow entity name if controller exists 2017-06-26 12:42:47 +03:00
yuri
175517a9dc list row menu links 2017-06-26 12:23:39 +03:00
yuri
9cb3548393 cleanup reminders 2017-06-26 12:22:36 +03:00
yuri
ac0dea317f reminder threshold fix 2017-06-26 11:42:44 +03:00
yuri
839dfb9709 Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2017-06-23 15:47:24 +03:00
Taras Machyshyn
2c7a4098f3 Upgrade manager: added beforeUpgradeFiles and afterUpgradeFiles 2017-06-23 13:16:36 +03:00
yuri
0228396376 fix formula dayOfWeek 2017-06-23 13:11:00 +03:00
yuri
c5aa0eba48 fix formula dayOfWeek 2017-06-23 13:10:42 +03:00
Taras Machyshyn
ac4472975d Fixed scheduled job run date 2017-06-23 12:40:33 +03:00
yuri
379f5fbaaf field manager ui fix 2017-06-23 12:34:21 +03:00
yuri
af71408c52 cleanup 2017-06-23 12:19:22 +03:00
yuri
b7a4fa1e7c Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend 2017-06-23 12:15:39 +03:00
yuri
92b6e45ef2 default for link field 2017-06-23 12:15:31 +03:00
yuri
acf6f67627 fix warning 2017-06-23 12:15:12 +03:00
Taras Machyshyn
017c34dcde Orm converter: remove fields without type 2017-06-22 17:26:05 +03:00
yuri
f6e54918b2 fix orm 2017-06-22 12:43:11 +03:00
yuri
75b3c33526 tpl fix 2017-06-21 15:22:28 +03:00
yuri
f784760735 css fix 2017-06-21 14:22:30 +03:00
yuri
a739b3a96e fix link field empty name 2017-06-21 13:10:55 +03:00
yuri
0a8e0f00c2 defaultAttributes param 2017-06-21 12:37:01 +03:00
yuri
00c4cf4893 update mailmimeparser 2017-06-21 12:07:17 +03:00
yuri
e8e9ea9e2f fix search trim 2017-06-20 16:08:50 +03:00
yuri
c9cdb6767b update slim 2017-06-20 14:50:03 +03:00
yuri
24f9c1c762 es_MX lang 2017-06-20 11:08:15 +03:00
yuri
766a880799 lang 2017-06-20 11:04:23 +03:00
yuri
07de0dbeb0 fix search preset 2017-06-19 16:35:04 +03:00
yuri
d1752f2ff0 fix select related 2017-06-19 16:13:59 +03:00
yuri
ef27234ef8 enum column and varchar column field views 2017-06-19 15:58:20 +03:00
yuri
685862f1de fix enum int float 2017-06-19 11:04:12 +03:00
yuri
97621c45a8 opportunity role search 2017-06-16 12:50:35 +03:00
yuri
b96d433796 search by account fixes 2017-06-15 17:31:32 +03:00
yuri
03d784c284 label manager 2017-06-15 16:53:12 +03:00
yuri
02ef97724f inactive account 2017-06-15 16:50:37 +03:00
yuri
93e2c65d30 en_GB lang 2017-06-14 15:55:17 +03:00
yuri
d02bf65a55 version 2017-06-14 12:53:29 +03:00
yuri
fb2f566314 email importer contact parent if no account 2017-06-14 12:51:36 +03:00
yuri
2227870854 Merge branch 'master' of github.com:espocrm/espocrm 2017-06-14 12:34:16 +03:00
yuri
b195509843 Merge branch 'hotfix/4.7.3' 2017-06-14 12:34:06 +03:00
yuri
2100fa0606 fix email acl ui 2017-06-14 12:33:56 +03:00
Dmitry Danilson
3fe43bdfa3 #563 Replace Container::loadLog() with loader class (#565)
Looks good. Thanks.
2017-06-14 12:18:25 +03:00
yuri
26ad3d48af da_DK fix 2017-06-14 11:59:16 +03:00
yuri
9c224fefb7 label editor: confirm leave out 2017-06-12 14:51:51 +03:00
barwi
e4db873e43 fix typo (#560) 2017-06-12 14:29:50 +03:00
barwi
f06d53b3d2 fix typo (#560) 2017-06-12 14:28:58 +03:00
yuri
4ef706b716 Merge branch 'hotfix/4.7.2' 2017-06-08 16:52:16 +03:00
yuri
b8be2c9e13 travel industry 2017-06-07 11:51:20 +03:00
yuri
f05c763119 label manager 2017-06-06 15:09:36 +03:00
yuri
262b3bb3b3 Merge branch 'hotfix/4.7.2' 2017-06-06 14:01:53 +03:00
yuri
d3ea71c726 Merge branch 'hotfix/4.7.2' 2017-06-06 12:03:35 +03:00
yuri
62ccfaa9c0 Merge branch 'hotfix/4.7.2' 2017-05-31 10:38:00 +03:00
yuri
c0b169f7dd Merge branch 'hotfix/4.7.2' 2017-05-29 11:40:18 +03:00
yuri
3d68c77ed4 Merge branch 'hotfix/4.7.1' 2017-05-26 11:19:11 +03:00
yuri
a2e241966e Merge branch 'hotfix/4.7.1' 2017-05-25 14:22:49 +03:00
yuri
7bf7944360 excel export fix 2017-05-25 11:16:23 +03:00
yuri
9f72b074d5 Merge branch 'hotfix/4.7.1' 2017-05-24 13:41:26 +03:00
yuri
59b7934aa2 Merge branch 'hotfix/4.7.1' 2017-05-22 15:41:03 +03:00
yuri
a7a61a993c Merge branch 'master' of github.com:espocrm/espocrm 2017-05-22 14:36:09 +03:00
yunga91
68d1c0854b Fix view name resolving for custom field types in filter action (#546)
* fix view name resolving for custom field types in merge action

* fix view name resolving for custom field types in filter action
2017-05-22 14:35:31 +03:00
yuri
fdd6f01415 Merge branch 'hotfix/4.7.1' 2017-05-22 14:16:03 +03:00
yuri
54a045e25a Merge branch 'hotfix/4.7.1' 2017-05-19 17:37:25 +03:00
yuri
862b76f165 Merge branch 'hotfix/4.7.1' 2017-05-19 15:11:16 +03:00
yuri
5ed0b2ba6b field manager: forbid underscore name 2017-05-18 13:24:15 +03:00
yuri
bbe26618ac fix excel export label 2017-05-18 12:44:10 +03:00
yuri
6f65d9e8d8 Merge branch 'hotfix/4.7.1' 2017-05-18 12:18:39 +03:00
yuri
f1b0decbab Merge branch 'hotfix/4.7.1' 2017-05-17 16:08:06 +03:00
yuri
c2b60698bc Merge branch 'hotfix/4.7.1' 2017-05-17 15:56:37 +03:00
yuri
63ff4c1dca Merge branch 'hotfix/4.7.1' 2017-05-17 15:32:03 +03:00
yuri
8987ec61fe Merge branch 'hotfix/4.7.1' 2017-05-16 13:45:29 +03:00
yuri
a93627d274 tooltips 2017-05-16 12:58:23 +03:00
yuri
90fe18b660 Merge branch 'hotfix/4.7.1' 2017-05-16 12:47:44 +03:00
yuri
6182376d1f Merge branch 'hotfix/4.7.1' 2017-05-16 12:21:48 +03:00
yuri
66235f7e77 hide 2017-05-16 11:36:46 +03:00
yuri
49b61dab53 Merge branch 'hotfix/4.7.1' 2017-05-16 11:34:33 +03:00
yuri
a10f2949a4 Merge branch 'hotfix/4.7.1' 2017-05-16 11:30:52 +03:00
yuri
666d8ab5ad Merge branch 'hotfix/4.7.1' 2017-05-16 10:55:34 +03:00
yuri
bbcf9e00fa notification number 2017-05-15 16:54:34 +03:00
544 changed files with 17819 additions and 2290 deletions

View File

@@ -0,0 +1,78 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Utils as Utils;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
class LabelManager extends \Espo\Core\Controllers\Base
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
public function postActionGetScopeList($params)
{
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
return $labelManager->getScopeList();
}
public function postActionGetScopeData($params, $data, $request)
{
if (empty($data['scope']) || empty($data['language'])) {
throw new BadRequest();
}
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
return $labelManager->getScopeData($data['language'], $data['scope']);
}
public function postActionSaveLabels($params, $data)
{
if (empty($data['scope']) || empty($data['language']) || !isset($data['labels'])) {
throw new BadRequest();
}
$labels = get_object_vars($data['labels']);
$labelManager = $this->getContainer()->get('injectableFactory')->createByClassName('\\Espo\\Core\\Utils\\LabelManager');
$returnData = $labelManager->saveLabels($data['language'], $data['scope'], $labels);
$this->getContainer()->get('dataManager')->clearCache();
return $returnData;
}
}

View File

@@ -76,7 +76,11 @@ class Settings extends \Espo\Core\Controllers\Base
throw new BadRequest();
}
if (isset($data['useCache']) && $data['useCache'] != $this->getConfig()->get('useCache')) {
if (
(isset($data['useCache']) && $data['useCache'] != $this->getConfig()->get('useCache'))
||
(isset($data['aclStrictMode']) && $data['aclStrictMode'] !== $this->getConfig()->get('aclStrictMode'))
) {
$this->getContainer()->get('dataManager')->clearCache();
}

View File

@@ -77,6 +77,8 @@ class Table
protected $forbiddenFieldsCache = array();
protected $isStrictMode = false;
public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManagerUtil $fieldManager = null)
{
$this->data = (object) [
@@ -85,6 +87,8 @@ class Table
'fieldTableQuickAccess' => (object) [],
];
$this->isStrictMode = $config->get('aclStrictMode', false);
$this->user = $user;
$this->metadata = $metadata;
@@ -135,11 +139,6 @@ class Table
return $this->fieldManager;
}
protected function getConfig()
{
return $this->config;
}
public function getMap()
{
return $this->data;
@@ -454,7 +453,11 @@ class Table
$aclType = $this->defaultAclType;
}
if (!empty($aclType)) {
$defaultValue = $this->metadata->get('app.'.$this->type.'.scopeLevelTypesDefaults.' . $aclType, $this->metadata->get('app.'.$this->type.'.scopeLevelTypesDefaults.record'));
$paramDefaultsName = 'scopeLevelTypesDefaults';
if ($this->isStrictMode) {
$paramDefaultsName = 'scopeLevelTypesStrictDefaults';
}
$defaultValue = $this->metadata->get(['app', $this->type, $paramDefaultsName, $aclType], $this->metadata->get(['app', $this->type, $paramDefaultsName, 'record']));
if (is_array($defaultValue)) {
$defaultValue = (object) $defaultValue;
}

View File

@@ -134,7 +134,7 @@ class Application
public function runCron()
{
$auth = $this->createAuth();
$auth->useNoAuth(true);
$auth->useNoAuth();
$cronManager = new \Espo\Core\CronManager($this->container);
$cronManager->run();
@@ -327,6 +327,7 @@ class Application
public function setupSystemUser()
{
$user = $this->getContainer()->get('entityManager')->getEntity('User', 'system');
$user->set('isAdmin', true);
$this->getContainer()->setUser($user);
$this->getContainer()->get('entityManager')->setUser($user);
}

View File

@@ -93,6 +93,24 @@ class Container
return $className;
}
protected function loadContainer()
{
return $this;
}
protected function loadSlim()
{
return new \Espo\Core\Utils\Api\Slim();
}
protected function loadFileStorageManager()
{
return new \Espo\Core\FileStorage\Manager(
$this->get('metadata')->get(['app', 'fileStorage', 'implementationClassNameMap']),
$this
);
}
protected function loadLog()
{
$config = $this->get('config');
@@ -118,24 +136,6 @@ class Container
return $log;
}
protected function loadContainer()
{
return $this;
}
protected function loadSlim()
{
return new \Espo\Core\Utils\Api\Slim();
}
protected function loadFileStorageManager()
{
return new \Espo\Core\FileStorage\Manager(
$this->get('metadata')->get(['app', 'fileStorage', 'implementationClassNameMap']),
$this
);
}
protected function loadFileManager()
{
return new \Espo\Core\Utils\File\Manager(

View File

@@ -60,9 +60,9 @@ class ControllerManager
public function process($controllerName, $actionName, $params, $data, $request)
{
$customeClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName);
if (class_exists($customeClassName)) {
$controllerClassName = $customeClassName;
$customClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($controllerName);
if (class_exists($customClassName)) {
$controllerClassName = $customClassName;
} else {
$moduleName = $this->metadata->getScopeModuleName($controllerName);
if ($moduleName) {

View File

@@ -283,24 +283,20 @@ class CronManager
}
try {
$previousDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s');
$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s');
} catch (\Exception $e) {
$GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob['id'].']): Unsupported CRON expression ['.$scheduling.']');
continue;
}
if ($cronExpression->isDue()) {
$previousDate = date('Y-m-d H:i:s');
}
$existingJob = $this->getCronJob()->getJobByScheduledJob($scheduledJob['id'], $previousDate);
$existingJob = $this->getCronJob()->getJobByScheduledJob($scheduledJob['id'], $nextDate);
if ($existingJob) continue;
$className = $this->getScheduledJobUtil()->get($scheduledJob['job']);
if ($className) {
if (method_exists($className, 'prepare')) {
$implementation = new $className($this->container);
$implementation->prepare($scheduledJob, $previousDate);
$implementation->prepare($scheduledJob, $nextDate);
continue;
}
}
@@ -314,7 +310,7 @@ class CronManager
'name' => $scheduledJob['name'],
'status' => self::PENDING,
'scheduledJobId' => $scheduledJob['id'],
'executeTime' => $previousDate,
'executeTime' => $nextDate,
'method' => $scheduledJob['job']
));
$this->getEntityManager()->saveEntity($jobEntity);

View File

@@ -195,9 +195,9 @@ class Xlsx extends \Espo\Core\Injectable
$label = $name;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreigScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreigScope) {
$label = $this->getInjection('language')->translate($linkName, 'links', $entityType) . '.' . $this->getInjection('language')->translate($foreignField, 'fields', $foreigScope);
$foreignScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$label = $this->getInjection('language')->translate($linkName, 'links', $entityType) . '.' . $this->getInjection('language')->translate($foreignField, 'fields', $foreignScope);
}
} else {
$label = $this->getInjection('language')->translate($name, 'fields', $entityType);
@@ -205,13 +205,7 @@ class Xlsx extends \Espo\Core\Injectable
$sheet->setCellValue($col . $rowNumber, $label);
$sheet->getColumnDimension($col)->setAutoSize(true);
if (
$defs['type'] == 'phone'
|| $defs['type'] == 'email'
|| $defs['type'] == 'url'
|| $defs['type'] == 'link'
|| $defs['type'] == 'linkParent'
) {
if (in_array($defs['type'], ['phone', 'email', 'url', 'link', 'linkParent'])) {
$linkColList[] = $col;
} else if ($name == 'name') {
$linkColList[] = $col;
@@ -231,6 +225,8 @@ class Xlsx extends \Espo\Core\Injectable
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($headerStyle);
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
$typesCache = array();
$rowNumber++;
foreach ($dataList as $row) {
$i = 0;
@@ -242,18 +238,31 @@ class Xlsx extends \Espo\Core\Injectable
$defs = array();
$defs['type'] = 'base';
}
$type = $defs['type'];
$foreignField = $name;
$linkName = null;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreignScope = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$type = $this->getInjection('metadata')->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
}
}
$typesCache[$name] = $type;
$link = null;
if ($defs['type'] == 'link') {
if ($type == 'link') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($defs['type'] == 'linkParent') {
} else if ($type == 'linkParent') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($defs['type'] == 'int') {
} else if ($type == 'int') {
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
} else if ($defs['type'] == 'currency') {
} else if ($type == 'currency') {
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
$currency = $row[$name . 'Currency'];
@@ -263,7 +272,7 @@ class Xlsx extends \Espo\Core\Injectable
->getNumberFormat()
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
}
} else if ($defs['type'] == 'currencyConverted') {
} else if ($type == 'currencyConverted') {
if (array_key_exists($name, $row)) {
$currency = $this->getConfig()->get('baseCurrency');
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
@@ -274,7 +283,7 @@ class Xlsx extends \Espo\Core\Injectable
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
}
} else if ($defs['type'] == 'personName') {
} else if ($type == 'personName') {
if (!empty($row['name'])) {
$sheet->setCellValue("$col$rowNumber", $row['name']);
} else {
@@ -290,15 +299,15 @@ class Xlsx extends \Espo\Core\Injectable
}
$sheet->setCellValue("$col$rowNumber", $personName);
}
} else if ($defs['type'] == 'date') {
} else if ($type == 'date') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($defs['type'] == 'datetime' || $defs['type'] == 'datetimeOptional') {
} else if ($type == 'datetime' || $type == 'datetimeOptional') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($defs['type'] == 'image') {
} else if ($type == 'image') {
if (isset($row[$name . 'Id']) && $row[$name . 'Id']) {
$attachment = $this->getEntityManager()->getEntity('Attachment', $row[$name . 'Id']);
@@ -316,13 +325,17 @@ class Xlsx extends \Espo\Core\Injectable
}
}
} else if ($defs['type'] == 'file') {
} else if ($type == 'file') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($defs['type'] == 'enum') {
} else if ($type == 'enum') {
if (array_key_exists($name, $row)) {
$value = $this->getInjection('language')->translateOption($row[$name], $name, $entityType);
if ($linkName) {
$value = $this->getInjection('language')->translateOption($row[$name], $foreignField, $foreignScope);
} else {
$value = $this->getInjection('language')->translateOption($row[$name], $name, $entityType);
}
$sheet->setCellValue("$col$rowNumber", $value);
}
} else {
@@ -337,30 +350,30 @@ class Xlsx extends \Espo\Core\Injectable
if (array_key_exists('id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
}
} else if ($defs['type'] == 'url') {
} else if ($type == 'url') {
if (array_key_exists($name, $row) && filter_var($row[$name], FILTER_VALIDATE_URL)) {
$link = $row[$name];
}
} else if ($defs['type'] == 'link') {
} else if ($type == 'link') {
if (array_key_exists($name.'Id', $row)) {
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
if ($foreignEntity) {
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
}
}
} else if ($defs['type'] == 'file') {
} else if ($type == 'file') {
if (array_key_exists($name.'Id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/?entryPoint=download&id=" . $row[$name.'Id'];
}
} else if ($defs['type'] == 'linkParent') {
} else if ($type == 'linkParent') {
if (array_key_exists($name.'Id', $row) && array_key_exists($name.'Type', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$row[$name.'Type']."/view/". $row[$name.'Id'];
}
} else if ($defs['type'] == 'phone') {
} else if ($type == 'phone') {
if (array_key_exists($name, $row)) {
$link = "tel:".$row[$name];
}
} else if ($defs['type'] == 'email' && array_key_exists($name, $row)) {
} else if ($type == 'email' && array_key_exists($name, $row)) {
if (array_key_exists($name, $row)) {
$link = "mailto:".$row[$name];
}
@@ -373,52 +386,48 @@ class Xlsx extends \Espo\Core\Injectable
$rowNumber++;
}
$sheet->getStyle("A2:A$rowNumber")
->getNumberFormat()
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
$startingRowNumber = 4;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs['type'] = 'base';
}
$type = $typesCache[$name];
if ($col == 'A') {
$sheet->getStyle("A2:A$rowNumber")
->getNumberFormat()
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
} else {
switch($defs['type']) {
case 'currency':
case 'currencyConverted': {
} break;
case 'int': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('0');
} break;
case 'date': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateFormat());
} break;
case 'datetime': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
case 'datetimeOptional': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
default: {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('@');
} break;
}
switch($type) {
case 'currency':
case 'currencyConverted': {
} break;
case 'int': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('0');
} break;
case 'date': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateFormat());
} break;
case 'datetime': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
case 'datetimeOptional': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->getInjection('dateTime')->getDateTimeFormat());
} break;
default: {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('@');
} break;
}
}

View File

@@ -42,6 +42,11 @@ class ExtensionManager extends Upgrades\Base
'after' => 'AfterInstall',
'beforeUninstall' => 'BeforeUninstall',
'afterUninstall' => 'AfterUninstall',
),
'customDirNames' => array(
'before' => 'beforeInstallFiles',
'after' => 'afterInstallFiles',
)
);
}

View File

@@ -0,0 +1,50 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Formula\Functions\ArrayGroup;
use \Espo\Core\Exceptions\Error;
class LengthType extends \Espo\Core\Formula\Functions\Base
{
public function process(\StdClass $item)
{
if (!property_exists($item, 'value')) {
throw new Error();
}
$list = $this->evaluate($item->value[0]);
if (!is_array($list)) {
return 0;
}
return count($list);
}
}

View File

@@ -54,18 +54,15 @@ class DayOfWeekType extends \Espo\Core\Formula\Functions\Base
$value = $this->evaluate($item->value[0]);
if (empty($value)) return 0;
if (empty($value)) return -1;
if (strlen($value) > 11) {
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'D');
$resultString = $this->getInjection('dateTime')->convertSystemDateTime($value, null, 'e');
} else {
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'D');
$resultString = $this->getInjection('dateTime')->convertSystemDate($value, 'e');
}
$result = intval($resultString) + 1;
if ($result == 8) {
$result = 1;
}
$result = intval($resultString);
return $result;
}

View File

@@ -393,6 +393,9 @@ class Parser
{
$functionContent = trim($functionContent);
$isString = false;
$isSingleQuote = false;
if ($functionContent === '') {
return [];
}
@@ -400,13 +403,35 @@ class Parser
$commaIndexList = [];
$braceCounter = 0;
for ($i = 0; $i < strlen($functionContent); $i++) {
if ($functionContent[$i] === '(') {
$braceCounter++;
} else if ($functionContent[$i] === ')') {
$braceCounter--;
if ($functionContent[$i] === "'" && ($i === 0 || $functionContent[$i - 1] !== "\\")) {
if (!$isString) {
$isString = true;
$isSingleQuote = true;
} else {
if ($isSingleQuote) {
$isString = false;
}
}
} else if ($functionContent[$i] === "\"" && ($i === 0 || $functionContent[$i - 1] !== "\\")) {
if (!$isString) {
$isString = true;
$isSingleQuote = false;
} else {
if (!$isSingleQuote) {
$isString = false;
}
}
}
if ($braceCounter === 0 && $functionContent[$i] === ',') {
if (!$isString) {
if ($functionContent[$i] === '(') {
$braceCounter++;
} else if ($functionContent[$i] === ')') {
$braceCounter--;
}
}
if ($braceCounter === 0 && !$isString && $functionContent[$i] === ',') {
$commaIndexList[] = $i;
}
}

View File

@@ -49,6 +49,17 @@ abstract class Injectable implements \Espo\Core\Interfaces\Injectable
{
}
public function __call($methodName, $args)
{
if (strpos($methodName, 'get') === 0) {
$injectionName = lcfirst(substr($methodName, 3));
if (in_array($injectionName, $this->dependencyList)) {
return $this->getInjection($injectionName);
}
}
throw new \BadMethodCallException('Method ' . $methodName . ' does not exist');
}
protected function getInjection($name)
{
return $this->injections[$name];

View File

@@ -45,7 +45,12 @@ class EntityManager extends Base
'metadata' => $this->getContainer()->get('ormMetadata')->getData(),
'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory',
'driver' => $config->get('database.driver'),
'platform' => $config->get('database.platform')
'platform' => $config->get('database.platform'),
'sslCA' => $config->get('database.sslCA'),
'sslCert' => $config->get('database.sslCert'),
'sslKey' => $config->get('database.sslKey'),
'sslCAPath' => $config->get('database.sslCAPath'),
'sslCipher' => $config->get('database.sslCipher')
);
$entityManager = new \Espo\Core\ORM\EntityManager($params);

View File

@@ -203,8 +203,10 @@ class Importer
} catch (\Exception $e) {}
}
$inlineAttachmentList = [];
if (!$fetchOnlyHeader) {
$parser->fetchContentParts($email, $message);
$parser->fetchContentParts($email, $message, $inlineAttachmentList);
if ($this->getFiltersMatcher()->match($email, $filterList)) {
return false;
@@ -299,6 +301,14 @@ class Importer
$this->getEntityManager()->saveEntity($email);
foreach ($inlineAttachmentList as $attachment) {
$attachment->set(array(
'relatedId' => $email->id,
'relatedType' => 'Email'
));
$this->getEntityManager()->saveEntity($attachment);
}
return $email;
}
@@ -314,11 +324,10 @@ class Importer
$email->set('parentId', $contact->get('accountId'));
return true;
}
} else {
$email->set('parentType', 'Contact');
$email->set('parentId', $contact->id);
return true;
}
$email->set('parentType', 'Contact');
$email->set('parentId', $contact->id);
return true;
} else {
$account = $this->getEntityManager()->getRepository('Account')->where(array(
'emailAddress' => $emailAddress

View File

@@ -143,7 +143,7 @@ class MailMimeParser
return $addressList;
}
public function fetchContentParts(\Espo\Entities\Email $email, $message)
public function fetchContentParts(\Espo\Entities\Email $email, $message, &$inlineAttachmentList = [])
{
$this->loadContent($message);
@@ -202,6 +202,7 @@ class MailMimeParser
if ($contentId) {
$inlineIds[$contentId] = $attachment->id;
}
$inlineAttachmentList[] = $attachment;
}
}

View File

@@ -133,7 +133,7 @@ class PhpMimeMailParser
return $addressList;
}
public function fetchContentParts(\Espo\Entities\Email $email, $message)
public function fetchContentParts(\Espo\Entities\Email $email, $message, &$inlineAttachmentList = [])
{
$this->loadContent($message);
@@ -187,6 +187,7 @@ class PhpMimeMailParser
if ($contentId) {
$inlineIds[$contentId] = $attachment->id;
}
$inlineAttachmentList[] = $attachment;
}
}

View File

@@ -125,7 +125,7 @@ class ZendMail
return $addressList;
}
public function fetchContentParts(\Espo\Entities\Email $email, $message)
public function fetchContentParts(\Espo\Entities\Email $email, $message, &$inlineAttachmentList = [])
{
$zendMessage = $message->getZendMessage();
@@ -133,10 +133,10 @@ class ZendMail
if ($zendMessage->isMultipart()) {
foreach (new \RecursiveIteratorIterator($zendMessage) as $part) {
$this->importPartDataToEmail($email, $part, $inlineIds);
$this->importPartDataToEmail($email, $part, $inlineIds, null, $inlineAttachmentList);
}
} else {
$this->importPartDataToEmail($email, $zendMessage, $inlineIds, 'text/plain');
$this->importPartDataToEmail($email, $zendMessage, $inlineIds, 'text/plain', $inlineAttachmentList);
}
if (!$email->get('body') && $email->get('bodyPlain')) {
@@ -156,7 +156,7 @@ class ZendMail
}
}
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null, &$inlineAttachmentList = [])
{
try {
$type = null;
@@ -275,6 +275,7 @@ class ZendMail
}
} else if ($disposition == 'inline') {
$inlineIds[$contentId] = $attachment->id;
$inlineAttachmentList[] = $attachment;
}
}
} catch (\Exception $e) {}

View File

@@ -47,6 +47,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
private $restoreData = null;
protected $hooksDisabled = false;
protected $processFieldsAfterSaveDisabled = false;
protected function addDependency($name)
{
$this->dependencies[] = $name;
@@ -172,7 +176,9 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
protected function beforeRemove(Entity $entity, array $options = array())
{
parent::beforeRemove($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeRemove', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeRemove', $entity, $options);
}
$nowString = date('Y-m-d H:i:s', time());
if ($entity->hasAttribute('modifiedAt')) {
@@ -186,25 +192,25 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
protected function afterRemove(Entity $entity, array $options = array())
{
parent::afterRemove($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
}
}
protected function afterMassRelate(Entity $entity, $relationName, array $params = array(), array $options = array())
{
$hookData = array(
'relationName' => $relationName,
'relationParams' => $params
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterMassRelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'relationParams' => $params
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterMassRelate', $entity, $options, $hookData);
}
}
public function remove(Entity $entity, array $options = array())
{
$result = parent::remove($entity, $options);
if ($result) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRemove', $entity, $options);
}
return $result;
}
@@ -214,12 +220,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'relationData' => $data,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'relationData' => $data,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
}
}
}
@@ -229,11 +237,13 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
if ($foreign instanceof Entity) {
$foreignEntity = $foreign;
$hookData = array(
'relationName' => $relationName,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
if (!$this->hooksDisabled) {
$hookData = array(
'relationName' => $relationName,
'foreignEntity' => $foreignEntity
);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
}
}
}
@@ -241,7 +251,9 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
{
parent::beforeSave($entity, $options);
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'beforeSave', $entity, $options);
}
}
protected function afterSave(Entity $entity, array $options = array())
@@ -252,12 +264,16 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
}
parent::afterSave($entity, $options);
$this->processEmailAddressSave($entity);
$this->processPhoneNumberSave($entity);
$this->processSpecifiedRelationsSave($entity);
$this->processFileFieldsSave($entity);
if (!$this->processFieldsAfterSaveDisabled) {
$this->processEmailAddressSave($entity);
$this->processPhoneNumberSave($entity);
$this->processSpecifiedRelationsSave($entity);
$this->processFileFieldsSave($entity);
}
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterSave', $entity, $options);
if (!$this->hooksDisabled) {
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterSave', $entity, $options);
}
}
public function save(Entity $entity, array $options = array())
@@ -324,6 +340,24 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
));
$this->getEntityManager()->saveEntity($attachment);
}
if (!$entity->isNew()) {
foreach ($this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields']) as $name => $defs) {
if (!empty($defs['type']) && in_array($defs['type'], ['file', 'image'])) {
$attribute = $name . 'Id';
if ($entity->isAttributeChanged($attribute)) {
$previousAttachmentId = $entity->getFetched($attribute);
if ($previousAttachmentId) {
$attachment = $this->getEntityManager()->getEntity('Attachment', $previousAttachmentId);
if ($attachment) {
$this->getEntityManager()->removeEntity($attachment);
}
}
}
}
}
}
}
protected function processEmailAddressSave(Entity $entity)
@@ -405,8 +439,16 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
} else {
if (!empty($columns)) {
foreach ($columns as $columnName => $columnField) {
if (isset($columnData->$id)) {
if ($columnData->$id->$columnName !== $existingColumnsData->$id->$columnName) {
if (isset($columnData->$id) && is_object($columnData->$id)) {
if (
property_exists($columnData->$id, $columnName)
&&
(
!property_exists($existingColumnsData->$id, $columnName)
||
$columnData->$id->$columnName !== $existingColumnsData->$id->$columnName
)
) {
$toUpdateIds[] = $id;
}
}

View File

@@ -36,8 +36,20 @@ class Event extends \Espo\Core\ORM\Repositories\RDB
{
protected $reminderDateAttribute = 'dateStart';
protected $reminderSkippingStatusList = ['Held', 'Not Held'];
protected function beforeSave(Entity $entity, array $options = array())
{
if ($entity->isAttributeChanged('status') && in_array($entity->get('status'), $this->reminderSkippingStatusList)) {
$entity->set('reminders', []);
}
parent::beforeSave($entity, $options);
}
protected function afterRemove(Entity $entity, array $options = array())
{
parent::afterRemove($entity, $options);
$pdo = $this->getEntityManager()->getPDO();
$sql = "
DELETE FROM `reminder`

View File

@@ -188,6 +188,8 @@ class Base
$whereClause = array();
foreach ($where as $item) {
if (!isset($item['type'])) continue;
if ($item['type'] == 'bool' && !empty($item['value']) && is_array($item['value'])) {
foreach ($item['value'] as $filter) {
$p = $this->getBoolFilterWhere($filter);
@@ -210,6 +212,8 @@ class Base
$additionalFilters = array();
foreach ($where as $item) {
if (!isset($item['type'])) continue;
$type = $item['type'];
if (!in_array($type, $ignoreTypeList)) {
$part = $this->getWherePart($item, $result);
@@ -817,7 +821,7 @@ class Base
$dt->setTime(0, 0, 0);
$dt->setTimezone(new \DateTimeZone('UTC'));
$from = $dt->format($format);
$dt->modify('+1 day');
$dt->modify('+1 day -1 second');
$to = $dt->format($format);
$where['value'] = [$from, $to];
break;
@@ -907,8 +911,7 @@ class Base
$dt = new \DateTime($value, new \DateTimeZone($timeZone));
$dt->setTimezone(new \DateTimeZone('UTC'));
$from = $dt->format($format);
$dt->modify('+1 day');
$dt->modify('+1 day -1 second');
$to = $dt->format($format);
$where['value'] = [$from, $to];
break;
@@ -933,6 +936,7 @@ class Base
$dt = new \DateTime($value[1], new \DateTimeZone($timeZone));
$dt->setTimezone(new \DateTimeZone('UTC'));
$dt->modify('-1 second');
$to = $dt->format($format);
$where['value'] = [$from, $to];
@@ -1002,6 +1006,9 @@ class Base
case 'like':
$part[$attribute . '*'] = $item['value'];
break;
case 'notLike':
$part[$attribute . '!*'] = $item['value'];
break;
case 'equals':
case 'on':
$part[$attribute . '='] = $item['value'];
@@ -1015,6 +1022,9 @@ class Base
case 'contains':
$part[$attribute . '*'] = '%' . $item['value'] . '%';
break;
case 'notContains':
$part[$attribute . '!*'] = '%' . $item['value'] . '%';
break;
case 'notEquals':
case 'notOn':
$part[$attribute . '!='] = $item['value'];
@@ -1031,7 +1041,7 @@ class Base
$part[$attribute . '>='] = $item['value'];
break;
case 'lessThanOrEquals':
$part[$attribute . '<'] = $item['value'];
$part[$attribute . '<='] = $item['value'];
break;
case 'in':
$part[$attribute . '='] = $item['value'];
@@ -1162,16 +1172,49 @@ class Base
);
}
break;
case 'columnLike':
case 'columnIn':
case 'columnIsNull':
case 'columnNotIn':
$link = $this->getMetadata()->get(['entityDefs', $this->entityType, 'fields', $attribute, 'link']);
$column = $this->getMetadata()->get(['entityDefs', $this->entityType, 'fields', $attribute, 'column']);
$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') {
$part[$columnKey] = $value;
} else if ($item['type'] === 'columnNotIn') {
$part[$columnKey . '!='] = $value;
} else if ($item['type'] === 'columnIsNull') {
$part[$columnKey] = null;
} else if ($item['type'] === 'columnIsNotNull') {
$part[$columnKey . '!='] = null;
} else if ($item['type'] === 'columnLike') {
$part[$columnKey . '*'] = $value;
} else if ($item['type'] === 'columnStartsWith') {
$part[$columnKey . '*'] = $value . '%';
} else if ($item['type'] === 'columnEndsWith') {
$part[$columnKey . '*'] = '%' . $value;
} else if ($item['type'] === 'columnContains') {
$part[$columnKey . '*'] = '%' . $value . '%';
} else if ($item['type'] === 'columnEquals') {
$part[$columnKey . '='] = $value;
} else if ($item['type'] === 'columnNotEquals') {
$part[$columnKey . '!='] = $value;
}
break;
case 'isNotLinked':
if (!$result) break;
$alias = $attribute . 'IsNotLinkedFilter' . strval(rand(10000, 99999));;
$alias = $attribute . 'IsNotLinkedFilter' . strval(rand(10000, 99999));
$part[$alias . '.id'] = null;
$this->setDistinct(true, $result);
$this->addLeftJoin([$attribute, $alias], $result);
break;
case 'isLinked':
if (!$result) break;
$alias = $attribute . 'IsLinkedFilter' . strval(rand(10000, 99999));;
$alias = $attribute . 'IsLinkedFilter' . strval(rand(10000, 99999));
$part[$alias . '.id!='] = null;
$this->setDistinct(true, $result);
$this->addLeftJoin([$attribute, $alias], $result);

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Billing Address",
"shippingAddress": "Shipping Address",
"website": "Website"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Parent",
"dateStart": "Date Start",
"dateEnd": "Date End",
"duration": "Duration",
"status": "Status",
"reminders": "Reminders"
},
"links": {
"parent": "Parent"
},
"options": {
"status": {
"Planned": "Planned",
"Held": "Held",
"Not Held": "Not Held"
}
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}",
"Schedule {entityType}": "Schedule {entityTypeTranslated}",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held"
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Set Not Held"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Address"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Create {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Juntas",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Dirección de Facturación",
"shippingAddress": "Dirección de envio",
"website": "Sitio Web"
},
"links": {
"meetings": "reuniones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Padre",
"dateStart": "Fecha de incio",
"dateEnd": "Fecha final",
"duration": "Duracion",
"status": "Estátus",
"reminders": "Recordatorios"
},
"links": {
"parent": "Padre"
},
"options": {
"status": {
"Planned": "Planeado",
"Held": "Retenidas",
"Not Held": "Pendientes"
}
},
"labels": {
"Create {entityType}": "Creat",
"Schedule {entityType}": "Agendar {entityTypeTranslated}",
"Log {entityType}": "Registrar {entityTypeTranslated}",
"Set Held": "Marcar como Retenida",
"Set Not Held": "Marcar como Pendiente"
},
"massActions": {
"setHeld": "Marcar como Retenida",
"setNotHeld": "Marcar como Pendiente"
},
"presetFilters": {
"planned": "Planeado",
"held": "Retenida",
"todays": "Hoy"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Dirección"
},
"links": {
"meetings": "Reuniones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create {entityType}": "Crear {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,5 @@
{
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,10 @@
{
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}
"links": {
"meetings": "Meeting",
"calls": "Chiamate",
"tasks": "Task"
},
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,15 @@
{
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}
"fields": {
"billingAddress": "Indirizzo di Fatturazione",
"shippingAddress": "Indirizzo di Spedizione",
"website": "Website"
},
"links": {
"meetings": "Meetings",
"calls": "Chiamate",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}

View File

@@ -1,5 +1,36 @@
{
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
"fields": {
"parent": "Parente",
"dateStart": "Data Inizio",
"dateEnd": "Data Termine",
"duration": "Durata",
"status": "Stato",
"reminders": "Avvisi"
},
"links": {
"parent": "Parente"
},
"options": {
"status": {
"Planned": "Pianificato",
"Held": "Held",
"Not Held": "Not Held"
}
}
},
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}",
"Schedule {entityType}": "Programma {entityTypeTranslated}",
"Log {entityType}": "Log {entityTypeTranslated}",
"Set Held": "Imposta Held",
"Set Not Held": "Imposta Not Held"
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Imposta Not Held"
},
"presetFilters": {
"planned": "Pianificato",
"held": "Held",
"todays": "Oggi"
}
}

View File

@@ -1,5 +1,13 @@
{
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}
"fields": {
"address": "Indirizzo"
},
"links": {
"meetings": "Meetings",
"calls": "Chiamate",
"tasks": "Tasks"
},
"labels": {
"Create {entityType}": "Crea {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Ročišta",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,15 @@
{
"fields": {
"billingAddress": "Adresa za naplatu",
"shippingAddress": "Adresa za dostavu",
"website": "Sajt"
},
"links": {
"meetings": "Ročišta",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -0,0 +1,36 @@
{
"fields": {
"parent": "Roditelj",
"dateStart": "Početni datum",
"dateEnd": "Krajnji datum",
"duration": "Trajanje",
"status": "Status",
"reminders": "Podsetnici"
},
"links": {
"parent": "Roditelj"
},
"options": {
"status": {
"Planned": "Planiran",
"Held": "Održan",
"Not Held": "Nije održan"
}
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}",
"Schedule {entityType}": "Zakaži {entityTypeTranslated}",
"Log {entityType}": "Zabeleži {entitiTipeTranslated}",
"Set Held": "Postavi kao održano",
"Set Not Held": "Postavi kao nije održano"
},
"massActions": {
"setHeld": "Postavi kao održano",
"setNotHeld": "Postavi kao nije održano"
},
"presetFilters": {
"planned": "Planiran",
"held": "Održan",
"todays": "Današnji"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"address": "Adresa"
},
"links": {
"meetings": "Ročišta",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create {entityType}": "Napravi {entityTypeTranslated}"
}
}

View File

@@ -42,6 +42,11 @@ class UpgradeManager extends Upgrades\Base
'scriptNames' => array(
'before' => 'BeforeUpgrade',
'after' => 'AfterUpgrade',
),
'customDirNames' => array(
'before' => 'beforeUpgradeFiles',
'after' => 'afterUpgradeFiles',
)
);
}

View File

@@ -389,9 +389,7 @@ abstract class Base
{
if (!isset($this->data['fileList'])) {
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, self::FILES);
$this->data['fileList'] = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
$this->data['fileList'] = $this->getFileList($packagePath);
}
return $this->data['fileList'];
@@ -401,14 +399,55 @@ abstract class Base
{
if (!isset($this->data['restoreFileList'])) {
$backupPath = $this->getPath('backupPath');
$backupFilePath = Util::concatPath($backupPath, self::FILES);
$this->data['restoreFileList'] = $this->getFileManager()->getFileList($backupFilePath, true, '', true, true);
$this->data['restoreFileList'] = $this->getFileList($backupPath);
}
return $this->data['restoreFileList'];
}
/**
* Get file directories (files, beforeInstallFiles, afterInstallFiles)
*
* @param sting $parentDirPath
*
* @return array
*/
protected function getFileDirs($parentDirPath = null)
{
$dirNames = $this->getParams('customDirNames');
$paths = array(self::FILES, $dirNames['before'], $dirNames['after']);
if (isset($parentDirPath)) {
foreach ($paths as &$path) {
$path = Util::concatPath($parentDirPath, $path);
}
}
return $paths;
}
/**
* Get file list from directories: files, beforeUpgradeFiles, afterUpgradeFiles
*
* @param string $dirPath
*
* @return array
*/
protected function getFileList($dirPath)
{
$fileList = array();
$paths = $this->getFileDirs($dirPath);
foreach ($paths as $filesPath) {
if (file_exists($filesPath)) {
$files = $this->getFileManager()->getFileList($filesPath, true, '', true, true);
$fileList = array_merge($fileList, $files);
}
}
return $fileList;
}
protected function copy($sourcePath, $destPath, $recursively = false, array $fileList = null, $copyOnlyFiles = false)
{
try {
@@ -423,15 +462,32 @@ abstract class Base
/**
* Copy files from upgrade/extension package
*
* @param string $processId
* @param string $type
*
* @return boolean
*/
protected function copyFiles()
protected function copyFiles($type = null)
{
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, self::FILES);
switch ($type) {
case 'before':
case 'after':
$dirNames = $this->getParams('customDirNames');
$dirPath = $dirNames[$type];
break;
return $this->copy($filesPath, '', true);
default:
$dirPath = self::FILES;
break;
}
$packagePath = $this->getPackagePath();
$filesPath = Util::concatPath($packagePath, $dirPath);
if (file_exists($filesPath)) {
return $this->copy($filesPath, '', true);
}
return true;
}
public function getManifest()

View File

@@ -67,6 +67,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$this->backupExistingFiles();
//beforeInstallFiles
if (!$this->copyFiles('before')) {
$this->throwErrorAndRemovePackage('Cannot copy beforeInstall files.');
}
/* run before install script */
$this->runScript('before');
@@ -85,6 +90,11 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
}
//afterInstallFiles
if (!$this->copyFiles('after')) {
$this->throwErrorAndRemovePackage('Cannot copy afterInstall files.');
}
/* run before install script */
$this->runScript('after');
@@ -112,7 +122,9 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$deleteFileList = array_diff($copyFileList, $backupFileList);
$res = $this->copy($backupFilePath, '', true);
$res &= $this->getFileManager()->remove($deleteFileList, null, true);
if (!empty($deleteFileList)) {
$res &= $this->getFileManager()->remove($deleteFileList, null, true);
}
if ($res) {
$this->getFileManager()->removeInDir($backupPath, true);

View File

@@ -95,15 +95,20 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
protected function restoreFiles()
{
$packagePath = $this->getPath('packagePath');
$filesPath = Util::concatPath($packagePath, self::FILES);
if (!file_exists($filesPath)) {
$manifestPath = Util::concatPath($packagePath, $this->manifestName);
if (!file_exists($manifestPath)) {
$this->unzipArchive($packagePath);
}
$res = $this->copy($filesPath, '', true);
$fileDirs = $this->getFileDirs($packagePath);
foreach ($fileDirs as $filesPath) {
if (file_exists($filesPath)) {
$res = $this->copy($filesPath, '', true);
}
}
$manifestJson = $this->getFileManager()->getContents(array($packagePath, $this->manifestName));
$manifestJson = $this->getFileManager()->getContents($manifestPath);
$manifest = Json::decode($manifestJson, true);
if (!empty($manifest['delete'])) {
$res &= $this->getFileManager()->remove($manifest['delete'], null, true);
@@ -114,7 +119,7 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
return $res;
}
protected function copyFiles()
protected function copyFiles($type = null)
{
$backupPath = $this->getPath('backupPath');
$res = $this->copy(array($backupPath, self::FILES), '', true);

View File

@@ -63,9 +63,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
/** copy scripts files */
$packagePath = $this->getPackagePath();
$res &= $this->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true);
return $res;
return $this->copy(array($packagePath, self::SCRIPTS), array($backupPath, self::SCRIPTS), true);
}
protected function isNew()

View File

@@ -99,7 +99,7 @@ class Auth
return $this->getContainer()->get('entityManager');
}
public function useNoAuth($isAdmin = false)
public function useNoAuth()
{
$entityManager = $this->getContainer()->get('entityManager');
@@ -108,7 +108,7 @@ class Auth
throw new Error("System user is not found");
}
$user->set('isAdmin', $isAdmin);
$user->set('isAdmin', true);
$user->set('ipAddress', $_SERVER['REMOTE_ADDR']);
$entityManager->setUser($user);

View File

@@ -28,6 +28,7 @@
************************************************************************/
namespace Espo\Core\Utils\Database\Orm;
use Espo\Core\Utils\Util;
class Base

View File

@@ -190,8 +190,14 @@ class Converter
public function afterProcess(array $ormMeta)
{
foreach($ormMeta as $entityName => &$entityParams) {
foreach($entityParams['fields'] as $fieldName => &$fieldParams) {
foreach ($ormMeta as $entityName => &$entityParams) {
foreach ($entityParams['fields'] as $fieldName => &$fieldParams) {
/* remove fields without type */
if (!isset($fieldParams['type']) && (!isset($fieldParams['notStorable']) || $fieldParams['notStorable'] === false)) {
unset($entityParams['fields'][$fieldName]);
continue;
}
switch ($fieldParams['type']) {
case 'id':
@@ -240,16 +246,16 @@ class Converter
$outputMeta = array(
'id' => array(
'type' => Entity::ID,
'dbType' => 'varchar',
'dbType' => 'varchar'
),
'name' => array(
'type' => isset($entityMeta['fields']['name']['type']) ? $entityMeta['fields']['name']['type'] : Entity::VARCHAR,
'notStorable' => true,
'notStorable' => true
),
'deleted' => array(
'type' => Entity::BOOL,
'default' => false,
),
'default' => false
)
);
foreach($entityMeta['fields'] as $fieldName => $fieldParams) {
@@ -307,14 +313,27 @@ class Converter
if (class_exists($className) && method_exists($className, 'load')) {
$helperClass = new $className($this->metadata, $ormMeta, $entityDefs);
$fieldResult = $helperClass->process( $fieldName, $entityName );
$fieldResult = $helperClass->process($fieldName, $entityName);
if (isset($fieldResult['unset'])) {
$ormMeta = Util::unsetInArray($ormMeta, $fieldResult['unset']);
unset($fieldResult['unset']);
}
$ormMeta = Util::merge($ormMeta, $fieldResult);
}
$defaultAttributes = $this->metadata->get(['entityDefs', $entityName, 'fields', $fieldName, 'defaultAttributes']);
if ($defaultAttributes && array_key_exists($fieldName, $defaultAttributes)) {
$defaultMetadataPart = array(
$entityName => array(
'fields' => array(
$fieldName => array(
'default' => $defaultAttributes[$fieldName]
)
)
)
);
$ormMeta = Util::merge($ormMeta, $defaultMetadataPart);
}
}
@@ -387,6 +406,10 @@ class Converter
$relationships = array();
foreach ($entityMeta['links'] as $linkName => $linkParams) {
if (isset($linkParams['skipOrmDefs']) && $linkParams['skipOrmDefs'] === true) {
continue;
}
$convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityName, $ormMeta);
if (isset($convertedLink)) {

View File

@@ -0,0 +1,82 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Orm\Fields;
class File extends Base
{
protected function load($fieldName, $entityName)
{
$fieldParams = $this->getFieldParams();
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => false
),
$fieldName.'Name' => array(
'type' => 'foreign'
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName
)
)
);
if (!empty($fieldParams['notStorable'])) {
$data[$entityName]['fields'][$fieldName.'Id']['notStorable'] = true;
$data[$entityName]['fields'][$fieldName.'Name']['type'] = 'varchar';
}
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Id', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Id']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Id'];
}
if (empty($fieldParams['notStorable'])) {
$data[$entityName]['fields'][$fieldName . 'Name']['relation'] = $fieldName;
$data[$entityName]['fields'][$fieldName . 'Name']['foreign'] = 'name';
$linkName = $fieldName;
$data[$entityName]['relations'] = array();
$data[$entityName]['relations'][$linkName] = array(
'type' => 'belongsTo',
'entity' => 'Attachment',
'key' => $linkName.'Id',
'foreignKey' => 'id',
'foreign' => null
);
}
return $data;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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\Orm\Fields;
class Image extends File
{
}

View File

@@ -40,24 +40,27 @@ class Link extends Base
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Name' => array(
'type' => 'varchar',
'notStorable' => true,
),
),
'notStorable' => true
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName,
),
),
'fields.'.$fieldName
)
)
);
if (!empty($fieldParams['notStorable'])) {
$data[$entityName]['fields'][$fieldName.'Id']['notStorable'] = true;
}
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Id', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Id']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Id'];
}
return $data;
}

View File

@@ -33,31 +33,42 @@ class LinkParent extends Base
{
protected function load($fieldName, $entityName)
{
return array(
$data = array(
$entityName => array (
'fields' => array(
$fieldName.'Id' => array(
'type' => 'foreignId',
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Type' => array(
'type' => 'foreignType',
'notNull' => false,
'index' => $fieldName,
'index' => $fieldName
),
$fieldName.'Name' => array(
'type' => 'varchar',
'notStorable' => true,
'relation' => $fieldName,
'isParentName' => true
),
),
)
)
),
'unset' => array(
$entityName => array(
'fields.'.$fieldName,
),
),
'fields.'.$fieldName
)
)
);
$fieldParams = $this->getFieldParams();
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Id', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Id']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Id'];
}
if (!empty($fieldParams['defaultAttributes']) && array_key_exists($fieldName.'Type', $fieldParams['defaultAttributes'])) {
$data[$entityName]['fields'][$fieldName.'Type']['default'] = $fieldParams['defaultAttributes'][$fieldName.'Type'];
}
return $data;
}
}

View File

@@ -38,6 +38,10 @@ class BelongsTo extends Base
$foreignEntityName = $this->getForeignEntityName();
$foreignLinkName = $this->getForeignLinkName();
$index = true;
if (!empty($linkParams['noIndex'])) {
$index = false;
}
$noForeignName = false;
if (!empty($linkParams['noForeignName'])) {
@@ -71,7 +75,7 @@ class BelongsTo extends Base
'fields' => array(
$linkName.'Id' => array(
'type' => 'foreignId',
'index' => true
'index' => $index
)
),
'relations' => array(

View File

@@ -251,16 +251,11 @@ class Converter
case 'manyMany':
$tableName = $relationParams['relationName'];
//check for duplication tables
//check for duplicate tables
if (!isset($tables[$tableName])) { //no needs to create the table if it already exists
$tables[$tableName] = $this->prepareManyMany($entityName, $relationParams, $tables);
}
break;
case 'belongsTo':
$columnName = Util::toUnderScore($relationParams['key']);
$tables[$entityName]->addIndex(array($columnName));
break;
}
}
}
@@ -440,13 +435,13 @@ class Converter
$dependentEntities[] = $entityName;
foreach ($ormMeta[$entityName]['relations'] as $relationName => $relationParams) {
if (isset($relationParams['entity'])) {
$relationEntity = $relationParams['entity'];
if (!in_array($relationEntity, $dependentEntities)) {
$dependentEntities = $this->getDependentEntities($relationEntity, $ormMeta, $dependentEntities);
if (array_key_exists('relations', $ormMeta[$entityName])) {
foreach ($ormMeta[$entityName]['relations'] as $relationName => $relationParams) {
if (isset($relationParams['entity'])) {
$relationEntity = $relationParams['entity'];
if (!in_array($relationEntity, $dependentEntities)) {
$dependentEntities = $this->getDependentEntities($relationEntity, $ormMeta, $dependentEntities);
}
}
}
}

View File

@@ -49,7 +49,7 @@ class EntityManager
private $container;
private $reservedWordList = ['__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor'];
private $reservedWordList = ['__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'common'];
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager, Config $config, Container $container = null)
{
@@ -102,6 +102,26 @@ class EntityManager
return $this->container->get('serviceFactory');
}
protected function checkControllerExists($name)
{
$controllerClassName = '\\Espo\\Custom\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
} else {
foreach ($this->getMetadata()->getModuleList() as $moduleName) {
$controllerClassName = '\\Espo\\Modules\\' . $moduleName . '\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
}
}
$controllerClassName = '\\Espo\\Controllers\\' . Util::normilizeClassName($name);
if (class_exists($controllerClassName)) {
return true;
}
}
return false;
}
public function create($name, $type, $params = array())
{
$name = ucfirst($name);
@@ -114,6 +134,10 @@ class EntityManager
throw new Error();
}
if ($this->checkControllerExists($name)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
}
$serviceFactory = $this->getServiceFactory();
if ($serviceFactory && $serviceFactory->checKExists($name)) {
throw new Conflict('Entity name \''.$name.'\' is not allowed.');

View File

@@ -0,0 +1,216 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 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;
use \Espo\Core\Utils\Util;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
class LabelManager extends \Espo\Core\Injectable
{
protected $dependencyList = [
'config',
'fileManager',
'metadata',
'defaultLanguage'
];
protected $ignoreList = [
'Global.sets'
];
public function getScopeList()
{
$scopeList = [];
$languageObj = $this->getDefaultLanguage();
$data = $languageObj->getAll();
foreach (array_keys($data) as $scope) {
if (!in_array($scope, $scopeList)) {
$scopeList[] = $scope;
}
}
foreach ($this->getMetadata()->get('scopes') as $scope => $data) {
if (!in_array($scope, $scopeList)) {
$scopeList[] = $scope;
}
}
return $scopeList;
}
public function getScopeData($language, $scope)
{
$languageObj = new Language($language, $this->getFileManager(), $this->getMetadata());
$data = $languageObj->get($scope);
if (empty($data)) {
return (object) [];
}
if ($this->getMetadata()->get(['scopes', $scope, 'entity'])) {
if (empty($data['fields'])) {
$data['fields'] = array();
}
foreach ($this->getMetadata()->get(['entityDefs', $scope, 'fields']) as $field => $item) {
if (!array_key_exists($field, $data['fields'])) {
$data['fields'][$field] = $languageObj->get('Global.fields.' . $field);
if (is_null($data['fields'][$field])) {
$data['fields'][$field] = '';
}
}
}
if (empty($data['links'])) {
$data['links'] = array();
}
foreach ($this->getMetadata()->get(['entityDefs', $scope, 'links']) as $link => $item) {
if (!array_key_exists($link, $data['links'])) {
$data['links'][$link] = $languageObj->get('Global.links.' . $link);
if (is_null($data['links'][$link])) {
$data['links'][$link] = '';
}
}
}
if (empty($data['labels'])) {
$data['labels'] = array();
}
if (!array_key_exists('Create ' . $scope, $data['labels'])) {
$data['labels']['Create ' . $scope] = '';
}
}
if ($scope === 'Global') {
if (empty($data['scopeNames'])) {
$data['scopeNames'] = array();
}
if (empty($data['scopeNamesPlural'])) {
$data['scopeNamesPlural'] = array();
}
foreach ($this->getMetadata()->get(['scopes']) as $scopeKey => $item) {
if (!empty($item['entity'])) {
if (empty($data['scopeNamesPlural'][$scopeKey])) {
$data['scopeNamesPlural'][$scopeKey] = '';
}
}
if (empty($data['scopeNames'][$scopeKey])) {
$data['scopeNames'][$scopeKey] = '';
}
}
}
foreach ($data as $key => $value) {
if (empty($value)) {
unset($data[$key]);
}
}
$finalData = array();
foreach ($data as $category => $item) {
if (in_array($scope . '.' . $category, $this->ignoreList)) continue;
foreach ($item as $key => $categoryItem) {
if (is_array($categoryItem)) {
foreach ($categoryItem as $subKey => $subItem) {
$finalData[$category][$category .'[.]' . $key .'[.]' . $subKey] = $subItem;
}
} else {
$finalData[$category][$category .'[.]' . $key] = $categoryItem;
}
}
}
return $finalData;
}
public function saveLabels($language, $scope, $labels)
{
$languageObj = new Language($language, $this->getFileManager(), $this->getMetadata());
$languageOriginalObj = new Language($language, $this->getFileManager(), $this->getMetadata(), false, true);
$returnDataHash = array();
foreach ($labels as $key => $value) {
$arr = explode('[.]', $key);
$category = $arr[0];
$name = $arr[1];
$setPath = [$scope, $category, $name];
$setValue = null;
if (count($arr) == 2) {
if ($value !== '') {
$languageObj->set($scope, $category, $name, $value);
$setValue = $value;
} else {
$setValue = $languageOriginalObj->get(implode('.', [$scope, $category, $name]));
if (is_null($setValue) && $scope !== 'Global') {
$setValue = $languageOriginalObj->get(implode('.', ['Global', $category, $name]));
}
$languageObj->delete($scope, $category, $name);
}
} else if (count($arr) == 3) {
$name = $arr[1];
$attribute = $arr[2];
$data = $languageObj->get($scope . '.' . $category . '.' . $name);
$setPath[] = $attribute;
if (is_array($data)) {
if ($value !== '') {
$data[$attribute] = $value;
$setValue = $value;
} else {
$dataOriginal = $languageOriginalObj->get($scope . '.' . $category . '.' . $name);
if (is_array($dataOriginal) && isset($dataOriginal[$attribute])) {
$data[$attribute] = $dataOriginal[$attribute];
$setValue = $dataOriginal[$attribute];
}
}
$languageObj->set($scope, $category, $name, $data);
}
}
if (!is_null($setValue)) {
$frontKey = implode('[.]', $setPath);
$returnDataHash[$frontKey] = $setValue;
}
}
$languageObj->save();
return $returnDataHash;
}
}

View File

@@ -61,13 +61,15 @@ class Language
protected $useCache = false;
protected $noCustom = false;
private $paths = array(
'corePath' => 'application/Espo/Resources/i18n',
'modulePath' => 'application/Espo/Modules/{*}/Resources/i18n',
'customPath' => 'custom/Espo/Custom/Resources/i18n',
);
public function __construct($language = null, File\Manager $fileManager, Metadata $metadata, $useCache = false)
public function __construct($language = null, File\Manager $fileManager, Metadata $metadata, $useCache = false, $noCustom = false)
{
if ($language) {
$this->currentLanguage = $language;
@@ -79,6 +81,7 @@ class Language
$this->metadata = $metadata;
$this->useCache = $useCache;
$this->noCustom = $noCustom;
$this->unifier = new \Espo\Core\Utils\File\Unifier($this->fileManager, $this->metadata);
}
@@ -345,7 +348,13 @@ class Language
protected function init($reload = false)
{
if ($reload || !file_exists($this->getLangCacheFile()) || !$this->useCache) {
$fullData = $this->getUnifier()->unify($this->name, $this->paths, true);
$paths = $this->paths;
if ($this->noCustom) {
unset($paths['customPath']);
}
$fullData = $this->getUnifier()->unify($this->name, $paths, true);
$result = true;
foreach ($fullData as $i18nName => $i18nData) {

View File

@@ -602,5 +602,18 @@ class Util
return $array;
}
/**
* Array keys exists
*
* @param array $keys
* @param array $array
*
* @return boolean
*/
static public function arrayKeysExists(array $keys, array $array)
{
return !array_diff_key(array_flip($keys), $array);
}
}

View File

@@ -63,7 +63,9 @@ return array (
'smtpUsername' => '',
'smtpPassword' => '',
'languageList' => [
'en_GB',
'en_US',
'es_MX',
'cs_CZ',
'da_DK',
'de_DE',
@@ -74,6 +76,7 @@ return array (
'nb_NO',
'nl_NL',
'tr_TR',
'sr_RS',
'ro_RO',
'ru_RU',
'pl_PL',
@@ -162,6 +165,7 @@ return array (
'cleanupAuthTokenPeriod' => '1 month',
'currencyFormat' => 1,
'currencyDecimalPlaces' => null,
'aclStrictMode' => false,
'isInstalled' => false
);

View File

@@ -115,6 +115,7 @@ return array ( 'defaultPermissions' =>
'smtpPassword',
'cron',
'authenticationMethod',
'adminPanelIframeUrl',
'ldapHost',
'ldapPort',
'ldapSecurity',

View File

@@ -124,7 +124,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getToList()
{
$value = $email->get('to');
$value = $this->get('to');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -136,7 +136,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getCcList()
{
$value = $email->get('cc');
$value = $this->get('cc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -148,7 +148,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getBccList()
{
$value = $email->get('bcc');
$value = $this->get('bcc');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {
@@ -160,7 +160,7 @@ class Email extends \Espo\Core\ORM\Entity
public function getReplyToList()
{
$value = $email->get('replyTo');
$value = $this->get('replyTo');
if ($value) {
$arr = explode(';', $value);
if (is_array($arr)) {

View File

@@ -36,7 +36,7 @@ use \Espo\Core\Exceptions\Error;
class Avatar extends Image
{
public static $authRequired = false;
public static $authRequired = true;
public static $notStrictAuth = true;
@@ -88,14 +88,7 @@ class Avatar extends Image
exit;
}
if (isset($_GET['attachmentId'])) {
$id = $_GET['attachmentId'];
if ($id == 'false') {
$id = false;
}
} else {
$id = $user->get('avatarId');
}
$id = $user->get('avatarId');
$size = null;
if (!empty($_GET['size'])) {

View File

@@ -75,7 +75,10 @@ class NextNumber extends \Espo\Core\Hooks\Base
'fieldName' => $fieldName,
'entityType' => $entity->getEntityType()
))->findOne();
if (!$nextNumber) continue;
if (!$nextNumber) {
$this->getEntityManager()->getPdo()->query('UNLOCK TABLES');
continue;
}
$entity->set($fieldName, $this->composeNumberAttribute($nextNumber));
$value = $nextNumber->get('value');

View File

@@ -45,6 +45,10 @@ class Cleanup extends \Espo\Core\Jobs\Base
protected $cleanupAttachmentsPeriod = '1 month';
protected $cleanupAttachmentsFromPeriod = '3 months';
protected $cleanupRemindersPeriod = '15 days';
public function run()
{
$this->cleanupJobs();
@@ -103,6 +107,19 @@ class Cleanup extends \Espo\Core\Jobs\Base
$sth->execute();
}
protected function cleanupReminders()
{
$period = '-' . $this->getConfig()->get('cleanupRemindersPeriod', $this->cleanupRemindersPeriod);
$datetime = new \DateTime();
$datetime->modify($period);
$query = "DELETE FROM `reminder` WHERE DATE(remind_at) < '" . $datetime->format('Y-m-d') . "'";
$pdo = $this->getEntityManager()->getPDO();
$sth = $pdo->prepare($query);
$sth->execute();
}
protected function cleanupAuthToken()
{
$period = '-' . $this->getConfig()->get('cleanupAuthTokenPeriod', $this->cleanupAuthTokenPeriod);
@@ -171,6 +188,57 @@ class Cleanup extends \Espo\Core\Jobs\Base
}
}
$fromPeriod = '-' . $this->getConfig()->get('cleanupAttachmentsFromPeriod', $this->cleanupAttachmentsFromPeriod);
$datetimeFrom = new \DateTime();
$datetimeFrom->modify($fromPeriod);
$scopeList = array_keys($this->getMetadata()->get(['scopes']));
foreach ($scopeList as $scope) {
if (!$this->getMetadata()->get(['scopes', $scope, 'entity'])) continue;
if (!$this->getMetadata()->get(['scopes', $scope, 'object']) && $scope !== 'Note') continue;
if (!$this->getMetadata()->get(['entityDefs', $scope, 'fields', 'modifiedAt'])) continue;
$hasAttachmentField = false;
if ($scope === 'Note') {
$hasAttachmentField = true;
}
if (!$hasAttachmentField) {
foreach ($this->getMetadata()->get(['entityDefs', $scope, 'fields']) as $field => $defs) {
if (empty($defs['type'])) continue;
if (in_array($defs['type'], ['file', 'image', 'attachmentMultiple'])) {
$hasAttachmentField = true;
break;
}
}
}
if (!$hasAttachmentField) continue;
$deletedEntityList = $this->getEntityManager()->getRepository($scope)->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(
'relatedType' => $scope,
'relatedId' => $deletedEntity->id
),
array(
'parentType' => $scope,
'parentId' => $deletedEntity->id
)
)
))->find();
foreach ($attachmentToRemoveList as $attachmentToRemove) {
$this->getEntityManager()->removeEntity($attachmentToRemove);
}
}
}
$sql = "DELETE FROM attachment WHERE deleted = 1 AND created_at < ".$pdo->quote($datetime->format('Y-m-d H:i:s'));
$sth = $pdo->query($sql);
}

View File

@@ -66,12 +66,20 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base
foreach ($collection as $entity) {
$running = $this->getEntityManager()->getRepository('Job')->where(array(
'scheduledJobId' => $data['id'],
'status' => ['Running', 'Pending'],
'status' => 'Running',
'targetType' => 'EmailAccount',
'targetId' => $entity->id
))->findOne();
if ($running) continue;
$countPending = $this->getEntityManager()->getRepository('Job')->where(array(
'scheduledJobId' => $data['id'],
'status' => 'Pending',
'targetType' => 'EmailAccount',
'targetId' => $entity->id
))->count();
if ($countPending > 1) continue;
$job = $this->getEntityManager()->getEntity('Job');
$jobEntity = $this->getEntityManager()->getEntity('Job');

View File

@@ -36,8 +36,6 @@ class Meeting extends \Espo\Core\Repositories\Event
{
protected function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
$parentId = $entity->get('parentId');
$parentType = $entity->get('parentType');
if (!empty($parentId) || !empty($parentType)) {
@@ -61,6 +59,27 @@ class Meeting extends \Espo\Core\Repositories\Event
}
}
if (!$entity->isNew()) {
if ($entity->isFieldChanged('dateStart') && $entity->isFieldChanged('dateStart') && !$entity->isFieldChanged('dateEnd')) {
$dateEndPrevious = $entity->getFetched('dateEnd');
$dateStartPrevious = $entity->getFetched('dateStart');
if ($dateStartPrevious && $dateEndPrevious) {
$dtStart = new \DateTime($dateStartPrevious);
$dtEnd = new \DateTime($dateEndPrevious);
$dt = new \DateTime($entity->get('dateStart'));
if ($dtStart && $dtEnd && $dt) {
$duration = ($dtEnd->getTimestamp() - $dtStart->getTimestamp());
$dt->modify('+' . $duration . ' seconds');
$dateEnd = $dt->format('Y-m-d H:i:s');
$entity->set('dateEnd', $dateEnd);
}
}
}
}
parent::beforeSave($entity, $options);
$assignedUserId = $entity->get('assignedUserId');
if ($assignedUserId) {
if ($entity->has('usersIds')) {
@@ -98,24 +117,5 @@ class Meeting extends \Espo\Core\Repositories\Event
}
}
}
if (!$entity->isNew()) {
if ($entity->isFieldChanged('dateStart') && $entity->isFieldChanged('dateStart') && !$entity->isFieldChanged('dateEnd')) {
$dateEndPrevious = $entity->getFetched('dateEnd');
$dateStartPrevious = $entity->getFetched('dateStart');
if ($dateStartPrevious && $dateEndPrevious) {
$dtStart = new \DateTime($dateStartPrevious);
$dtEnd = new \DateTime($dateEndPrevious);
$dt = new \DateTime($entity->get('dateStart'));
if ($dtStart && $dtEnd && $dt) {
$duration = ($dtEnd->getTimestamp() - $dtStart->getTimestamp());
$dt->modify('+' . $duration . ' seconds');
$dateEnd = $dt->format('Y-m-d H:i:s');
$entity->set('dateEnd', $dateEnd);
}
}
}
}
}
}

View File

@@ -35,14 +35,13 @@ class Opportunity extends \Espo\Core\ORM\Repositories\RDB
{
public function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
if ($entity->isNew()) {
if (!$entity->has('probability') && $entity->get('stage')) {
$probability = $this->getMetadata()->get('entityDefs.Opportunity.probabilityMap.' . $entity->get('stage'), 0);
$entity->set('probability', $probability);
}
}
parent::beforeSave($entity, $options);
}
}

View File

@@ -35,6 +35,8 @@ class Task extends \Espo\Core\Repositories\Event
{
protected $reminderDateAttribute = 'dateEnd';
protected $reminderSkippingStatusList = ['Completed', 'Canceled'];
protected function init()
{
parent::init();
@@ -69,8 +71,6 @@ class Task extends \Espo\Core\Repositories\Event
protected function beforeSave(Entity $entity, array $options = array())
{
parent::beforeSave($entity, $options);
if ($entity->isFieldChanged('status')) {
if ($entity->get('status') == 'Completed') {
$entity->set('dateCompleted', date('Y-m-d H:i:s'));
@@ -125,5 +125,7 @@ class Task extends \Espo\Core\Repositories\Event
}
}
}
parent::beforeSave($entity, $options);
}
}

View File

@@ -1,19 +0,0 @@
{
"fields": {
"name": "Název",
"emailAddress": "Email",
"title": "Pozice",
"website": "Web",
"accountName": "Jméno organizace",
"phoneNumber": "Telefon",
"doNotCall": "Nevolat",
"address": "Adresa",
"description": "Popis"
},
"links": {
},
"labels": {
"Create Target": "Vytvořit cíl",
"Convert to Lead": "Konvertovat na stopu"
}
}

View File

@@ -16,7 +16,8 @@
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"portalUser": "Portalbruger",
"originalLead": "Oprindelig Lead"
"originalLead": "Oprindelig Lead",
"acceptanceStatus": "Acceptance Status"
},
"links": {
"opportunities": "Muligheder",

View File

@@ -3,6 +3,8 @@
"Create Lead": "Opret Lead",
"Create Contact": "Opret Kontakt",
"Create Task": "Opret Opgave",
"Create Case": "Opret Sag"
"Create Case": "Opret Sag",
"Add to Contact": "Add to Contact",
"Add to Lead": "Add to Lead"
}
}

View File

@@ -78,12 +78,12 @@
"labels": {
"Create InboundEmail": "Opret Indkommende Email",
"Activities": "Aktiviteter",
"History": "Forløb",
"History": "Historik",
"Attendees": "Deltagere",
"Schedule Meeting": "Planlæg Møde",
"Schedule Call": "Planlæg Opkald",
"Compose Email": "Skriv Email",
"Log Meeting": "Logge Møde",
"Log Meeting": "Log møde",
"Log Call": "Logge Opkald",
"Archive Email": "Arkiver Email",
"Create Task": "Opret Opgave",

View File

@@ -38,9 +38,10 @@
"Article": "Artikel"
}
},
"tooltips": {
},
"presetFilters": {
"published": "Publiseret"
},
"tooltips": {
"portals": "Article will be available only in specified portals."
}
}

View File

@@ -3,7 +3,7 @@
"Converted To": "Konverteret Til",
"Create Lead": "Opret Lead",
"Convert": "Konverter",
"convert": "konverter"
"convert": "convert"
},
"fields": {
"name": " Navn",
@@ -25,7 +25,9 @@
"campaign": "Kampagne",
"targetLists": "Kontaktlister",
"targetList": "Kontaktliste",
"industry": "Industri"
"industry": "Industri",
"acceptanceStatus": "Acceptance Status",
"opportunityAmountCurrency": "Opportunity Amount Currency"
},
"links": {
"targetLists": "Kontaktlister",

View File

@@ -13,7 +13,8 @@
"amountConverted": "Beløb (konverteret)",
"amountWeightedConverted": "Beløb Vægtet",
"campaign": "Kampagne",
"originalLead": "Oprindelig Lead"
"originalLead": "Oprindelig Lead",
"amountCurrency": "Amount Currency"
},
"links": {
"contacts": "Kontakter",

View File

@@ -1,17 +0,0 @@
{
"fields": {
"name": "Navn",
"emailAddress": "Email",
"title": "Titel",
"website": "Webside",
"accountName": "Kontonavn",
"phoneNumber": "Telefon",
"doNotCall": "Ring Ikke!",
"address": "Adresse",
"description": "Beskrivelse"
},
"labels": {
"Create Target": "Opret Mål",
"Convert to Lead": "Konverter til Lead"
}
}

View File

@@ -1,5 +1,8 @@
{
"links": {
"targetLists": "Kontaktlister"
},
"fields": {
"acceptanceStatus": "Acceptance Status"
}
}

View File

@@ -1,17 +0,0 @@
{
"fields": {
"name": "Name",
"emailAddress": "E-Mail",
"title": "Funktion",
"website": "Webseite",
"accountName": "Firmenname",
"phoneNumber": "Telefon",
"doNotCall": "Anrufe unerwünscht",
"address": "Adresse",
"description": "Beschreibung"
},
"labels": {
"Create Target": "Zielkontakt erstellen",
"Convert to Lead": "Zu Interessent umwandeln"
}
}

View File

@@ -0,0 +1,103 @@
{
"fields": {
"name": "Name",
"emailAddress": "Email",
"website": "Website",
"phoneNumber": "Phone",
"billingAddress": "Billing Address",
"shippingAddress": "Shipping Address",
"description": "Description",
"sicCode": "Sic Code",
"industry": "Industry",
"type": "Type",
"contactRole": "Title",
"campaign": "Campaign",
"targetLists": "Target Lists",
"targetList": "Target List",
"originalLead": "Original Lead"
},
"links": {
"contacts": "Contacts",
"opportunities": "Opportunities",
"cases": "Cases",
"documents": "Documents",
"meetingsPrimary": "Meetings (expanded)",
"callsPrimary": "Calls (expanded)",
"tasksPrimary": "Tasks (expanded)",
"emailsPrimary": "Emails (expanded)",
"targetLists": "Target Lists",
"campaignLogRecords": "Campaign Log",
"campaign": "Campaign",
"portalUsers": "Portal Users",
"originalLead": "Original Lead"
},
"options": {
"type": {
"Customer": "Customer",
"Investor": "Investor",
"Partner": "Partner",
"Reseller": "Reseller"
},
"industry": {
"Agriculture": "Agriculture",
"Advertising": "Advertising",
"Apparel & Accessories": "Apparel & Accessories",
"Automotive": "Automotive",
"Banking": "Banking",
"Biotechnology": "Biotechnology",
"Building Materials & Equipment": "Building Materials & Equipment",
"Chemical": "Chemical",
"Computer": "Computer",
"Education": "Education",
"Electronics": "Electronics",
"Energy": "Energy",
"Entertainment & Leisure": "Entertainment & Leisure",
"Finance": "Finance",
"Food & Beverage": "Food & Beverage",
"Grocery": "Grocery",
"Healthcare": "Healthcare",
"Insurance": "Insurance",
"Legal": "Legal",
"Manufacturing": "Manufacturing",
"Publishing": "Publishing",
"Real Estate": "Real Estate",
"Service": "Service",
"Sports": "Sports",
"Software": "Software",
"Technology": "Technology",
"Telecommunications": "Telecommunications",
"Television": "Television",
"Transportation": "Transportation",
"Venture Capital": "Venture Capital",
"Aerospace": "Aerospace",
"Architecture": "Architecture",
"Construction": "Construction",
"Defense": "Defense",
"Creative": "Creative",
"Culture": "Culture",
"Consulting": "Consulting",
"Electric Power": "Electric Power",
"Hospitality": "Hospitality",
"Mass Media": "Mass Media",
"Mining": "Mining",
"Music": "Music",
"Marketing": "Marketing",
"Petroleum": "Petroleum",
"Retail": "Retail",
"Shipping": "Shipping",
"Support": "Support",
"Testing, Inspection & Certification": "Testing, Inspection & Certification",
"Wholesale": "Wholesale",
"Water": "Water"
}
},
"labels": {
"Create Account": "Create Account",
"Copy Billing": "Copy Billing"
},
"presetFilters": {
"customers": "Customers",
"partners": "Partners",
"recentlyCreated": "Recently Created"
}
}

View File

@@ -0,0 +1,6 @@
{
"layouts": {
"detailConvert": "Convert Lead",
"listForAccount": "List (for Account)"
}
}

View File

@@ -0,0 +1,20 @@
{
"modes": {
"month": "Month",
"week": "Week",
"agendaWeek": "Week",
"day": "Day",
"agendaDay": "Day",
"timeline": "Timeline"
},
"labels": {
"Today": "Today",
"Create": "Create",
"Shared": "Shared",
"Add User": "Add User",
"current": "current",
"time": "time",
"User List": "User List",
"Manage Users": "Manage Users"
}
}

View File

@@ -0,0 +1,50 @@
{
"fields": {
"name": "Name",
"parent": "Parent",
"status": "Status",
"dateStart": "Date Start",
"dateEnd": "Date End",
"direction": "Direction",
"duration": "Duration",
"description": "Description",
"users": "Users",
"contacts": "Contacts",
"leads": "Leads",
"reminders": "Reminders",
"account": "Account",
"acceptanceStatus": "Acceptance Status"
},
"options": {
"status": {
"Planned": "Planned",
"Held": "Held",
"Not Held": "Not Held"
},
"direction": {
"Outbound": "Outbound",
"Inbound": "Inbound"
},
"acceptanceStatus": {
"None": "None",
"Accepted": "Accepted",
"Declined": "Declined",
"Tentative": "Tentative"
}
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Set Not Held"
},
"labels": {
"Create Call": "Create Call",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
}
}

View File

@@ -0,0 +1,75 @@
{
"fields": {
"name": "Name",
"description": "Description",
"status": "Status",
"type": "Type",
"startDate": "Start Date",
"endDate": "End Date",
"targetLists": "Target Lists",
"excludingTargetLists": "Excluding Target Lists",
"sentCount": "Sent",
"openedCount": "Opened",
"clickedCount": "Clicked",
"optedOutCount": "Opted Out",
"bouncedCount": "Bounced",
"hardBouncedCount": "Hard Bounced",
"softBouncedCount": "Soft Bounced",
"leadCreatedCount": "Leads Created",
"revenue": "Revenue",
"revenueConverted": "Revenue (converted)",
"budget": "Budget",
"budgetConverted": "Budget (converted)"
},
"links": {
"targetLists": "Target Lists",
"excludingTargetLists": "Excluding Target Lists",
"accounts": "Accounts",
"contacts": "Contacts",
"leads": "Leads",
"opportunities": "Opportunities",
"campaignLogRecords": "Log",
"massEmails": "Mass Emails",
"trackingUrls": "Tracking URLs"
},
"options": {
"type": {
"Email": "Email",
"Web": "Web",
"Television": "Television",
"Radio": "Radio",
"Newsletter": "Newsletter",
"Mail": "Mail"
},
"status": {
"Planning": "Planning",
"Active": "Active",
"Inactive": "Inactive",
"Complete": "Complete"
}
},
"labels": {
"Create Campaign": "Create Campaign",
"Target Lists": "Target Lists",
"Statistics": "Statistics",
"hard": "hard",
"soft": "soft",
"Unsubscribe": "Unsubscribe",
"Mass Emails": "Mass Emails",
"Email Templates": "Email Templates",
"Unsubscribe again": "Unsubscribe again",
"Subscribe again": "Subscribe again",
"Create Target List": "Create Target List"
},
"presetFilters": {
"active": "Active"
},
"messages": {
"unsubscribed": "You have been unsubscribed from our mailing list.",
"subscribedAgain": "You are subscribed again."
},
"tooltips": {
"targetLists": "Targets that should receive messages.",
"excludingTargetLists": "Targets that should not receive messages."
}
}

View File

@@ -0,0 +1,40 @@
{
"fields": {
"action": "Action",
"actionDate": "Date",
"data": "Data",
"campaign": "Campaign",
"parent": "Target",
"object": "Object",
"application": "Application",
"queueItem": "Queue Item",
"stringData": "String Data",
"stringAdditionalData": "String Additional Data"
},
"links": {
"queueItem": "Queue Item",
"parent": "Parent",
"object": "Object"
},
"options": {
"action": {
"Sent": "Sent",
"Opened": "Opened",
"Opted Out": "Opted Out",
"Bounced": "Bounced",
"Clicked": "Clicked",
"Lead Created": "Lead Created"
}
},
"labels": {
"All": "All"
},
"presetFilters": {
"sent": "Sent",
"opened": "Opened",
"optedOut": "Opted Out",
"bounced": "Bounced",
"clicked": "Clicked",
"leadCreated": "Lead Created"
}
}

View File

@@ -0,0 +1,13 @@
{
"fields": {
"url": "URL",
"urlToUse": "Code to insert instead of URL",
"campaign": "Campaign"
},
"links": {
"campaign": "Campaign"
},
"labels": {
"Create CampaignTrackingUrl": "Create Tracking URL"
}
}

View File

@@ -0,0 +1,59 @@
{
"fields": {
"name": "Name",
"number": "Number",
"status": "Status",
"account": "Account",
"contact": "Contact",
"contacts": "Contacts",
"priority": "Priority",
"type": "Type",
"description": "Description",
"inboundEmail": "Inbound Email",
"lead": "Lead"
},
"links": {
"inboundEmail": "Inbound Email",
"account": "Account",
"contact": "Contact (Primary)",
"Contacts": "Contacts",
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"emails": "Emails",
"articles": "Knowledge Base Articles",
"lead": "Lead"
},
"options": {
"status": {
"New": "New",
"Assigned": "Assigned",
"Pending": "Pending",
"Closed": "Closed",
"Rejected": "Rejected",
"Duplicate": "Duplicate"
},
"priority": {
"Low": "Low",
"Normal": "Normal",
"High": "High",
"Urgent": "Urgent"
},
"type": {
"Question": "Question",
"Incident": "Incident",
"Problem": "Problem"
}
},
"labels": {
"Create Case": "Create Case",
"Close": "Close",
"Reject": "Reject",
"Closed": "Closed",
"Rejected": "Rejected"
},
"presetFilters": {
"open": "Open",
"closed": "Closed"
}
}

View File

@@ -0,0 +1,50 @@
{
"fields": {
"name": "Name",
"emailAddress": "Email",
"title": "Title",
"accountRole": "Title",
"account": "Account",
"accounts": "Accounts",
"phoneNumber": "Phone",
"accountType": "Account Type",
"doNotCall": "Do Not Call",
"address": "Address",
"opportunityRole": "Opportunity Role",
"description": "Description",
"campaign": "Campaign",
"targetLists": "Target Lists",
"targetList": "Target List",
"portalUser": "Portal User",
"originalLead": "Original Lead",
"acceptanceStatus": "Acceptance Status"
},
"links": {
"opportunities": "Opportunities",
"cases": "Cases",
"targetLists": "Target Lists",
"campaignLogRecords": "Campaign Log",
"campaign": "Campaign",
"account": "Account (Primary)",
"accounts": "Accounts",
"casesPrimary": "Cases (Primary)",
"portalUser": "Portal User",
"originalLead": "Original Lead",
"documents": "Documents"
},
"labels": {
"Create Contact": "Create Contact"
},
"options": {
"opportunityRole": {
"": "--None--",
"Decision Maker": "Decision Maker",
"Evaluator": "Evaluator",
"Influencer": "Influencer"
}
},
"presetFilters": {
"portalUsers": "Portal Users",
"notPortalUsers": "Not Portal Users"
}
}

View File

@@ -0,0 +1,43 @@
{
"labels": {
"Create Document": "Create Document",
"Details": "Details"
},
"fields": {
"name": "Name",
"status": "Status",
"file": "File",
"type": "Type",
"publishDate": "Publish Date",
"expirationDate": "Expiration Date",
"description": "Description",
"accounts": "Accounts",
"folder": "Folder"
},
"links": {
"accounts": "Accounts",
"opportunities": "Opportunities",
"folder": "Folder",
"leads": "Leads",
"contacts": "Contacts"
},
"options": {
"status": {
"Active": "Active",
"Draft": "Draft",
"Expired": "Expired",
"Canceled": "Cancelled"
},
"type": {
"": "None",
"Contract": "Contract",
"NDA": "NDA",
"EULA": "EULA",
"License Agreement": "License Agreement"
}
},
"presetFilters": {
"active": "Active",
"draft": "Draft"
}
}

View File

@@ -0,0 +1,10 @@
{
"labels": {
"Create DocumentFolder": "Create Document Folder",
"Manage Categories": "Manage Folders",
"Documents": "Documents"
},
"links": {
"documents": "Documents"
}
}

View File

@@ -0,0 +1,10 @@
{
"labels": {
"Create Lead": "Create Lead",
"Create Contact": "Create Contact",
"Create Task": "Create Task",
"Create Case": "Create Case",
"Add to Contact": "Add to Contact",
"Add to Lead": "Add to Lead"
}
}

View File

@@ -0,0 +1,29 @@
{
"fields": {
"name": "Name",
"status": "Status",
"target": "Target",
"sentAt": "Date Sent",
"attemptCount": "Attempts",
"emailAddress": "Email Address",
"massEmail": "Mass Email",
"isTest": "Is Test"
},
"links": {
"target": "Target",
"massEmail": "Mass Email"
},
"options": {
"status": {
"Pending": "Pending",
"Sent": "Sent",
"Failed": "Failed",
"Sending": "Sending"
}
},
"presetFilters": {
"pending": "Pending",
"sent": "Sent",
"failed": "Failed"
}
}

View File

@@ -0,0 +1,118 @@
{
"links": {
"parent": "Parent",
"contacts": "Contacts",
"opportunities": "Opportunities",
"leads": "Leads",
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"emails": "Emails",
"accounts": "Accounts",
"cases": "Cases",
"documents": "Documents",
"account": "Account",
"opportunity": "Opportunity",
"contact": "Contact"
},
"scopeNames": {
"Account": "Account",
"Contact": "Contact",
"Lead": "Lead",
"Target": "Target",
"Opportunity": "Opportunity",
"Meeting": "Meeting",
"Calendar": "Calendar",
"Call": "Call",
"Task": "Task",
"Case": "Case",
"Document": "Document",
"DocumentFolder": "Document Folder",
"Campaign": "Campaign",
"TargetList": "Target List",
"MassEmail": "Mass Email",
"EmailQueueItem": "Email Queue Item",
"CampaignTrackingUrl": "Tracking URL",
"Activities": "Activities",
"KnowledgeBaseArticle": "Knowledge Base Article",
"KnowledgeBaseCategory": "Knowledge Base Category",
"CampaignLogRecord": "Campaign Log Record"
},
"scopeNamesPlural": {
"Account": "Accounts",
"Contact": "Contacts",
"Lead": "Leads",
"Target": "Targets",
"Opportunity": "Opportunities",
"Meeting": "Meetings",
"Calendar": "Calendar",
"Call": "Calls",
"Task": "Tasks",
"Case": "Cases",
"Document": "Documents",
"DocumentFolder": "Document Folders",
"Campaign": "Campaigns",
"TargetList": "Target Lists",
"MassEmail": "Mass Emails",
"EmailQueueItem": "Email Queue Items",
"CampaignTrackingUrl": "Tracking URLs",
"Activities": "Activities",
"KnowledgeBaseArticle": "Knowledge Base",
"KnowledgeBaseCategory": "Knowledge Base Categories",
"CampaignLogRecord": "Campaign Log Records"
},
"dashlets": {
"Leads": "My Leads",
"Opportunities": "My Opportunities",
"Tasks": "My Tasks",
"Cases": "My Cases",
"Calendar": "Calendar",
"Calls": "My Calls",
"Meetings": "My Meetings",
"OpportunitiesByStage": "Opportunities by Stage",
"OpportunitiesByLeadSource": "Opportunities by Lead Source",
"SalesByMonth": "Sales by Month",
"SalesPipeline": "Sales Pipeline",
"Activities": "My Activities"
},
"labels": {
"Create InboundEmail": "Create Inbound Email",
"Activities": "Activities",
"History": "History",
"Attendees": "Attendees",
"Schedule Meeting": "Schedule Meeting",
"Schedule Call": "Schedule Call",
"Compose Email": "Compose Email",
"Log Meeting": "Log Meeting",
"Log Call": "Log Call",
"Archive Email": "Archive Email",
"Create Task": "Create Task",
"Tasks": "Tasks"
},
"fields": {
"billingAddressCity": "City",
"addressCity": "City",
"billingAddressCountry": "Country",
"addressCountry": "Country",
"billingAddressPostalCode": "Postal Code",
"addressPostalCode": "Postal Code",
"billingAddressState": "County",
"addressState": "County",
"billingAddressStreet": "Street",
"addressStreet": "Street",
"billingAddressMap": "Map",
"addressMap": "Map",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
"shippingAddressState": "County (Shipping)",
"shippingAddressPostalCode": "Postal Code (Shipping)",
"shippingAddressMap": "Map (Shipping)"
},
"options": {
"reminderTypes": {
"Popup": "Popup",
"Email": "Email"
}
}
}

View File

@@ -0,0 +1,47 @@
{
"labels": {
"Create KnowledgeBaseArticle": "Create Article",
"Any": "Any",
"Send in Email": "Send in Email",
"Move Up": "Move Up",
"Move Down": "Move Down",
"Move to Top": "Move to Top",
"Move to Bottom": "Move to Bottom"
},
"fields": {
"name": "Name",
"status": "Status",
"type": "Type",
"attachments": "Attachments",
"publishDate": "Publish Date",
"expirationDate": "Expiration Date",
"description": "Description",
"body": "Body",
"categories": "Categories",
"language": "Language",
"portals": "Portals"
},
"links": {
"cases": "Cases",
"opportunities": "Opportunities",
"categories": "Categories",
"portals": "Portals"
},
"options": {
"status": {
"In Review": "In Review",
"Draft": "Draft",
"Archived": "Archived",
"Published": "Published"
},
"type": {
"Article": "Article"
}
},
"presetFilters": {
"published": "Published"
},
"tooltips": {
"portals": "Article will be available only in specified portals."
}
}

View File

@@ -0,0 +1,10 @@
{
"labels": {
"Create KnowledgeBaseCategory": "Create Category",
"Manage Categories": "Manage Categories",
"Articles": "Articles"
},
"links": {
"articles": "Articles"
}
}

View File

@@ -0,0 +1,68 @@
{
"labels": {
"Converted To": "Converted To",
"Create Lead": "Create Lead",
"Convert": "Convert",
"convert": "convert"
},
"fields": {
"name": "Name",
"emailAddress": "Email",
"title": "Title",
"website": "Website",
"phoneNumber": "Phone",
"accountName": "Account Name",
"doNotCall": "Do Not Call",
"address": "Address",
"status": "Status",
"source": "Source",
"opportunityAmount": "Opportunity Amount",
"opportunityAmountConverted": "Opportunity Amount (converted)",
"description": "Description",
"createdAccount": "Account",
"createdContact": "Contact",
"createdOpportunity": "Opportunity",
"campaign": "Campaign",
"targetLists": "Target Lists",
"targetList": "Target List",
"industry": "Industry",
"acceptanceStatus": "Acceptance Status",
"opportunityAmountCurrency": "Opportunity Amount Currency"
},
"links": {
"targetLists": "Target Lists",
"campaignLogRecords": "Campaign Log",
"campaign": "Campaign",
"createdAccount": "Account",
"createdContact": "Contact",
"createdOpportunity": "Opportunity",
"cases": "Cases",
"documents": "Documents"
},
"options": {
"status": {
"New": "New",
"Assigned": "Assigned",
"In Process": "In Process",
"Converted": "Converted",
"Recycled": "Recycled",
"Dead": "Dead"
},
"source": {
"": "None",
"Call": "Call",
"Email": "Email",
"Existing Customer": "Existing Customer",
"Partner": "Partner",
"Public Relations": "Public Relations",
"Web Site": "Web Site",
"Campaign": "Campaign",
"Other": "Other"
}
},
"presetFilters": {
"active": "Active",
"actual": "Actual",
"converted": "Converted"
}
}

View File

@@ -0,0 +1,53 @@
{
"fields": {
"name": "Name",
"status": "Status",
"storeSentEmails": "Store Sent Emails",
"startAt": "Date Start",
"fromAddress": "From Address",
"fromName": "From Name",
"replyToAddress": "Reply-to Address",
"replyToName": "Reply-to Name",
"campaign": "Campaign",
"emailTemplate": "Email Template",
"inboundEmail": "Email Account",
"targetLists": "Target Lists",
"excludingTargetLists": "Excluding Target Lists",
"optOutEntirely": "Opt-Out Entirely"
},
"links": {
"targetLists": "Target Lists",
"excludingTargetLists": "Excluding Target Lists",
"queueItems": "Queue Items",
"campaign": "Campaign",
"emailTemplate": "Email Template",
"inboundEmail": "Email Account"
},
"options": {
"status": {
"Draft": "Draft",
"Pending": "Pending",
"In Process": "In Process",
"Complete": "Complete",
"Canceled": "Cancelled",
"Failed": "Failed"
}
},
"labels": {
"Create MassEmail": "Create Mass Email",
"Send Test": "Send Test"
},
"messages": {
"selectAtLeastOneTarget": "Select at least one target.",
"testSent": "Test email(s) supposed to be sent"
},
"tooltips": {
"optOutEntirely": "Email addresses of recipients that unsubscribed will be marked as opted out and they will not receive any mass emails anymore.",
"targetLists": "Targets that should receive messages.",
"excludingTargetLists": "Targets that should not receive messages."
},
"presetFilters": {
"actual": "Actual",
"complete": "Complete"
}
}

View File

@@ -0,0 +1,50 @@
{
"fields": {
"name": "Name",
"parent": "Parent",
"status": "Status",
"dateStart": "Date Start",
"dateEnd": "Date End",
"duration": "Duration",
"description": "Description",
"users": "Users",
"contacts": "Contacts",
"leads": "Leads",
"reminders": "Reminders",
"account": "Account",
"acceptanceStatus": "Acceptance Status"
},
"options": {
"status": {
"Planned": "Planned",
"Held": "Held",
"Not Held": "Not Held"
},
"acceptanceStatus": {
"None": "None",
"Accepted": "Accepted",
"Declined": "Declined",
"Tentative": "Tentative"
}
},
"massActions": {
"setHeld": "Set Held",
"setNotHeld": "Set Not Held"
},
"labels": {
"Create Meeting": "Create Meeting",
"Set Held": "Set Held",
"Set Not Held": "Set Not Held",
"Send Invitations": "Send Invitations",
"on time": "on time",
"before": "before"
},
"presetFilters": {
"planned": "Planned",
"held": "Held",
"todays": "Today's"
},
"messages": {
"nothingHasBeenSent": "Nothing were sent"
}
}

View File

@@ -0,0 +1,47 @@
{
"fields": {
"name": "Name",
"account": "Account",
"stage": "Stage",
"amount": "Amount",
"probability": "Probability, %",
"leadSource": "Lead Source",
"doNotCall": "Do Not Call",
"closeDate": "Close Date",
"contacts": "Contacts",
"description": "Description",
"amountConverted": "Amount (converted)",
"amountWeightedConverted": "Amount Weighted",
"campaign": "Campaign",
"originalLead": "Original Lead",
"amountCurrency": "Amount Currency"
},
"links": {
"contacts": "Contacts",
"documents": "Documents",
"campaign": "Campaign",
"originalLead": "Original Lead"
},
"options": {
"stage": {
"Prospecting": "Prospecting",
"Qualification": "Qualification",
"Needs Analysis": "Needs Analysis",
"Value Proposition": "Value Proposition",
"Id. Decision Makers": "Id. Decision Makers",
"Perception Analysis": "Perception Analysis",
"Proposal/Price Quote": "Proposal/Price Quote",
"Negotiation/Review": "Negotiation/Review",
"Closed Won": "Closed Won",
"Closed Lost": "Closed Lost"
}
},
"labels": {
"Create Opportunity": "Create Opportunity"
},
"presetFilters": {
"open": "Open",
"won": "Won",
"lost": "Lost"
}
}

View File

@@ -0,0 +1,5 @@
{
"links": {
"articles": "Knowledge Base Articles"
}
}

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