mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-04 17:57:01 +00:00
Compare commits
373 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d5f3bcbfe | ||
|
|
339e1dc897 | ||
|
|
c4e617f18e | ||
|
|
89bc329ea6 | ||
|
|
eee49b215e | ||
|
|
d9a8b83268 | ||
|
|
def7abe3ba | ||
|
|
75e64b3836 | ||
|
|
efb106c597 | ||
|
|
5e1087c6b4 | ||
|
|
5f8800defa | ||
|
|
445c0b3834 | ||
|
|
61c1e5ab6c | ||
|
|
2502cad0c1 | ||
|
|
76032493b9 | ||
|
|
0a3a6b3773 | ||
|
|
014d47d017 | ||
|
|
8bd7734ae3 | ||
|
|
7aa2ba917f | ||
|
|
96f0ce2381 | ||
|
|
f1eb5b9862 | ||
|
|
45cb5a6e38 | ||
|
|
4131ea54ae | ||
|
|
3806affe1b | ||
|
|
6ea3a03de3 | ||
|
|
5b25e8b7e6 | ||
|
|
60c96ba5f7 | ||
|
|
9732852891 | ||
|
|
326b46a6e6 | ||
|
|
29924ea3b1 | ||
|
|
434506699b | ||
|
|
eeaaaf7af1 | ||
|
|
3f5dab0680 | ||
|
|
dd69658f2a | ||
|
|
3c2d998a30 | ||
|
|
a0ead9d774 | ||
|
|
4460a105a8 | ||
|
|
c6b369587d | ||
|
|
8c19ad5f7f | ||
|
|
1824e0d0a6 | ||
|
|
404bb0c3cd | ||
|
|
7fd6b16cf3 | ||
|
|
47abd1ab52 | ||
|
|
5c8d728ac5 | ||
|
|
2706bc461e | ||
|
|
91f52e4f42 | ||
|
|
5538dd45d3 | ||
|
|
105205a9aa | ||
|
|
e3547717a0 | ||
|
|
1714e0e9a0 | ||
|
|
afdd15ac44 | ||
|
|
09097dba4a | ||
|
|
01a8fbd5b1 | ||
|
|
f2d5eed7e3 | ||
|
|
cfae3cde1d | ||
|
|
87a0c1b293 | ||
|
|
486c8c8f37 | ||
|
|
ea99ca3d38 | ||
|
|
2069a25f30 | ||
|
|
baf3493044 | ||
|
|
543a12a475 | ||
|
|
a73f97744a | ||
|
|
ae135ab678 | ||
|
|
96366548a3 | ||
|
|
54cdd5180d | ||
|
|
0a5ce2a4d4 | ||
|
|
a4e1ccc4bb | ||
|
|
55c632979e | ||
|
|
b1208a449e | ||
|
|
9e2b329beb | ||
|
|
10d329094c | ||
|
|
38f44b3e5f | ||
|
|
c99a1261d5 | ||
|
|
4e2e344892 | ||
|
|
e0128bf54f | ||
|
|
a683e6bdf8 | ||
|
|
bc64effb14 | ||
|
|
6b0a280e2c | ||
|
|
ddf25e02e3 | ||
|
|
967d5a6387 | ||
|
|
a5137b85af | ||
|
|
1ed79fedf0 | ||
|
|
8a8cb08b99 | ||
|
|
b49aaa69cc | ||
|
|
015357d551 | ||
|
|
5419d76080 | ||
|
|
21e1422994 | ||
|
|
dafb076659 | ||
|
|
491f3d27ff | ||
|
|
3be58d5d09 | ||
|
|
8aeb23a985 | ||
|
|
fa99040232 | ||
|
|
2e928f2442 | ||
|
|
8ebb35c0d5 | ||
|
|
7d17bee201 | ||
|
|
5c345fb4d9 | ||
|
|
d2cd098ab0 | ||
|
|
f4a88df067 | ||
|
|
8f4917eb24 | ||
|
|
a9310b108a | ||
|
|
8c638dffd4 | ||
|
|
0151273a75 | ||
|
|
b10dc8128c | ||
|
|
476d36c9c5 | ||
|
|
64087defd0 | ||
|
|
807b7bc349 | ||
|
|
3018c345fa | ||
|
|
8f35c59943 | ||
|
|
348332f7ca | ||
|
|
9ebd992222 | ||
|
|
9597ea0dbd | ||
|
|
23fb6c6e1f | ||
|
|
ddb5033de8 | ||
|
|
43a49e3c31 | ||
|
|
5a06ee50d8 | ||
|
|
c02f65ef81 | ||
|
|
dbb9b20b7f | ||
|
|
3bfd540f53 | ||
|
|
a971f7357f | ||
|
|
4918f8f985 | ||
|
|
988388ed16 | ||
|
|
f1de9deb80 | ||
|
|
2642a6ea6e | ||
|
|
74b82c92d8 | ||
|
|
ded3d112b2 | ||
|
|
c9e7f02b23 | ||
|
|
c7fb864e04 | ||
|
|
16d300445c | ||
|
|
edc24621a6 | ||
|
|
17b48c0e4c | ||
|
|
771d58f425 | ||
|
|
fd76459077 | ||
|
|
1955dff68e | ||
|
|
730b5fb8a9 | ||
|
|
c1a115e2b0 | ||
|
|
7773e41ad4 | ||
|
|
304bbb62ad | ||
|
|
7b439ace04 | ||
|
|
d9ef87fb90 | ||
|
|
2333b7a1ef | ||
|
|
1a00932c63 | ||
|
|
1f09015c86 | ||
|
|
5d79d8520f | ||
|
|
dde044d8c5 | ||
|
|
aeeb84b626 | ||
|
|
ccb9022e16 | ||
|
|
a9e783b5f1 | ||
|
|
46190df6ff | ||
|
|
1eb258cab7 | ||
|
|
a3836b0091 | ||
|
|
7fbc9fbac8 | ||
|
|
d0b5e2188b | ||
|
|
dfa2d6be89 | ||
|
|
276941197f | ||
|
|
20569e4252 | ||
|
|
3b7f99daa1 | ||
|
|
5ee30d9f3f | ||
|
|
3cde7fa5e3 | ||
|
|
aecb9bbdf4 | ||
|
|
1a95e14ee8 | ||
|
|
83aa07b918 | ||
|
|
4f8eea59fe | ||
|
|
248280e086 | ||
|
|
02b465ab8c | ||
|
|
c9d26dc1d8 | ||
|
|
389b158134 | ||
|
|
312af9befb | ||
|
|
b7f22bf687 | ||
|
|
f062ac255a | ||
|
|
dd8082244c | ||
|
|
25ff7e17c1 | ||
|
|
f4b3071a92 | ||
|
|
0c7b182215 | ||
|
|
2223a1ecf4 | ||
|
|
3a24784980 | ||
|
|
f86564a580 | ||
|
|
58897949eb | ||
|
|
2dee859b2a | ||
|
|
b1dbd173ad | ||
|
|
a9fc290627 | ||
|
|
12794968fc | ||
|
|
1b4cbb91ee | ||
|
|
62f1362cf6 | ||
|
|
49031d0b33 | ||
|
|
22fea07b0a | ||
|
|
81692371ec | ||
|
|
a96314f0f0 | ||
|
|
a92c294255 | ||
|
|
7c554a605e | ||
|
|
57eeec63c1 | ||
|
|
bd158008b2 | ||
|
|
c0ae8d5cdd | ||
|
|
78347920aa | ||
|
|
c88e662a30 | ||
|
|
5ae57ff5f6 | ||
|
|
7c74c85a66 | ||
|
|
07fc2123e7 | ||
|
|
afdb47b601 | ||
|
|
5ea437afbb | ||
|
|
c7041546c4 | ||
|
|
1d2fd21a06 | ||
|
|
27624ead8d | ||
|
|
fefc003984 | ||
|
|
70d3bf9fff | ||
|
|
4a64c4b648 | ||
|
|
15c980e57f | ||
|
|
9d8fc5b8ea | ||
|
|
8184221d2b | ||
|
|
5786983d8b | ||
|
|
1e1468ab68 | ||
|
|
e3f0177759 | ||
|
|
f1fc6160fe | ||
|
|
38d4a39014 | ||
|
|
566d0c1aa0 | ||
|
|
ce752b32fd | ||
|
|
a61553d08b | ||
|
|
daacb026d8 | ||
|
|
0ae637fba2 | ||
|
|
b81fd66b85 | ||
|
|
071e94ac97 | ||
|
|
a98465f30e | ||
|
|
e949a0bae6 | ||
|
|
f7f9e2fda0 | ||
|
|
80a0571b0c | ||
|
|
d91897a922 | ||
|
|
05180ece92 | ||
|
|
ee8e960ce4 | ||
|
|
5b4a8666cd | ||
|
|
7240757a28 | ||
|
|
2a8baa2f34 | ||
|
|
0c44e0d101 | ||
|
|
63ab5b77f3 | ||
|
|
300198c9fa | ||
|
|
340af18beb | ||
|
|
8862fd279e | ||
|
|
83cff1bc42 | ||
|
|
a75534e32a | ||
|
|
462eb8792c | ||
|
|
19f29c0401 | ||
|
|
27657260fc | ||
|
|
c17fb9ab1e | ||
|
|
bd6eafd291 | ||
|
|
a63306603a | ||
|
|
d3b1114b1e | ||
|
|
87340e29e8 | ||
|
|
4f1f8d9301 | ||
|
|
664b294471 | ||
|
|
7774c12ab2 | ||
|
|
6592dfc4da | ||
|
|
e823ecc178 | ||
|
|
74d5a737db | ||
|
|
bf9799af66 | ||
|
|
fc9b67672f | ||
|
|
6e8342511f | ||
|
|
da2cebd765 | ||
|
|
ef19dc3330 | ||
|
|
2f73a2d19b | ||
|
|
fab257eb97 | ||
|
|
850c3fd696 | ||
|
|
a619738652 | ||
|
|
395c575f78 | ||
|
|
ecf8256b14 | ||
|
|
2d6d6a2b19 | ||
|
|
ee404fa400 | ||
|
|
10fc546369 | ||
|
|
0ea0c09007 | ||
|
|
85b2dbac11 | ||
|
|
d69f994f4c | ||
|
|
12a368054a | ||
|
|
7a2a9de174 | ||
|
|
0e14f2b987 | ||
|
|
a50d97adf5 | ||
|
|
62e9138cf1 | ||
|
|
657e7f0e37 | ||
|
|
66d648c7d4 | ||
|
|
08dd77a6b6 | ||
|
|
037f0d4db1 | ||
|
|
19ff591105 | ||
|
|
7d2655f757 | ||
|
|
7b1a1be366 | ||
|
|
1fc3ae72b5 | ||
|
|
991b864881 | ||
|
|
f95fa96ae1 | ||
|
|
e33cdf268a | ||
|
|
dcae13f4dc | ||
|
|
96ac275970 | ||
|
|
e58a4c4bbf | ||
|
|
c35985e658 | ||
|
|
4bc5a89ac2 | ||
|
|
b7e14d60cc | ||
|
|
7164a71d88 | ||
|
|
6bedd86e08 | ||
|
|
d39e99cbdd | ||
|
|
ab3134f44a | ||
|
|
5aaca9921c | ||
|
|
9223e43b7c | ||
|
|
3766dd16e7 | ||
|
|
e53aff0031 | ||
|
|
4a40f1fba7 | ||
|
|
8432e9dcc8 | ||
|
|
04e9bd9de3 | ||
|
|
ed8ed591f7 | ||
|
|
34ff0218b9 | ||
|
|
2f6c928077 | ||
|
|
eff0152f44 | ||
|
|
a3188ce694 | ||
|
|
4bf81be56f | ||
|
|
7c22960f42 | ||
|
|
3d6f90ad93 | ||
|
|
a02aebeed2 | ||
|
|
d06afd4def | ||
|
|
88e148fca8 | ||
|
|
2d00ef314f | ||
|
|
0b6a29a035 | ||
|
|
5feb0eb7c7 | ||
|
|
48cb718fbe | ||
|
|
13ad6ec0d0 | ||
|
|
9cfdb3a00b | ||
|
|
b560254df2 | ||
|
|
101ed19073 | ||
|
|
65868fd26a | ||
|
|
d2e5b26765 | ||
|
|
2cac1ea608 | ||
|
|
9d37e68e24 | ||
|
|
290a1c5b31 | ||
|
|
ff924f666f | ||
|
|
8c5eb9c913 | ||
|
|
25e72fa6a5 | ||
|
|
76602aede1 | ||
|
|
77411702a7 | ||
|
|
1aeee24d97 | ||
|
|
d8cd0d31db | ||
|
|
e72557bb59 | ||
|
|
84e587fcaf | ||
|
|
f8e75dccdf | ||
|
|
b6eca1db17 | ||
|
|
6ff414ffcd | ||
|
|
b54c9a797d | ||
|
|
7acbf3084d | ||
|
|
87847446a8 | ||
|
|
59f250b386 | ||
|
|
f93ce594d0 | ||
|
|
c41f4e51e5 | ||
|
|
6efd355864 | ||
|
|
7ee2ee9d35 | ||
|
|
737215c599 | ||
|
|
ced0c25a08 | ||
|
|
5e810d2aa3 | ||
|
|
c9e27dd190 | ||
|
|
1f8a14aca0 | ||
|
|
f518ae59c0 | ||
|
|
c5866f3d86 | ||
|
|
4d4bde46f8 | ||
|
|
cb89ad59af | ||
|
|
092b5024fe | ||
|
|
8221f7182b | ||
|
|
ecee739b1a | ||
|
|
702b47296c | ||
|
|
517edf2ced | ||
|
|
219c28313c | ||
|
|
656bba1d3f | ||
|
|
311d92202b | ||
|
|
05cb2ee272 | ||
|
|
f896a2d71a | ||
|
|
dd6704ace5 | ||
|
|
1dc4d44a65 | ||
|
|
bfb28ea178 | ||
|
|
ec6f3a22f2 | ||
|
|
5a0c7c330c | ||
|
|
f64df5af87 | ||
|
|
d4dc7a4051 | ||
|
|
d676f85c8f | ||
|
|
ab382f2387 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,7 +14,9 @@
|
||||
/client/css/sakura-vertical.css
|
||||
/client/css/violet.css
|
||||
/client/css/violet-vertical.css
|
||||
/tests/testData/cache/*
|
||||
/tests/unit/testData/cache/*
|
||||
!/tests/unit/testData/cache/.data
|
||||
/tests/integration/config.php
|
||||
composer.phar
|
||||
vendor/
|
||||
/custom/Espo/Custom/*
|
||||
@@ -8,14 +8,14 @@ Download the latest release from our [website](http://www.espocrm.com).
|
||||
|
||||
### Requirements
|
||||
|
||||
* PHP 5.4 or above (with pdo, json, gd, mcrypt extensions);
|
||||
* PHP 5.5 or above (with pdo, json, gd, mcrypt extensions);
|
||||
* MySQL 5.1 or above.
|
||||
|
||||
For more information about server configuration see [this article](http://blog.espocrm.com/administration/server-configuration-for-espocrm/).
|
||||
|
||||
### How to report bug
|
||||
|
||||
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/bug-reports?routestring=forum/bug-reports).
|
||||
Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our [forum](http://forum.espocrm.com/forum/bug-reports).
|
||||
|
||||
### How to get started (for developers)
|
||||
|
||||
|
||||
41
application/Espo/Acl/Team.php
Normal file
41
application/Espo/Acl/Team.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Team extends \Espo\Core\Acl\Base
|
||||
{
|
||||
public function checkInTeam(\Espo\Entities\User $user, Entity $entity)
|
||||
{
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
return in_array($entity->id, $userTeamIdList);
|
||||
}
|
||||
}
|
||||
@@ -60,11 +60,17 @@ class App extends \Espo\Core\Controllers\Base
|
||||
}
|
||||
$userData['emailAddressList'] = $emailAddressList;
|
||||
|
||||
$settings = (object)[];
|
||||
foreach ($this->getConfig()->get('userItems') as $item) {
|
||||
$settings->$item = $this->getConfig()->get($item);
|
||||
}
|
||||
|
||||
return array(
|
||||
'user' => $userData,
|
||||
'acl' => $this->getAcl()->getMap(),
|
||||
'preferences' => $preferences,
|
||||
'token' => $this->getUser()->get('token')
|
||||
'token' => $this->getUser()->get('token'),
|
||||
'settings' => $settings
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,13 @@ class Attachment extends \Espo\Core\Controllers\Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
list($prefix, $contents) = explode(',', $data);
|
||||
$contents = base64_decode($contents);
|
||||
$arr = explode(',', $data);
|
||||
if (count($arr) > 1) {
|
||||
list($prefix, $contents) = $arr;
|
||||
$contents = base64_decode($contents);
|
||||
} else {
|
||||
$contents = '';
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
@@ -40,32 +40,32 @@ class AuthToken extends \Espo\Core\Controllers\Record
|
||||
}
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data)
|
||||
public function actionUpdate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data)
|
||||
public function actionCreate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionListLinked($params, $data)
|
||||
public function actionListLinked($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionMassUpdate($params, $data)
|
||||
public function actionMassUpdate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data)
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data)
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -198,6 +198,13 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
|
||||
}
|
||||
|
||||
if (array_key_exists('audited', $data)) {
|
||||
$params['audited'] = $data['audited'];
|
||||
}
|
||||
if (array_key_exists('auditedForeign', $data)) {
|
||||
$params['auditedForeign'] = $data['auditedForeign'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->createLink($params);
|
||||
|
||||
if ($result) {
|
||||
@@ -242,6 +249,13 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
$params['linkMultipleFieldForeign'] = $data['linkMultipleFieldForeign'];
|
||||
}
|
||||
|
||||
if (array_key_exists('audited', $data)) {
|
||||
$params['audited'] = $data['audited'];
|
||||
}
|
||||
if (array_key_exists('auditedForeign', $data)) {
|
||||
$params['auditedForeign'] = $data['auditedForeign'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($params);
|
||||
|
||||
if ($result) {
|
||||
|
||||
@@ -94,17 +94,17 @@ class Extension extends \Espo\Core\Controllers\Record
|
||||
return true;
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data)
|
||||
public function actionCreate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data)
|
||||
public function actionUpdate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionPatch($params, $data)
|
||||
public function actionPatch($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -139,12 +139,12 @@ class Extension extends \Espo\Core\Controllers\Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data)
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data)
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error,
|
||||
\Espo\Core\Exceptions\Forbidden,
|
||||
\Espo\Core\Exceptions\NotFound;
|
||||
\Espo\Core\Exceptions\NotFound,
|
||||
\Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class FieldManager extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
@@ -44,19 +45,23 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
|
||||
public function actionRead($params, $data)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$data = $this->getContainer()->get('fieldManager')->read($params['name'], $params['scope']);
|
||||
|
||||
if (!isset($data)) {
|
||||
throw new NotFound();
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data)
|
||||
public function postActionCreate($params, $data)
|
||||
{
|
||||
if (empty($data['name'])) {
|
||||
throw new Error("Field 'name' cannnot be empty");
|
||||
if (empty($params['scope']) || empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$fieldManager = $this->getContainer()->get('fieldManager');
|
||||
@@ -72,8 +77,12 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
return $fieldManager->read($data['name'], $params['scope']);
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data)
|
||||
public function putActionUpdate($params, $data)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$fieldManager = $this->getContainer()->get('fieldManager');
|
||||
$fieldManager->update($params['name'], $data, $params['scope']);
|
||||
|
||||
@@ -86,8 +95,12 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
return $fieldManager->read($params['name'], $params['scope']);
|
||||
}
|
||||
|
||||
public function actionDelete($params, $data)
|
||||
public function deleteActionDelete($params, $data)
|
||||
{
|
||||
if (empty($params['scope']) || empty($params['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$res = $this->getContainer()->get('fieldManager')->delete($params['name'], $params['scope']);
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuildMetadata();
|
||||
@@ -95,5 +108,17 @@ class FieldManager extends \Espo\Core\Controllers\Base
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function postActionResetToDefault($params, $data)
|
||||
{
|
||||
if (empty($data['scope']) || empty($data['name'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getContainer()->get('fieldManager')->resetToDefault($data['name'], $data['scope']);
|
||||
|
||||
$this->getContainer()->get('dataManager')->rebuildMetadata();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,8 +127,48 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['fieldDelimiter'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['textQualifier'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['dateFormat'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['timeFormat'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['personNameFormat'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['decimalMark'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['defaultValues'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['action'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['attachmentId'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (!isset($data['entityType'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$importParams = array(
|
||||
'headerRow' => $data['headerRow'],
|
||||
'headerRow' => !empty($data['headerRow']),
|
||||
'fieldDelimiter' => $data['fieldDelimiter'],
|
||||
'textQualifier' => $data['textQualifier'],
|
||||
'dateFormat' => $data['dateFormat'],
|
||||
@@ -138,6 +178,8 @@ class Import extends \Espo\Core\Controllers\Record
|
||||
'currency' => $data['currency'],
|
||||
'defaultValues' => $data['defaultValues'],
|
||||
'action' => $data['action'],
|
||||
'skipDuplicateChecking' => !empty($data['skipDuplicateChecking']),
|
||||
'idleMode' => !empty($data['idleMode'])
|
||||
);
|
||||
|
||||
if (array_key_exists('updateBy', $data)) {
|
||||
|
||||
@@ -67,6 +67,19 @@ class Integration extends \Espo\Core\Controllers\Record
|
||||
$entity->set($data);
|
||||
$this->getEntityManager()->saveEntity($entity);
|
||||
|
||||
$integrationsConfigData = $this->getConfig()->get('integrations');
|
||||
|
||||
if (!$integrationsConfigData || !($integrationsConfigData instanceof \StdClass)) {
|
||||
$integrationsConfigData = (object)[];
|
||||
}
|
||||
$integrationName = $params['id'];
|
||||
|
||||
$integrationsConfigData->$integrationName = $entity->get('enabled');
|
||||
$this->getConfig()->set('integrations', $integrationsConfigData);
|
||||
|
||||
$this->getConfig()->save();
|
||||
|
||||
|
||||
return $entity->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,11 @@ class Settings extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
protected function getConfigData()
|
||||
{
|
||||
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
|
||||
if ($this->getUser()->id == 'system') {
|
||||
$data = $this->getConfig()->getData();
|
||||
} else {
|
||||
$data = $this->getConfig()->getData($this->getUser()->isAdmin());
|
||||
}
|
||||
|
||||
$fieldDefs = $this->getMetadata()->get('entityDefs.Settings.fields');
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ class Base implements Injectable
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
@@ -177,7 +177,7 @@ class Table
|
||||
return $this->data->table->$scope->$action;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return 'no';
|
||||
}
|
||||
|
||||
private function load()
|
||||
@@ -459,6 +459,17 @@ class Table
|
||||
$defaultValue = (object) $defaultValue;
|
||||
}
|
||||
$table->$scope = $defaultValue;
|
||||
|
||||
if (is_object($table->$scope)) {
|
||||
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList']);
|
||||
if ($actionList) {
|
||||
foreach (get_object_vars($table->$scope) as $action => $level) {
|
||||
if (!in_array($action, $actionList)) {
|
||||
unset($table->$scope->$action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,7 +617,9 @@ class Table
|
||||
|
||||
if (!is_object($row)) continue;
|
||||
|
||||
foreach ($this->actionList as $i => $action) {
|
||||
$actionList = $this->getMetadata()->get(['scopes', $scope, $this->type . 'ActionList'], $this->actionList);
|
||||
|
||||
foreach ($actionList as $i => $action) {
|
||||
if (isset($row->$action)) {
|
||||
$level = $row->$action;
|
||||
if (!isset($data->$scope->$action)) {
|
||||
@@ -623,7 +636,7 @@ class Table
|
||||
if (in_array($action, $this->booleanActionList)) {
|
||||
$data->$scope->$action = 'yes';
|
||||
} else {
|
||||
if (isset($data->$scope->$previousAction)) {
|
||||
if ($action === 'stream' && isset($data->$scope->$previousAction)) {
|
||||
$data->$scope->$action = $data->$scope->$previousAction;
|
||||
}
|
||||
}
|
||||
@@ -684,11 +697,12 @@ class Table
|
||||
|
||||
private function buildCache()
|
||||
{
|
||||
$contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';';
|
||||
$this->fileManager->putContents($this->cacheFilePath, $contents);
|
||||
$this->fileManager->putPhpContents($this->cacheFilePath, $this->data, true);
|
||||
/*$contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';';
|
||||
$this->fileManager->putContents($this->cacheFilePath, $contents);*/
|
||||
}
|
||||
|
||||
private function varExport($variable)
|
||||
/*private function varExport($variable)
|
||||
{
|
||||
if ($variable instanceof \StdClass) {
|
||||
$result = '(object) ' . $this->varExport(get_object_vars($variable), true);
|
||||
@@ -703,6 +717,6 @@ class Table
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class Base extends \Espo\Core\Acl\Base
|
||||
}
|
||||
|
||||
if (!isset($data->$action)) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $data->$action;
|
||||
|
||||
@@ -222,7 +222,7 @@ class Application
|
||||
}
|
||||
|
||||
try {
|
||||
$controllerManager = new \Espo\Core\ControllerManager($container);
|
||||
$controllerManager = $this->getContainer()->get('controllerManager');
|
||||
$result = $controllerManager->process($controllerName, $actionName, $params, $data, $slim->request());
|
||||
$container->get('output')->render($result);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -135,6 +135,13 @@ class Container
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadControllerManager()
|
||||
{
|
||||
return new \Espo\Core\ControllerManager(
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadPreferences()
|
||||
{
|
||||
return $this->get('entityManager')->getEntity('Preferences', $this->get('user')->id);
|
||||
@@ -181,7 +188,7 @@ class Container
|
||||
|
||||
protected function loadNumber()
|
||||
{
|
||||
return new \Espo\Core\Utils\Number(
|
||||
return new \Espo\Core\Utils\NumberUtil(
|
||||
$this->get('config')->get('decimalMark'),
|
||||
$this->get('config')->get('thousandSeparator')
|
||||
);
|
||||
@@ -209,8 +216,8 @@ class Container
|
||||
protected function loadMetadata()
|
||||
{
|
||||
return new \Espo\Core\Utils\Metadata(
|
||||
$this->get('config'),
|
||||
$this->get('fileManager')
|
||||
$this->get('fileManager'),
|
||||
$this->get('config')->get('useCache')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -247,7 +254,17 @@ class Container
|
||||
$this->get('metadata'),
|
||||
$this->get('fileManager'),
|
||||
$this->get('entityManager'),
|
||||
$this->get('classParser')
|
||||
$this->get('classParser'),
|
||||
$this->get('ormMetadata')
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadOrmMetadata()
|
||||
{
|
||||
return new \Espo\Core\Utils\Metadata\OrmMetadata(
|
||||
$this->get('metadata'),
|
||||
$this->get('fileManager'),
|
||||
$this->get('config')->get('useCache')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -263,10 +280,20 @@ class Container
|
||||
protected function loadLanguage()
|
||||
{
|
||||
return new \Espo\Core\Utils\Language(
|
||||
\Espo\Core\Utils\Language::detectLanguage($this->get('config'), $this->get('preferences')),
|
||||
$this->get('fileManager'),
|
||||
$this->get('config'),
|
||||
$this->get('metadata'),
|
||||
$this->get('preferences')
|
||||
$this->get('config')->get('useCache')
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadDefaultLanguage()
|
||||
{
|
||||
return new \Espo\Core\Utils\Language(
|
||||
null,
|
||||
$this->get('fileManager'),
|
||||
$this->get('metadata'),
|
||||
$this->get('useCache')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,10 @@ class Record extends Base
|
||||
|
||||
public function actionExport($params, $data, $request)
|
||||
{
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if ($this->getConfig()->get('exportDisabled') && !$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
@@ -234,17 +238,23 @@ class Record extends Base
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$ids = $request->get('ids');
|
||||
$where = $request->get('where');
|
||||
$byWhere = $request->get('byWhere');
|
||||
$ids = isset($data['ids']) ? $data['ids'] : null;
|
||||
$where = isset($data['where']) ? json_decode(json_encode($data['where']), true) : null;
|
||||
$byWhere = isset($data['byWhere']) ? $data['byWhere'] : false;
|
||||
$selectData = isset($data['selectData']) ? json_decode(json_encode($data['selectData']), true) : null;
|
||||
|
||||
$params = array();
|
||||
if ($byWhere) {
|
||||
$params['selectData'] = $selectData;
|
||||
$params['where'] = $where;
|
||||
} else {
|
||||
$params['ids'] = $ids;
|
||||
}
|
||||
|
||||
if (isset($data['attributeList'])) {
|
||||
$params['attributeList'] = $data['attributeList'];
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $this->getRecordService()->export($params)
|
||||
);
|
||||
@@ -266,6 +276,9 @@ class Record extends Base
|
||||
$params = array();
|
||||
if (array_key_exists('where', $data) && !empty($data['byWhere'])) {
|
||||
$params['where'] = json_decode(json_encode($data['where']), true);
|
||||
if (array_key_exists('selectData', $data)) {
|
||||
$params['selectData'] = json_decode(json_encode($data['selectData']), true);
|
||||
}
|
||||
} else if (array_key_exists('ids', $data)) {
|
||||
$params['ids'] = $data['ids'];
|
||||
}
|
||||
@@ -290,6 +303,9 @@ class Record extends Base
|
||||
if (array_key_exists('where', $data) && !empty($data['byWhere'])) {
|
||||
$where = json_decode(json_encode($data['where']), true);
|
||||
$params['where'] = $where;
|
||||
if (array_key_exists('selectData', $data)) {
|
||||
$params['selectData'] = json_decode(json_encode($data['selectData']), true);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('ids', $data)) {
|
||||
$params['ids'] = $data['ids'];
|
||||
@@ -318,7 +334,13 @@ class Record extends Base
|
||||
throw new BadRequest();
|
||||
}
|
||||
$where = json_decode(json_encode($data['where']), true);
|
||||
return $this->getRecordService()->linkEntityMass($id, $link, $where);
|
||||
|
||||
$selectData = null;
|
||||
if (isset($data['selectData']) && is_array($data['selectData'])) {
|
||||
$selectData = json_decode(json_encode($data['selectData']), true);
|
||||
}
|
||||
|
||||
return $this->getRecordService()->linkEntityMass($id, $link, $where, $selectData);
|
||||
} else {
|
||||
$foreignIdList = array();
|
||||
if (isset($data['id'])) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core;
|
||||
|
||||
class DataManager
|
||||
{
|
||||
private $container;
|
||||
@@ -113,11 +114,11 @@ class DataManager
|
||||
|
||||
$metadata->init(true);
|
||||
|
||||
$ormMeta = $metadata->getOrmMetadata(true);
|
||||
$ormData = $this->getContainer()->get('ormMetadata')->getData(true);
|
||||
|
||||
$this->updateCacheTimestamp();
|
||||
|
||||
return empty($ormMeta) ? false : true;
|
||||
return empty($ormData) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,8 @@ class HookManager
|
||||
'afterSave',
|
||||
'beforeRemove',
|
||||
'afterRemove',
|
||||
'afterRelate',
|
||||
'afterUnrelate'
|
||||
);
|
||||
|
||||
protected $paths = array(
|
||||
@@ -63,7 +65,6 @@ class HookManager
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->loadHooks();
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
@@ -85,24 +86,30 @@ class HookManager
|
||||
|
||||
$metadata = $this->container->get('metadata');
|
||||
|
||||
$this->data = $this->getHookData($this->paths['corePath']);
|
||||
$data = $this->getHookData($this->paths['customPath']);
|
||||
|
||||
foreach ($metadata->getModuleList() as $moduleName) {
|
||||
$modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']);
|
||||
$this->data = array_merge_recursive($this->data, $this->getHookData($modulePath));
|
||||
$data = $this->getHookData($modulePath, $data);
|
||||
}
|
||||
|
||||
$this->data = array_merge_recursive($this->data, $this->getHookData($this->paths['customPath']));
|
||||
$data = $this->getHookData($this->paths['corePath'], $data);
|
||||
|
||||
$this->data = $this->sortHooks($data);
|
||||
|
||||
if ($this->getConfig()->get('useCache')) {
|
||||
$this->getFileManager()->putPhpContents($this->cacheFile, $this->data);
|
||||
}
|
||||
}
|
||||
|
||||
public function process($scope, $hookName, $injection = null, array $options = array())
|
||||
public function process($scope, $hookName, $injection = null, array $options = array(), array $hookData = array())
|
||||
{
|
||||
if (!isset($this->data)) {
|
||||
$this->loadHooks();
|
||||
}
|
||||
|
||||
if ($scope != 'Common') {
|
||||
$this->process('Common', $hookName, $injection, $options);
|
||||
$this->process('Common', $hookName, $injection, $options, $hookData);
|
||||
}
|
||||
|
||||
if (!empty($this->data[$scope])) {
|
||||
@@ -115,7 +122,7 @@ class HookManager
|
||||
}
|
||||
}
|
||||
$hook = $this->hooks[$className];
|
||||
$hook->$hookName($injection, $options);
|
||||
$hook->$hookName($injection, $options, $hookData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +138,7 @@ class HookManager
|
||||
}
|
||||
return $hook;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->error("Hook class '{$className}' does not exist.");
|
||||
}
|
||||
|
||||
@@ -141,14 +149,12 @@ class HookManager
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHookData($hookDirs)
|
||||
protected function getHookData($hookDirs, array $hookData = array())
|
||||
{
|
||||
if (is_string($hookDirs)) {
|
||||
$hookDirs = (array) $hookDirs;
|
||||
}
|
||||
|
||||
$hooks = array();
|
||||
|
||||
foreach ($hookDirs as $hookDir) {
|
||||
|
||||
if (file_exists($hookDir)) {
|
||||
@@ -157,6 +163,7 @@ class HookManager
|
||||
foreach ($fileList as $scopeName => $hookFiles) {
|
||||
|
||||
$hookScopeDirPath = Util::concatPath($hookDir, $scopeName);
|
||||
$normalizedScopeName = Util::normilizeScopeName($scopeName);
|
||||
|
||||
$scopeHooks = array();
|
||||
foreach($hookFiles as $hookFile) {
|
||||
@@ -164,32 +171,70 @@ class HookManager
|
||||
$className = Util::getClassName($hookFilePath);
|
||||
|
||||
foreach($this->hookList as $hookName) {
|
||||
if (method_exists($className, $hookName)) {
|
||||
$scopeHooks[$hookName][$className::$order][] = $className;
|
||||
$entityHookData = isset($hookData[$scopeName][$hookName]) ? $hookData[$scopeName][$hookName] : array();
|
||||
if (method_exists($className, $hookName) && !$this->isHookExists($className, $entityHookData)) {
|
||||
$hookData[$normalizedScopeName][$hookName][$className::$order][] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sort hooks by order
|
||||
foreach ($scopeHooks as $hookName => $hookList) {
|
||||
ksort($hookList);
|
||||
|
||||
$sortedHookList = array();
|
||||
foreach($hookList as $hookDetails) {
|
||||
$sortedHookList = array_merge($sortedHookList, $hookDetails);
|
||||
}
|
||||
|
||||
$normalizedScopeName = Util::normilizeScopeName($scopeName);
|
||||
|
||||
$hooks[$normalizedScopeName][$hookName] = isset($hooks[$normalizedScopeName][$hookName]) ? array_merge($hooks[$normalizedScopeName][$hookName], $sortedHookList) : $sortedHookList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $hookData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort hooks by an order
|
||||
*
|
||||
* @param array $scopeHooks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function sortHooks(array $unsortedHooks)
|
||||
{
|
||||
$hooks = array();
|
||||
|
||||
foreach ($unsortedHooks as $scopeName => $scopeHooks) {
|
||||
foreach ($scopeHooks as $hookName => $hookList) {
|
||||
ksort($hookList);
|
||||
|
||||
$sortedHookList = array();
|
||||
foreach($hookList as $hookDetails) {
|
||||
$sortedHookList = array_merge($sortedHookList, $hookDetails);
|
||||
}
|
||||
|
||||
$normalizedScopeName = Util::normilizeScopeName($scopeName);
|
||||
|
||||
$hooks[$normalizedScopeName][$hookName] = isset($hooks[$normalizedScopeName][$hookName]) ? array_merge($hooks[$normalizedScopeName][$hookName], $sortedHookList) : $sortedHookList;
|
||||
}
|
||||
}
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if hook exists in the list
|
||||
*
|
||||
* @param string $className
|
||||
* @param array $hookData
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isHookExists($className, array $hookData)
|
||||
{
|
||||
$class = preg_replace('/^.*\\\(.*)$/', '$1', $className);
|
||||
|
||||
foreach ($hookData as $key => $hookList) {
|
||||
foreach ($hookList as $rowHookName) {
|
||||
if (preg_match('/\\'.$class.'$/', $rowHookName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Utils\File\Manager as FileManager;
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Core\Utils\Number;
|
||||
use Espo\Core\Utils\NumberUtil;
|
||||
|
||||
require('vendor/zordius/lightncandy/src/lightncandy.php');
|
||||
|
||||
@@ -48,7 +48,7 @@ class Htmlizer
|
||||
|
||||
protected $acl;
|
||||
|
||||
public function __construct(FileManager $fileManager, DateTime $dateTime, Number $number, $acl = null)
|
||||
public function __construct(FileManager $fileManager, DateTime $dateTime, NumberUtil $number, $acl = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
$this->dateTime = $dateTime;
|
||||
@@ -61,15 +61,12 @@ class Htmlizer
|
||||
return $this->acl;
|
||||
}
|
||||
|
||||
protected function formatNumber($value)
|
||||
{
|
||||
return $this->number->format($value);
|
||||
}
|
||||
|
||||
protected function format($value)
|
||||
{
|
||||
if (is_float($value) || is_int($value)) {
|
||||
$value = $this->formatNumber($value);
|
||||
if (is_float($value)) {
|
||||
$value = $this->number->format($value, 2);
|
||||
} else if (is_int($value)) {
|
||||
$value = $this->number->format($value);
|
||||
} else if (is_string($value)) {
|
||||
$value = nl2br($value);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class EntityManager extends Base
|
||||
'user' => $config->get('database.user'),
|
||||
'charset' => $config->get('database.charset', 'utf8'),
|
||||
'password' => $config->get('database.password'),
|
||||
'metadata' => $this->getContainer()->get('metadata')->getOrmMetadata(),
|
||||
'metadata' => $this->getContainer()->get('ormMetadata')->getData(),
|
||||
'repositoryFactoryClassName' => '\\Espo\\Core\\ORM\\RepositoryFactory',
|
||||
'driver' => $config->get('database.driver'),
|
||||
'platform' => $config->get('database.platform')
|
||||
|
||||
@@ -36,7 +36,9 @@ class EntityManagerUtil extends Base
|
||||
$entityManager = new \Espo\Core\Utils\EntityManager(
|
||||
$this->getContainer()->get('metadata'),
|
||||
$this->getContainer()->get('language'),
|
||||
$this->getContainer()->get('fileManager')
|
||||
$this->getContainer()->get('fileManager'),
|
||||
$this->getContainer()->get('config'),
|
||||
$this->getContainer()
|
||||
);
|
||||
|
||||
return $entityManager;
|
||||
|
||||
44
application/Espo/Core/Loaders/TemplateFileManager.php
Normal file
44
application/Espo/Core/Loaders/TemplateFileManager.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Loaders;
|
||||
|
||||
class TemplateFileManager extends Base
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$templateFileManager = new \Espo\Core\Utils\TemplateFileManager(
|
||||
$this->getContainer()->get('config'),
|
||||
$this->getContainer()->get('metadata')
|
||||
);
|
||||
|
||||
return $templateFileManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,18 @@ class FiltersMatcher
|
||||
|
||||
}
|
||||
|
||||
protected function matchTo(Email $email, $filter)
|
||||
{
|
||||
if ($email->get('to')) {
|
||||
$toArr = explode(';', $email->get('to'));
|
||||
foreach ($toArr as $to) {
|
||||
if ($this->matchString(strtolower($filter->get('to')), strtolower($to))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function match(Email $email, $subject, $skipBody = false)
|
||||
{
|
||||
if (is_array($subject) || $subject instanceof \Traversable) {
|
||||
@@ -48,30 +60,42 @@ class FiltersMatcher
|
||||
}
|
||||
|
||||
foreach ($filterList as $filter) {
|
||||
if ($filter->get('from')) {
|
||||
if ($this->matchString(strtolower($filter->get('from')), strtolower($email->get('from')))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ($filter->get('to')) {
|
||||
if ($email->get('to')) {
|
||||
$toArr = explode(';', $email->get('to'));
|
||||
foreach ($toArr as $to) {
|
||||
if ($this->matchString(strtolower($filter->get('to')), strtolower($to))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($filter->get('subject')) {
|
||||
if ($this->matchString($filter->get('subject'), $email->get('name'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$filterCount = 0;
|
||||
|
||||
if (!$skipBody) {
|
||||
if ($this->matchBody($email, $filterList)) {
|
||||
if ($filter->get('from')) {
|
||||
$filterCount++;
|
||||
if (!$this->matchString(strtolower($filter->get('from')), strtolower($email->get('from')))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($filter->get('to')) {
|
||||
$filterCount++;
|
||||
if (!$this->matchTo($email, $filter)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($filter->get('subject')) {
|
||||
$filterCount++;
|
||||
if (!$this->matchString($filter->get('subject'), $email->get('name'))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$wordList = $filter->get('bodyContains');
|
||||
if (!empty($wordList)) {
|
||||
$filterCount++;
|
||||
if ($skipBody) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->matchBody($email, $filter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($filterCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -79,30 +103,19 @@ class FiltersMatcher
|
||||
return false;
|
||||
}
|
||||
|
||||
public function matchBody(Email $email, $subject)
|
||||
protected function matchBody(Email $email, $filter)
|
||||
{
|
||||
if (is_array($subject) || $subject instanceof \Traversable) {
|
||||
$filterList = $subject;
|
||||
} else {
|
||||
$filterList = [$subject];
|
||||
}
|
||||
|
||||
foreach ($filterList as $filter) {
|
||||
if ($filter->get('bodyContains')) {
|
||||
$phraseList = $filter->get('bodyContains');
|
||||
$body = $email->get('body');
|
||||
$bodyPlain = $email->get('bodyPlain');
|
||||
foreach ($phraseList as $phrase) {
|
||||
if (stripos($bodyPlain, $phrase) !== false) {
|
||||
return true;
|
||||
}
|
||||
if (stripos($body, $phrase) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$phraseList = $filter->get('bodyContains');
|
||||
$body = $email->get('body');
|
||||
$bodyPlain = $email->get('bodyPlain');
|
||||
foreach ($phraseList as $phrase) {
|
||||
if (stripos($bodyPlain, $phrase) !== false) {
|
||||
return true;
|
||||
}
|
||||
if (stripos($body, $phrase) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function matchString($pattern, $value)
|
||||
|
||||
@@ -64,235 +64,235 @@ class Importer
|
||||
return $this->filtersMatcher;
|
||||
}
|
||||
|
||||
public function importMessage($message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
|
||||
public function importMessage($parserType = 'ZendMail', $message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
|
||||
{
|
||||
try {
|
||||
$email = $this->getEntityManager()->getEntity('Email');
|
||||
$parser = $message->getParser();
|
||||
$parserClassName = '\\Espo\\Core\\Mail\\Parsers\\' . $parserType;
|
||||
|
||||
$email->set('isBeingImported', true);
|
||||
if (!$parser || get_class($parser) !== $parserClassName) {
|
||||
$parser = new $parserClassName($this->getEntityManager());
|
||||
}
|
||||
|
||||
$subject = $message->subject;
|
||||
if ($subject !== '0' && empty(trim($subject))) {
|
||||
$subject = '(No Subject)';
|
||||
$email = $this->getEntityManager()->getEntity('Email');
|
||||
|
||||
$email->set('isBeingImported', true);
|
||||
|
||||
$subject = '';
|
||||
if ($parser->checkMessageAttribute($message, 'subject')) {
|
||||
$subject = $parser->getMessageAttribute($message, 'subject');
|
||||
}
|
||||
if (!empty($subject) && is_string($subject)) {
|
||||
$subject = trim($subject);
|
||||
}
|
||||
if ($subject !== '0' && empty($subject)) {
|
||||
$subject = '(No Subject)';
|
||||
}
|
||||
|
||||
$email->set('isHtml', false);
|
||||
$email->set('name', $subject);
|
||||
$email->set('status', 'Archived');
|
||||
$email->set('attachmentsIds', []);
|
||||
if ($assignedUserId) {
|
||||
$email->set('assignedUserId', $assignedUserId);
|
||||
$email->addLinkMultipleId('assignedUsers', $assignedUserId);
|
||||
}
|
||||
$email->set('teamsIds', $teamsIdList);
|
||||
|
||||
if (!empty($userIdList)) {
|
||||
foreach ($userIdList as $uId) {
|
||||
$email->addLinkMultipleId('users', $uId);
|
||||
}
|
||||
}
|
||||
|
||||
$email->set('isHtml', false);
|
||||
$email->set('name', $subject);
|
||||
$email->set('status', 'Archived');
|
||||
$email->set('attachmentsIds', []);
|
||||
if ($parser->checkMessageAttribute($message, 'from')) {
|
||||
$email->set('fromString', $parser->getMessageAttribute($message, 'from'));
|
||||
}
|
||||
|
||||
if ($parser->checkMessageAttribute($message, 'reply-To')) {
|
||||
$email->set('replyToString', $parser->getMessageAttribute($message, 'reply-To'));
|
||||
}
|
||||
|
||||
$fromArr = $parser->getAddressListFromMessage($message, 'from');
|
||||
$toArr = $parser->getAddressListFromMessage($message, 'to');
|
||||
$ccArr = $parser->getAddressListFromMessage($message, 'cc');
|
||||
$replyToArr = $parser->getAddressListFromMessage($message, 'reply-To');
|
||||
|
||||
if (count($fromArr)) {
|
||||
$email->set('from', $fromArr[0]);
|
||||
}
|
||||
$email->set('to', implode(';', $toArr));
|
||||
$email->set('cc', implode(';', $ccArr));
|
||||
$email->set('replyTo', implode(';', $replyToArr));
|
||||
|
||||
if ($folderData) {
|
||||
foreach ($folderData as $uId => $folderId) {
|
||||
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getFiltersMatcher()->match($email, $filterList, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($parser->checkMessageAttribute($message, 'message-Id') && $parser->getMessageAttribute($message, 'message-Id')) {
|
||||
$messageId = $parser->getMessageMessageId($message);
|
||||
|
||||
$email->set('messageId', $messageId);
|
||||
if ($parser->checkMessageAttribute($message, 'delivered-To')) {
|
||||
$email->set('messageIdInternal', $messageId . '-' . $parser->getMessageAttribute($message, 'delivered-To'));
|
||||
}
|
||||
if (stripos($messageId, '@espo-system') !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($duplicate = $this->findDuplicate($email)) {
|
||||
if ($assignedUserId) {
|
||||
$email->set('assignedUserId', $assignedUserId);
|
||||
$email->addLinkMultipleId('assignedUsers', $assignedUserId);
|
||||
$duplicate->addLinkMultipleId('users', $assignedUserId);
|
||||
$duplicate->addLinkMultipleId('assignedUsers', $assignedUserId);
|
||||
}
|
||||
$email->set('teamsIds', $teamsIdList);
|
||||
|
||||
if (!empty($userIdList)) {
|
||||
foreach ($userIdList as $uId) {
|
||||
$email->addLinkMultipleId('users', $uId);
|
||||
$duplicate->addLinkMultipleId('users', $uId);
|
||||
}
|
||||
}
|
||||
|
||||
$fromArr = $this->getAddressListFromMessage($message, 'from');
|
||||
if (isset($message->from)) {
|
||||
$email->set('fromString', $message->from);
|
||||
}
|
||||
if (isset($message->replyTo)) {
|
||||
$email->set('replyToString', $message->replyTo);
|
||||
}
|
||||
|
||||
$toArr = $this->getAddressListFromMessage($message, 'to');
|
||||
$ccArr = $this->getAddressListFromMessage($message, 'cc');
|
||||
$replyToArr = $this->getAddressListFromMessage($message, 'replyTo');
|
||||
|
||||
$email->set('from', $fromArr[0]);
|
||||
$email->set('to', implode(';', $toArr));
|
||||
$email->set('cc', implode(';', $ccArr));
|
||||
$email->set('replyTo', implode(';', $replyToArr));
|
||||
|
||||
if ($folderData) {
|
||||
foreach ($folderData as $uId => $folderId) {
|
||||
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getFiltersMatcher()->match($email, $filterList, true)) {
|
||||
return false;
|
||||
}
|
||||
$duplicate->set('isBeingImported', true);
|
||||
|
||||
if (isset($message->messageId) && !empty($message->messageId)) {
|
||||
$email->set('messageId', $message->messageId);
|
||||
if (isset($message->deliveredTo)) {
|
||||
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
|
||||
}
|
||||
if (stripos($message->messageId, '@espo-system') !== false) {
|
||||
return;
|
||||
$this->getEntityManager()->saveEntity($duplicate);
|
||||
|
||||
if (!empty($teamsIdList)) {
|
||||
foreach ($teamsIdList as $teamId) {
|
||||
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);
|
||||
}
|
||||
}
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
if ($duplicate = $this->findDuplicate($email)) {
|
||||
if ($assignedUserId) {
|
||||
$duplicate->addLinkMultipleId('users', $assignedUserId);
|
||||
$duplicate->addLinkMultipleId('assignedUsers', $assignedUserId);
|
||||
}
|
||||
if (!empty($userIdList)) {
|
||||
foreach ($userIdList as $uId) {
|
||||
$duplicate->addLinkMultipleId('users', $uId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($folderData) {
|
||||
foreach ($folderData as $uId => $folderId) {
|
||||
$email->setLinkMultipleColumn('users', 'folderId', $uId, $folderId);
|
||||
}
|
||||
}
|
||||
|
||||
$duplicate->set('isBeingImported', true);
|
||||
|
||||
$this->getEntityManager()->saveEntity($duplicate);
|
||||
|
||||
if (!empty($teamsIdList)) {
|
||||
foreach ($teamsIdList as $teamId) {
|
||||
$this->getEntityManager()->getRepository('Email')->relate($duplicate, 'teams', $teamId);
|
||||
}
|
||||
}
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
if (isset($message->date)) {
|
||||
$dt = new \DateTime($message->date);
|
||||
if ($parser->checkMessageAttribute($message, 'date')) {
|
||||
try {
|
||||
$dt = new \DateTime($parser->getMessageAttribute($message, 'date'));
|
||||
if ($dt) {
|
||||
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('dateSent', $dateSent);
|
||||
}
|
||||
} else {
|
||||
$email->set('dateSent', date('Y-m-d H:i:s'));
|
||||
}
|
||||
if (isset($message->deliveryDate)) {
|
||||
$dt = new \DateTime($message->deliveryDate);
|
||||
} catch (\Exception $e) {}
|
||||
} else {
|
||||
$email->set('dateSent', date('Y-m-d H:i:s'));
|
||||
}
|
||||
if ($parser->checkMessageAttribute($message, 'delivery-Date')) {
|
||||
try {
|
||||
$dt = new \DateTime($parser->getMessageAttribute($message, 'delivery-Date'));
|
||||
if ($dt) {
|
||||
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('deliveryDate', $deliveryDate);
|
||||
$email->set('delivery-Date', $deliveryDate);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
if (!$fetchOnlyHeader) {
|
||||
$parser->fetchContentParts($email, $message);
|
||||
|
||||
if ($this->getFiltersMatcher()->match($email, $filterList)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$email->set('body', '(Not fetched)');
|
||||
$email->set('isHtml', false);
|
||||
}
|
||||
|
||||
$inlineIds = array();
|
||||
$parentFound = false;
|
||||
|
||||
if (!$fetchOnlyHeader) {
|
||||
if ($message->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($message) as $part) {
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
$this->importPartDataToEmail($email, $message, $inlineIds, 'text/plain');
|
||||
}
|
||||
$replied = null;
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
$body = $email->get('body');
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
if (strpos($body, 'cid:' . $cid) !== false) {
|
||||
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&id=' . $attachmentId, $body);
|
||||
} else {
|
||||
$email->addLinkMultipleId('attachments', $attachmentId);
|
||||
}
|
||||
}
|
||||
$email->set('body', $body);
|
||||
}
|
||||
|
||||
if ($this->getFiltersMatcher()->matchBody($email, $filterList)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$email->set('body', '(Not fetched)');
|
||||
$email->set('isHtml', false);
|
||||
if ($parser->checkMessageAttribute($message, 'in-Reply-To') && $parser->getMessageAttribute($message, 'in-Reply-To')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'in-Reply-To'));
|
||||
$inReplyTo = $arr[0];
|
||||
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageId' => $inReplyTo
|
||||
))->findOne();
|
||||
if ($replied) {
|
||||
$email->set('repliedId', $replied->id);
|
||||
}
|
||||
}
|
||||
|
||||
$parentFound = false;
|
||||
|
||||
$replied = null;
|
||||
if (isset($message->inReplyTo) && !empty($message->inReplyTo)) {
|
||||
$arr = explode(' ', $message->inReplyTo);
|
||||
$inReplyTo = $arr[0];
|
||||
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageId' => $inReplyTo
|
||||
))->findOne();
|
||||
if ($replied) {
|
||||
$email->set('repliedId', $replied->id);
|
||||
}
|
||||
if ($parser->checkMessageAttribute($message, 'references') && $parser->getMessageAttribute($message, 'references')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'references'));
|
||||
$reference = $arr[0];
|
||||
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
$emailSent = PHP_INT_MAX;
|
||||
$number = null;
|
||||
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
|
||||
if ($n != 4) {
|
||||
$n = sscanf($reference, '%s %s %d %d espo-system', $parentType, $parentId, $emailSent, $number);
|
||||
}
|
||||
|
||||
if (isset($message->references) && !empty($message->references)) {
|
||||
$arr = explode(' ', $message->references);
|
||||
$reference = $arr[0];
|
||||
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
$emailSent = PHP_INT_MAX;
|
||||
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
|
||||
if ($n == 4 && $emailSent < time()) {
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
if ($parentType == 'Lead') {
|
||||
$parent = $this->getEntityManager()->getEntity('Lead', $parentId);
|
||||
if ($parent && $parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$account = $this->getEntityManager()->getEntity('Account', $parent->get('createdAccountId'));
|
||||
if ($account) {
|
||||
$parentType = 'Account';
|
||||
$parentId = $account->id;
|
||||
}
|
||||
} else {
|
||||
if ($this->getConfig()->get('b2cMode')) {
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contact = $this->getEntityManager()->getEntity('Contact', $parent->get('createdContactId'));
|
||||
if ($contact) {
|
||||
$parentType = 'Contact';
|
||||
$parentId = $contact->id;
|
||||
}
|
||||
if ($n == 4 && $emailSent < time()) {
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
if ($parentType == 'Lead') {
|
||||
$parent = $this->getEntityManager()->getEntity('Lead', $parentId);
|
||||
if ($parent && $parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$account = $this->getEntityManager()->getEntity('Account', $parent->get('createdAccountId'));
|
||||
if ($account) {
|
||||
$parentType = 'Account';
|
||||
$parentId = $account->id;
|
||||
}
|
||||
} else {
|
||||
if ($this->getConfig()->get('b2cMode')) {
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contact = $this->getEntityManager()->getEntity('Contact', $parent->get('createdContactId'));
|
||||
if ($contact) {
|
||||
$parentType = 'Contact';
|
||||
$parentId = $contact->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$email->set('parentType', $parentType);
|
||||
$email->set('parentId', $parentId);
|
||||
$parentFound = true;
|
||||
}
|
||||
$email->set('parentType', $parentType);
|
||||
$email->set('parentId', $parentId);
|
||||
$parentFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$parentFound) {
|
||||
if ($replied && $replied->get('parentId') && $replied->get('parentType')) {
|
||||
$parentFound = $this->getEntityManager()->getEntity($replied->get('parentType'), $replied->get('parentId'));
|
||||
if ($parentFound) {
|
||||
$email->set('parentType', $replied->get('parentType'));
|
||||
$email->set('parentId', $replied->get('parentId'));
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if ($replied && $replied->get('parentId') && $replied->get('parentType')) {
|
||||
$parentFound = $this->getEntityManager()->getEntity($replied->get('parentType'), $replied->get('parentId'));
|
||||
if ($parentFound) {
|
||||
$email->set('parentType', $replied->get('parentType'));
|
||||
$email->set('parentId', $replied->get('parentId'));
|
||||
}
|
||||
}
|
||||
if (!$parentFound) {
|
||||
$from = $email->get('from');
|
||||
if ($from) {
|
||||
$parentFound = $this->findParent($email, $from);
|
||||
}
|
||||
}
|
||||
if (!$parentFound) {
|
||||
$from = $email->get('from');
|
||||
if ($from) {
|
||||
$parentFound = $this->findParent($email, $from);
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if (!empty($replyToArr)) {
|
||||
$parentFound = $this->findParent($email, $replyToArr[0]);
|
||||
}
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if (!empty($replyToArr)) {
|
||||
$parentFound = $this->findParent($email, $replyToArr[0]);
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if (!empty($toArr)) {
|
||||
$parentFound = $this->findParent($email, $toArr[0]);
|
||||
}
|
||||
}
|
||||
if (!$parentFound) {
|
||||
if (!empty($toArr)) {
|
||||
$parentFound = $this->findParent($email, $toArr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->getEntityManager()->saveEntity($email);
|
||||
$this->getEntityManager()->saveEntity($email);
|
||||
|
||||
return $email;
|
||||
|
||||
} catch (\Exception $e) {}
|
||||
return $email;
|
||||
}
|
||||
|
||||
protected function findParent(Entity $email, $emailAddress)
|
||||
@@ -321,15 +321,15 @@ class Importer
|
||||
$email->set('parentId', $account->id);
|
||||
return true;
|
||||
} else {
|
||||
$lead = $this->getEntityManager()->getRepository('Lead')->where(array(
|
||||
'emailAddress' => $emailAddress
|
||||
))->findOne();
|
||||
if ($lead) {
|
||||
$email->set('parentType', 'Lead');
|
||||
$email->set('parentId', $lead->id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$lead = $this->getEntityManager()->getRepository('Lead')->where(array(
|
||||
'emailAddress' => $emailAddress
|
||||
))->findOne();
|
||||
if ($lead) {
|
||||
$email->set('parentType', 'Lead');
|
||||
$email->set('parentId', $lead->id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,243 +344,4 @@ class Importer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function normilizeHeader($header)
|
||||
{
|
||||
if (is_a($header, 'ArrayIterator')) {
|
||||
return $header->current();
|
||||
} else {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$addressList = array();
|
||||
if (isset($message->$type)) {
|
||||
$list = $this->normilizeHeader($message->getHeader($type))->getAddressList();
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address->getEmail();
|
||||
}
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
|
||||
{
|
||||
try {
|
||||
$type = null;
|
||||
|
||||
if ($part->getHeaders() && isset($part->contentType)) {
|
||||
$type = strtok($part->contentType, ';');
|
||||
}
|
||||
|
||||
$contentDisposition = false;
|
||||
if (isset($part->ContentDisposition)) {
|
||||
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
|
||||
$contentDisposition = 'attachment';
|
||||
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
} else if (isset($part->contentID)) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
if (!empty($defaultContentType)) {
|
||||
$type = $defaultContentType;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$encoding = null;
|
||||
$isAttachment = true;
|
||||
if ($type == 'text/plain' || $type == 'text/html') {
|
||||
if ($contentDisposition !== 'attachment') {
|
||||
$isAttachment = false;
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
$bodyPlain = '';
|
||||
if ($email->get('bodyPlain')) {
|
||||
$bodyPlain .= $email->get('bodyPlain') . "\n";
|
||||
}
|
||||
$bodyPlain .= $content;
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
} else if ($type == 'text/html') {
|
||||
$body = '';
|
||||
if ($email->get('body')) {
|
||||
$body .= $email->get('body') . "<br>";
|
||||
}
|
||||
$body .= $content;
|
||||
$email->set('isHtml', true);
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAttachment) {
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
if ($contentDisposition) {
|
||||
if ($contentDisposition === 'attachment') {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
} else if ($contentDisposition === 'inline') {
|
||||
if (isset($part->contentID)) {
|
||||
$contentId = trim($part->contentID, '<>');
|
||||
$fileName = $contentId;
|
||||
$disposition = 'inline';
|
||||
} else {
|
||||
// for iOS attachments
|
||||
if (empty($fileName)) {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$attachment->set('contents', $content);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function decodeAttachmentFileName($fileName)
|
||||
{
|
||||
if ($fileName && stripos($fileName, "''") !== false) {
|
||||
list($encoding, $fileName) = explode("''", $fileName);
|
||||
$fileName = rawurldecode($fileName);
|
||||
if (strtoupper($encoding) !== 'UTF-8') {
|
||||
if ($encoding) {
|
||||
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function fetchFileNameFromContentDisposition($contentDisposition)
|
||||
{
|
||||
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
|
||||
|
||||
$fileName = false;
|
||||
$m = array();
|
||||
|
||||
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else {
|
||||
$fileName = '';
|
||||
foreach (['0', '1'] as $i) {
|
||||
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName === '') {
|
||||
$fileName = null;
|
||||
} else {
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function getContentFromPart($part)
|
||||
{
|
||||
if ($part instanceof \Zend\Mime\Part) {
|
||||
$content = $part->getRawContent();
|
||||
if (strtolower($part->charset) != 'utf-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
|
||||
}
|
||||
} else {
|
||||
$content = $part->getContent();
|
||||
|
||||
$encoding = null;
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
$encoding = strtolower($cteHeader->getTransferEncoding());
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
if (isset($part->contentType)) {
|
||||
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
|
||||
$charsetParamValue = $ctHeader->getParameter('charset');
|
||||
if (!empty($charsetParamValue)) {
|
||||
$charset = strtoupper($charsetParamValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
|
||||
$content = quoted_printable_decode($content);
|
||||
}
|
||||
}
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace Espo\Core\Mail\Mail\Storage;
|
||||
|
||||
class Imap extends \Zend\Mail\Storage\Imap
|
||||
{
|
||||
protected $messageClass = '\\Espo\\Core\\Mail\\Mail\\Storage\\Message';
|
||||
|
||||
public function getIdsFromUID($uid)
|
||||
{
|
||||
$uid = intval($uid) + 1;
|
||||
@@ -44,5 +42,22 @@ class Imap extends \Zend\Mail\Storage\Imap
|
||||
return $this->protocol->search(array('SINCE "' . $date . '"'));
|
||||
}
|
||||
|
||||
public function getHeaderAndFlags($id, $part = null)
|
||||
{
|
||||
$data = $this->protocol->fetch(['FLAGS', 'RFC822.HEADER'], $id);
|
||||
|
||||
$header = $data['RFC822.HEADER'];
|
||||
|
||||
$flags = [];
|
||||
foreach ($data['FLAGS'] as $flag) {
|
||||
$flags[] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag;
|
||||
}
|
||||
|
||||
return array(
|
||||
'flags' => $flags,
|
||||
'header' => $header
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
144
application/Espo/Core/Mail/MessageWrapper.php
Normal file
144
application/Espo/Core/Mail/MessageWrapper.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail;
|
||||
|
||||
use \Espo\Entities\Email;
|
||||
|
||||
class MessageWrapper
|
||||
{
|
||||
private $storage;
|
||||
|
||||
private $id;
|
||||
|
||||
private $rawHeader = null;
|
||||
|
||||
private $rawContent = null;
|
||||
|
||||
private $zendMessage = null;
|
||||
|
||||
protected $zendMessageClass = '\Zend\Mail\Storage\Message';
|
||||
|
||||
protected $fullRawContent = null;
|
||||
|
||||
protected $flagList = null;
|
||||
|
||||
public function __construct($storage = null, $id = null, $parser = null)
|
||||
{
|
||||
if ($storage) {
|
||||
$data = $storage->getHeaderAndFlags($id);
|
||||
$this->rawHeader = $data['header'];
|
||||
$this->flagList = $data['flags'];
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
$this->storage = $storage;
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
public function setFullRawContent($content)
|
||||
{
|
||||
$this->fullRawContent = $content;
|
||||
}
|
||||
|
||||
public function getRawHeader()
|
||||
{
|
||||
return $this->rawHeader;
|
||||
}
|
||||
|
||||
public function getParser()
|
||||
{
|
||||
return $this->parser;
|
||||
}
|
||||
|
||||
public function checkAttribute($attribute)
|
||||
{
|
||||
return $this->getParser()->checkMessageAttribute($this, $attribute);
|
||||
}
|
||||
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
return $this->getParser()->getMessageAttribute($this, $attribute);
|
||||
}
|
||||
|
||||
public function getRawContent()
|
||||
{
|
||||
if (is_null($this->rawContent)) {
|
||||
$this->rawContent = $this->storage->getRawContent($this->id);
|
||||
}
|
||||
|
||||
return $this->rawContent;
|
||||
}
|
||||
|
||||
public function getFullRawContent()
|
||||
{
|
||||
if ($this->fullRawContent) {
|
||||
return $this->fullRawContent;
|
||||
}
|
||||
|
||||
return $this->getRawHeader() . "\n" . $this->getRawContent();
|
||||
}
|
||||
|
||||
public function getZendMessage()
|
||||
{
|
||||
if (!$this->zendMessage) {
|
||||
$data = array();
|
||||
if ($this->storage) {
|
||||
$data['handler'] = $this->storage;
|
||||
}
|
||||
if ($this->flagList) {
|
||||
$data['flags'] = $this->flagList;
|
||||
}
|
||||
if ($this->fullRawContent) {
|
||||
$data['raw'] = $this->fullRawContent;
|
||||
} else {
|
||||
if ($this->rawHeader) {
|
||||
$data['headers'] = $this->rawHeader;
|
||||
}
|
||||
}
|
||||
if ($this->id) {
|
||||
$data['id'] = $this->id;
|
||||
}
|
||||
|
||||
$this->zendMessage = new $this->zendMessageClass($data);
|
||||
}
|
||||
|
||||
return $this->zendMessage;
|
||||
}
|
||||
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flagList;
|
||||
}
|
||||
|
||||
public function isFetched()
|
||||
{
|
||||
return !!$this->rawHeader;
|
||||
}
|
||||
}
|
||||
167
application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php
Normal file
167
application/Espo/Core/Mail/Parsers/PhpMimeMailParser.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Parsers;
|
||||
|
||||
class PhpMimeMailParser
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
private $parserHash = array();
|
||||
|
||||
public function __construct($entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
protected function getParser($message)
|
||||
{
|
||||
$key = spl_object_hash($message);
|
||||
if (!array_key_exists($key, $this->parserHash)) {
|
||||
$this->parserHash[$key] = new PhpMimeMailParser\Parser();
|
||||
$raw = $message->getRawHeader();
|
||||
if (!$raw) {
|
||||
$raw = $message->getFullRawContent();
|
||||
}
|
||||
$this->parserHash[$key]->setText($raw);
|
||||
}
|
||||
|
||||
return $this->parserHash[$key];
|
||||
}
|
||||
|
||||
protected function loadContent($message)
|
||||
{
|
||||
$this->getParser($message);
|
||||
|
||||
$raw = $message->getFullRawContent();
|
||||
$this->getParser($message)->setText($raw);
|
||||
}
|
||||
|
||||
public function checkMessageAttribute($message, $attribute)
|
||||
{
|
||||
return $this->getParser($message)->getHeader($attribute) !== false;
|
||||
}
|
||||
|
||||
public function getMessageAttribute($message, $attribute)
|
||||
{
|
||||
if (!$this->checkMessageAttribute($message, $attribute)) return null;
|
||||
|
||||
return $this->getParser($message)->getHeader($attribute);
|
||||
}
|
||||
|
||||
public function getMessageMessageId($message)
|
||||
{
|
||||
return $this->getMessageAttribute($message, 'Message-ID');
|
||||
}
|
||||
|
||||
public function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$addressList = [];
|
||||
if ($this->checkMessageAttribute($message, $type)) {
|
||||
$list = $this->getParser($message)->getAddresses($type);
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address['address'];
|
||||
}
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
public function fetchContentParts(\Espo\Entities\Email $email, $message)
|
||||
{
|
||||
$this->loadContent($message);
|
||||
|
||||
$bodyPlain = $this->getParser($message)->getMessageBody('text');
|
||||
$bodyHtml = $this->getParser($message)->getMessageBody('html');
|
||||
|
||||
if ($bodyHtml) {
|
||||
$email->set('isHtml', true);
|
||||
$email->set('body', $bodyHtml);
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
} else {
|
||||
$email->set('isHtml', false);
|
||||
$email->set('body', $bodyPlain);
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
$attachmentObjList = $this->getParser($message)->getAttachments();
|
||||
$inlineIds = array();
|
||||
|
||||
foreach ($attachmentObjList as $attachmentObj) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
|
||||
$content = $attachmentObj->getContent();
|
||||
$disposition = $attachmentObj->getContentDisposition();
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $attachmentObj->getFileName());
|
||||
$attachment->set('type', $attachmentObj->getContentType());
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
$contentId = $attachmentObj->getContentID();
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
$attachment->set('contents', $content);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
|
||||
$body = $email->get('body');
|
||||
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
if (strpos($body, 'cid:' . $cid) !== false) {
|
||||
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&id=' . $attachmentId, $body);
|
||||
} else {
|
||||
$email->addLinkMultipleId('attachments', $attachmentId);
|
||||
}
|
||||
}
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Parsers\PhpMimeMailParser;
|
||||
|
||||
use \PhpMimeMailParser\Attachment;
|
||||
|
||||
class Parser extends \PhpMimeMailParser\Parser
|
||||
{
|
||||
public function getAttachments()
|
||||
{
|
||||
$attachments = [];
|
||||
$dispositions = ['attachment', 'inline'];
|
||||
$non_attachment_types = ['text/plain', 'text/html'];
|
||||
$nonameIter = 0;
|
||||
|
||||
foreach ($this->parts as $part) {
|
||||
$disposition = $this->getPart('content-disposition', $part);
|
||||
$filename = 'noname';
|
||||
|
||||
if (isset($part['disposition-filename'])) {
|
||||
$filename = $this->decodeHeader($part['disposition-filename']);
|
||||
} elseif (isset($part['content-name'])) {
|
||||
// if we have no disposition but we have a content-name, it's a valid attachment.
|
||||
// we simulate the presence of an attachment disposition with a disposition filename
|
||||
$filename = $this->decodeHeader($part['content-name']);
|
||||
if (!$disposition) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
} elseif (!in_array($part['content-type'], $non_attachment_types, true)
|
||||
&& substr($part['content-type'], 0, 10) !== 'multipart/'
|
||||
) {
|
||||
// if we cannot get it by getMessageBody(), we assume it is an attachment
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
|
||||
if (in_array($disposition, $dispositions) === true && isset($filename) === true) {
|
||||
if ($filename == 'noname') {
|
||||
$nonameIter++;
|
||||
$filename = 'noname'.$nonameIter;
|
||||
}
|
||||
|
||||
$headersAttachments = $this->getPart('headers', $part);
|
||||
$contentidAttachments = $this->getPart('content-id', $part);
|
||||
|
||||
$mimePartStr = $this->getPartComplete($part);
|
||||
|
||||
$attachments[] = new Attachment(
|
||||
$filename,
|
||||
$this->getPart('content-type', $part),
|
||||
$this->getAttachmentStream($part),
|
||||
$disposition,
|
||||
$contentidAttachments,
|
||||
$headersAttachments,
|
||||
$mimePartStr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $attachments;
|
||||
}
|
||||
}
|
||||
|
||||
349
application/Espo/Core/Mail/Parsers/ZendMail.php
Normal file
349
application/Espo/Core/Mail/Parsers/ZendMail.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Parsers;
|
||||
|
||||
class ZendMail
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct($entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
public function checkMessageAttribute($message, $attribute)
|
||||
{
|
||||
$zendMessage = $message->getZendMessage();
|
||||
|
||||
return isset($zendMessage->$attribute);
|
||||
}
|
||||
|
||||
public function getMessageAttribute($message, $attribute)
|
||||
{
|
||||
$zendMessage = $message->getZendMessage();
|
||||
|
||||
if (!isset($zendMessage->$attribute)) return null;
|
||||
|
||||
return $zendMessage->$attribute;
|
||||
}
|
||||
|
||||
public function getMessageMessageId($message)
|
||||
{
|
||||
$zendMessage = $message->getZendMessage();
|
||||
|
||||
if (!isset($zendMessage->messageId)) return null;
|
||||
|
||||
$messageId = $zendMessage->messageId;
|
||||
$messageId = str_replace('<<', '<', $messageId);
|
||||
$messageId = str_replace('>>', '>', $messageId);
|
||||
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
public function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$zendMessage = $message->getZendMessage();
|
||||
|
||||
$addressList = array();
|
||||
if (isset($zendMessage->$type)) {
|
||||
$list = $this->normilizeHeader($zendMessage->getHeader($type))->getAddressList();
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address->getEmail();
|
||||
}
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
public function fetchContentParts(\Espo\Entities\Email $email, $message)
|
||||
{
|
||||
$zendMessage = $message->getZendMessage();
|
||||
|
||||
$inlineIds = array();
|
||||
|
||||
if ($zendMessage->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($zendMessage) as $part) {
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
$this->importPartDataToEmail($email, $zendMessage, $inlineIds, 'text/plain');
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
$body = $email->get('body');
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
if (strpos($body, 'cid:' . $cid) !== false) {
|
||||
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&id=' . $attachmentId, $body);
|
||||
} else {
|
||||
$email->addLinkMultipleId('attachments', $attachmentId);
|
||||
}
|
||||
}
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
|
||||
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
|
||||
{
|
||||
try {
|
||||
$type = null;
|
||||
|
||||
if ($part->getHeaders() && isset($part->contentType)) {
|
||||
$type = strtok($part->contentType, ';');
|
||||
}
|
||||
|
||||
$contentDisposition = false;
|
||||
if (isset($part->ContentDisposition)) {
|
||||
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
|
||||
$contentDisposition = 'attachment';
|
||||
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
} else if (isset($part->contentID)) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
if (!empty($defaultContentType)) {
|
||||
$type = $defaultContentType;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$encoding = null;
|
||||
$isAttachment = true;
|
||||
if ($type == 'text/plain' || $type == 'text/html') {
|
||||
if ($contentDisposition !== 'attachment') {
|
||||
$isAttachment = false;
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
$bodyPlain = '';
|
||||
if ($email->get('bodyPlain')) {
|
||||
$bodyPlain .= $email->get('bodyPlain') . "\n";
|
||||
}
|
||||
$bodyPlain .= $content;
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
} else if ($type == 'text/html') {
|
||||
$body = '';
|
||||
if ($email->get('body')) {
|
||||
$body .= $email->get('body') . "<br>";
|
||||
}
|
||||
$body .= $content;
|
||||
$email->set('isHtml', true);
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAttachment) {
|
||||
$content = $part->getContent();
|
||||
|
||||
$disposition = null;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
if ($contentDisposition) {
|
||||
if ($contentDisposition === 'attachment') {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
} else if ($contentDisposition === 'inline') {
|
||||
if (isset($part->contentID)) {
|
||||
$contentId = trim($part->contentID, '<>');
|
||||
$fileName = $contentId;
|
||||
$disposition = 'inline';
|
||||
} else {
|
||||
// for iOS attachments
|
||||
if (empty($fileName)) {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$attachment->set('contents', $content);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function getContentFromPart($part)
|
||||
{
|
||||
if ($part instanceof \Zend\Mime\Part) {
|
||||
$content = $part->getRawContent();
|
||||
if (strtolower($part->charset) != 'utf-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
|
||||
}
|
||||
} else {
|
||||
$content = $part->getContent();
|
||||
|
||||
$encoding = null;
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
$encoding = strtolower($cteHeader->getTransferEncoding());
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
if (isset($part->contentType)) {
|
||||
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
|
||||
$charsetParamValue = $ctHeader->getParameter('charset');
|
||||
if (!empty($charsetParamValue)) {
|
||||
$charset = strtoupper($charsetParamValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
|
||||
$content = quoted_printable_decode($content);
|
||||
}
|
||||
}
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function normilizeHeader($header)
|
||||
{
|
||||
if (is_a($header, 'ArrayIterator')) {
|
||||
return $header->current();
|
||||
} else {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
||||
protected function fetchFileNameFromContentDisposition($contentDisposition)
|
||||
{
|
||||
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
|
||||
|
||||
$fileName = false;
|
||||
$m = array();
|
||||
|
||||
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else {
|
||||
$fileName = '';
|
||||
foreach (['0', '1'] as $i) {
|
||||
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName === '') {
|
||||
$fileName = null;
|
||||
} else {
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function decodeAttachmentFileName($fileName)
|
||||
{
|
||||
if ($fileName && stripos($fileName, "''") !== false) {
|
||||
list($encoding, $fileName) = explode("''", $fileName);
|
||||
$fileName = rawurldecode($fileName);
|
||||
if (strtoupper($encoding) !== 'UTF-8') {
|
||||
if ($encoding) {
|
||||
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ class Sender
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send(Email $email, $params = array(), &$message = null, $attachmetList = [])
|
||||
public function send(Email $email, $params = array(), &$message = null, $attachmentList = [])
|
||||
{
|
||||
if (!$message) {
|
||||
$message = new Message();
|
||||
@@ -190,6 +190,10 @@ class Sender
|
||||
$email->set('from', $fromAddress);
|
||||
}
|
||||
|
||||
$sender = new \Zend\Mail\Header\Sender();
|
||||
$sender->setAddress($email->get('from'));
|
||||
$message->getHeaders()->addHeader($sender);
|
||||
|
||||
if (!empty($params['replyToAddress'])) {
|
||||
$replyToName = null;
|
||||
if (!empty($params['replyToName'])) {
|
||||
@@ -242,7 +246,7 @@ class Sender
|
||||
$attachmentCollection = $email->get('attachments');
|
||||
$attachmentInlineCollection = $email->getInlineAttachments();
|
||||
|
||||
foreach ($attachmetList as $attachment) {
|
||||
foreach ($attachmentList as $attachment) {
|
||||
$attachmentCollection[] = $attachment;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,15 @@ namespace Espo\Core\ORM;
|
||||
|
||||
class Entity extends \Espo\ORM\Entity
|
||||
{
|
||||
public function hasLinkMultipleField($field)
|
||||
{
|
||||
return $this->hasAttribute($field . 'Ids');
|
||||
}
|
||||
|
||||
public function hasLinkField($field)
|
||||
{
|
||||
return $this->hasAttribute($field . 'Id');
|
||||
}
|
||||
|
||||
public function loadLinkMultipleField($field, $columns = null)
|
||||
{
|
||||
|
||||
@@ -198,6 +198,35 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function afterRelate(Entity $entity, $relationName, $foreign, $data = null, array $options = array())
|
||||
{
|
||||
parent::afterRelate($entity, $relationName, $foreign, $data, $options);
|
||||
|
||||
if ($foreign instanceof Entity) {
|
||||
$foreignEntity = $foreign;
|
||||
$hookData = array(
|
||||
'relationName' => $relationName,
|
||||
'relationData' => $data,
|
||||
'foreignEntity' => $foreignEntity
|
||||
);
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterRelate', $entity, $options, $hookData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function afterUnrelate(Entity $entity, $relationName, $foreign, array $options = array())
|
||||
{
|
||||
parent::afterUnrelate($entity, $relationName, $foreign, $options);
|
||||
|
||||
if ($foreign instanceof Entity) {
|
||||
$foreignEntity = $foreign;
|
||||
$hookData = array(
|
||||
'relationName' => $relationName,
|
||||
'foreignEntity' => $foreignEntity
|
||||
);
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityType, 'afterUnrelate', $entity, $options, $hookData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function beforeSave(Entity $entity, array $options = array())
|
||||
{
|
||||
parent::beforeSave($entity, $options);
|
||||
@@ -241,24 +270,29 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
}
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
if (empty($options['import']) || !$entity->has('createdById')) {
|
||||
$entity->set('createdById', $this->entityManager->getUser()->id);
|
||||
$entity->set('createdById', $this->getEntityManager()->getUser()->id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->has('modifiedById')) {
|
||||
$restoreData['modifiedById'] = $entity->get('modifiedById');
|
||||
}
|
||||
if ($entity->has('modifiedByName')) {
|
||||
$restoreData['modifiedByName'] = $entity->get('modifiedByName');
|
||||
}
|
||||
if ($entity->has('modifiedAt')) {
|
||||
$restoreData['modifiedAt'] = $entity->get('modifiedAt');
|
||||
}
|
||||
$entity->clear('modifiedById');
|
||||
$entity->clear('modifiedByName');
|
||||
} else {
|
||||
if (empty($options['silent'])) {
|
||||
if ($entity->hasAttribute('modifiedAt')) {
|
||||
$entity->set('modifiedAt', $nowString);
|
||||
}
|
||||
if ($entity->hasAttribute('modifiedById')) {
|
||||
$entity->set('modifiedById', $this->entityManager->getUser()->id);
|
||||
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
|
||||
$entity->set('modifiedByName', $this->getEntityManager()->getUser()->get('name'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Pdf;
|
||||
|
||||
require "vendor/tecnick.com/tcpdf/tcpdf.php";
|
||||
require "vendor/tecnickcom/tcpdf/tcpdf.php";
|
||||
|
||||
class Tcpdf extends \TCPDF
|
||||
{
|
||||
|
||||
@@ -169,20 +169,22 @@ class AclManager extends \Espo\Core\AclManager
|
||||
return parent::get($user, $permission);
|
||||
}
|
||||
|
||||
public function checkReadOnlyTeam(User $user, $permission)
|
||||
public function checkReadOnlyTeam(User $user, $scope)
|
||||
{
|
||||
if ($this->checkUserIsNotPortal($user)) {
|
||||
return $this->getMainManager()->checkReadOnlyTeam($user, $permission);
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getMainManager()->checkReadOnlyTeam($user, $data);
|
||||
}
|
||||
return false;
|
||||
return parent::checkReadOnlyTeam($user, $scope);
|
||||
}
|
||||
|
||||
public function checkReadOnlyOwn(User $user, $permission)
|
||||
public function checkReadOnlyOwn(User $user, $scope)
|
||||
{
|
||||
if ($this->checkUserIsNotPortal($user)) {
|
||||
return $this->getMainManager()->checkReadOnlyOwn($user, $permission);
|
||||
$data = $this->getTable($user)->getScopeData($scope);
|
||||
return $this->getMainManager()->checkReadOnlyOwn($user, $data);
|
||||
}
|
||||
return false;
|
||||
return parent::checkReadOnlyOwn($user, $scope);
|
||||
}
|
||||
|
||||
public function check(User $user, $subject, $action = null)
|
||||
|
||||
@@ -91,10 +91,10 @@ class Container extends \Espo\Core\Container
|
||||
protected function loadLanguage()
|
||||
{
|
||||
$language = new \Espo\Core\Portal\Utils\Language(
|
||||
\Espo\Core\Utils\Language::detectLanguage($this->get('config'), $this->get('preferences')),
|
||||
$this->get('fileManager'),
|
||||
$this->get('config'),
|
||||
$this->get('metadata'),
|
||||
$this->get('preferences')
|
||||
$this->get('useCache')
|
||||
);
|
||||
$language->setPortal($this->get('portal'));
|
||||
return $language;
|
||||
|
||||
@@ -33,7 +33,6 @@ use \Espo\Entities\Portal;
|
||||
|
||||
class Language extends \Espo\Core\Utils\Language
|
||||
{
|
||||
|
||||
public function setPortal($portal)
|
||||
{
|
||||
if ($portal->get('language') !== '' && $portal->get('language')) {
|
||||
|
||||
174
application/Espo/Core/Repositories/Event.php
Normal file
174
application/Espo/Core/Repositories/Event.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
class Event extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
protected $reminderDateAttribute = 'dateStart';
|
||||
|
||||
protected function afterRemove(Entity $entity, array $options = array())
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
DELETE FROM `reminder`
|
||||
WHERE
|
||||
entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_type = ".$pdo->quote($entity->getEntityType())." AND
|
||||
deleted = 0
|
||||
";
|
||||
$pdo->query($sql);
|
||||
}
|
||||
|
||||
protected function afterSave(Entity $entity, array $options = array())
|
||||
{
|
||||
parent::afterSave($entity, $options);
|
||||
|
||||
if (
|
||||
$entity->isNew() ||
|
||||
$entity->isAttributeChanged('assignedUserId') ||
|
||||
$entity->isAttributeChanged('usersIds') ||
|
||||
$entity->isAttributeChanged($this->reminderDateAttribute) ||
|
||||
$entity->has('reminders')
|
||||
) {
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$reminderTypeList = $this->getMetadata()->get('entityDefs.Reminder.fields.type.options');
|
||||
|
||||
if (!$entity->has('reminders')) {
|
||||
$reminderList = $this->getEntityReminderList($entity);
|
||||
} else {
|
||||
$reminderList = $entity->get('reminders');
|
||||
}
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
$sql = "
|
||||
DELETE FROM `reminder`
|
||||
WHERE
|
||||
entity_id = ".$pdo->quote($entity->id)." AND
|
||||
entity_type = ".$pdo->quote($entity->getEntityType())." AND
|
||||
deleted = 0
|
||||
";
|
||||
$pdo->query($sql);
|
||||
}
|
||||
|
||||
if (empty($reminderList) || !is_array($reminderList)) return;
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
$dateValue = $entity->get($this->reminderDateAttribute);
|
||||
|
||||
if (!$dateValue) {
|
||||
$e = $this->get($entity->id);
|
||||
if ($e) {
|
||||
$dateValue = $e->get($this->reminderDateAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField('users')) {
|
||||
$userIdList = $entity->getLinkMultipleIdList('users');
|
||||
} else {
|
||||
$userIdList = [];
|
||||
if ($entity->get('assignedUserId')) {
|
||||
$userIdList[] = $entity->get('assignedUserId');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dateValue) return;
|
||||
if (empty($userIdList)) return;
|
||||
|
||||
$dateValueObj = new \DateTime($dateValue);
|
||||
if (!$dateValueObj) return;
|
||||
|
||||
foreach ($reminderList as $item) {
|
||||
$remindAt = clone $dateValueObj;
|
||||
$seconds = intval($item->seconds);
|
||||
$type = $item->type;
|
||||
|
||||
if (!in_array($type , $reminderTypeList)) continue;
|
||||
|
||||
$remindAt->sub(new \DateInterval('PT' . $seconds . 'S'));
|
||||
|
||||
foreach ($userIdList as $userId) {
|
||||
$id = Util::generateId();
|
||||
|
||||
$sql = "
|
||||
INSERT
|
||||
INTO `reminder`
|
||||
(id, entity_id, entity_type, `type`, user_id, remind_at, start_at, `seconds`)
|
||||
VALUES (
|
||||
".$pdo->quote($id).",
|
||||
".$pdo->quote($entity->id).",
|
||||
".$pdo->quote($entityType).",
|
||||
".$pdo->quote($type).",
|
||||
".$pdo->quote($userId).",
|
||||
".$pdo->quote($remindAt->format('Y-m-d H:i:s')).",
|
||||
".$pdo->quote($dateValue).",
|
||||
".$pdo->quote($seconds)."
|
||||
)
|
||||
";
|
||||
$pdo->query($sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntityReminderList(Entity $entity)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$reminderList = [];
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT `seconds`, `type`
|
||||
FROM `reminder`
|
||||
WHERE
|
||||
`entity_type` = ".$pdo->quote($entity->getEntityType())." AND
|
||||
`entity_id` = ".$pdo->quote($entity->id)." AND
|
||||
`deleted` = 0
|
||||
ORDER BY `seconds` ASC
|
||||
";
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
$rows = $sth->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$o = new \StdClass();
|
||||
$o->seconds = intval($row['seconds']);
|
||||
$o->type = $row['type'];
|
||||
$reminderList[] = $o;
|
||||
}
|
||||
|
||||
return $reminderList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +125,21 @@ class Base
|
||||
protected function order($sortBy, $desc = false, &$result)
|
||||
{
|
||||
if (!empty($sortBy)) {
|
||||
|
||||
$result['orderBy'] = $sortBy;
|
||||
$type = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortBy, 'type']);
|
||||
if ($type === 'link') {
|
||||
$result['orderBy'] .= 'Name';
|
||||
} else if ($type === 'linkParent') {
|
||||
$result['orderBy'] .= 'Type';
|
||||
} else if ($type === 'address') {
|
||||
if (!$desc) {
|
||||
$orderPart = 'ASC';
|
||||
} else {
|
||||
$orderPart = 'DESC';
|
||||
}
|
||||
$result['orderBy'] = [[$sortBy . 'Country', $orderPart], [$sortBy . 'City', $orderPart], [$sortBy . 'Street', $orderPart]];
|
||||
return;
|
||||
} else if ($type === 'enum') {
|
||||
$list = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields', $sortBy, 'options']);
|
||||
if ($list && is_array($list) && count($list)) {
|
||||
@@ -210,8 +219,17 @@ class Base
|
||||
if (!empty($item['value'])) {
|
||||
$methodName = 'apply' . ucfirst($type);
|
||||
|
||||
if (method_exists($this, $methodName)) {;
|
||||
$this->$methodName($item['field'], $item['value'], $result);
|
||||
if (method_exists($this, $methodName)) {
|
||||
$attribute = null;
|
||||
if (isset($item['field'])) {
|
||||
$attribute = $item['field'];
|
||||
}
|
||||
if (isset($item['attribute'])) {
|
||||
$attribute = $item['attribute'];
|
||||
}
|
||||
if ($attribute) {
|
||||
$this->$methodName($attribute, $item['value'], $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,18 +257,21 @@ class Base
|
||||
|
||||
$defs = $relDefs[$link];
|
||||
if ($relationType == 'manyMany') {
|
||||
$this->addJoin($link, $result);
|
||||
$this->addJoin([$link, $link . 'Filter'], $result);
|
||||
$midKeys = $seed->getRelationParam($link, 'midKeys');
|
||||
|
||||
if (!empty($midKeys)) {
|
||||
$key = $midKeys[1];
|
||||
$part[$link . 'Middle.' . $key] = $idsValue;
|
||||
$part[$link . 'Filter' . 'Middle.' . $key] = $idsValue;
|
||||
}
|
||||
} else if ($relationType== 'belongsTo') {
|
||||
} else if ($relationType == 'belongsTo') {
|
||||
$key = $seed->getRelationParam($link, 'key');
|
||||
if (!empty($key)) {
|
||||
$part[$key] = $idsValue;
|
||||
}
|
||||
} else if ($relationType == 'hasOne') {
|
||||
$this->addJoin([$link, $link . 'Filter'], $result);
|
||||
$part[$link . 'Filter' . '.id'] = $idsValue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -689,13 +710,20 @@ class Base
|
||||
protected function checkWhere($where)
|
||||
{
|
||||
foreach ($where as $w) {
|
||||
$attribute = null;
|
||||
if (isset($w['field'])) {
|
||||
$attribute = $w['field'];
|
||||
}
|
||||
if (isset($w['attribute'])) {
|
||||
$attribute = $w['attribute'];
|
||||
}
|
||||
if ($attribute) {
|
||||
if (isset($w['type']) && $w['type'] === 'linkedWith') {
|
||||
if (in_array($w['field'], $this->getAcl()->getScopeForbiddenFieldList($this->getEntityType()))) {
|
||||
if (in_array($attribute, $this->getAcl()->getScopeForbiddenFieldList($this->getEntityType()))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
} else {
|
||||
if (in_array($w['field'], $this->getAcl()->getScopeForbiddenAttributeList($this->getEntityType()))) {
|
||||
if (in_array($attribute, $this->getAcl()->getScopeForbiddenAttributeList($this->getEntityType()))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
@@ -728,7 +756,15 @@ class Base
|
||||
$value = null;
|
||||
$timeZone = 'UTC';
|
||||
|
||||
if (empty($item['field'])) {
|
||||
$attribute = null;
|
||||
if (isset($item['field'])) {
|
||||
$attribute = $item['field'];
|
||||
}
|
||||
if (isset($item['attribute'])) {
|
||||
$attribute = $item['attribute'];
|
||||
}
|
||||
|
||||
if (!$attribute) {
|
||||
return null;
|
||||
}
|
||||
if (empty($item['type'])) {
|
||||
@@ -741,14 +777,13 @@ class Base
|
||||
$timeZone = $item['timeZone'];
|
||||
}
|
||||
$type = $item['type'];
|
||||
$field = $item['field'];
|
||||
|
||||
if (empty($value) && in_array($type, array('on', 'before', 'after'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$where = array();
|
||||
$where['field'] = $field;
|
||||
$where['attribute'] = $attribute;
|
||||
|
||||
$dt = new \DateTime('now', new \DateTimeZone($timeZone));
|
||||
|
||||
@@ -875,8 +910,16 @@ class Base
|
||||
{
|
||||
$part = array();
|
||||
|
||||
if (!empty($item['field']) && !empty($item['type'])) {
|
||||
$methodName = 'getWherePart' . ucfirst($item['field']) . ucfirst($item['type']);
|
||||
$attribute = null;
|
||||
if (!empty($item['field'])) { // for backward compatibility
|
||||
$attribute = $item['field'];
|
||||
}
|
||||
if (!empty($item['attribute'])) {
|
||||
$attribute = $item['attribute'];
|
||||
}
|
||||
|
||||
if (!empty($attribute) && !empty($item['type'])) {
|
||||
$methodName = 'getWherePart' . ucfirst($attribute) . ucfirst($item['type']);
|
||||
if (method_exists($this, $methodName)) {
|
||||
$value = null;
|
||||
if (!empty($item['value'])) {
|
||||
@@ -909,74 +952,74 @@ class Base
|
||||
}
|
||||
break;
|
||||
case 'like':
|
||||
$part[$item['field'] . '*'] = $item['value'];
|
||||
$part[$attribute . '*'] = $item['value'];
|
||||
break;
|
||||
case 'equals':
|
||||
case 'on':
|
||||
$part[$item['field'] . '='] = $item['value'];
|
||||
$part[$attribute . '='] = $item['value'];
|
||||
break;
|
||||
case 'startsWith':
|
||||
$part[$item['field'] . '*'] = $item['value'] . '%';
|
||||
$part[$attribute . '*'] = $item['value'] . '%';
|
||||
break;
|
||||
case 'endsWith':
|
||||
$part[$item['field'] . '*'] = $item['value'] . '%';
|
||||
$part[$attribute . '*'] = '%' . $item['value'];
|
||||
break;
|
||||
case 'contains':
|
||||
$part[$item['field'] . '*'] = '%' . $item['value'] . '%';
|
||||
$part[$attribute . '*'] = '%' . $item['value'] . '%';
|
||||
break;
|
||||
case 'notEquals':
|
||||
case 'notOn':
|
||||
$part[$item['field'] . '!='] = $item['value'];
|
||||
$part[$attribute . '!='] = $item['value'];
|
||||
break;
|
||||
case 'greaterThan':
|
||||
case 'after':
|
||||
$part[$item['field'] . '>'] = $item['value'];
|
||||
$part[$attribute . '>'] = $item['value'];
|
||||
break;
|
||||
case 'lessThan':
|
||||
case 'before':
|
||||
$part[$item['field'] . '<'] = $item['value'];
|
||||
$part[$attribute . '<'] = $item['value'];
|
||||
break;
|
||||
case 'greaterThanOrEquals':
|
||||
$part[$item['field'] . '>='] = $item['value'];
|
||||
$part[$attribute . '>='] = $item['value'];
|
||||
break;
|
||||
case 'lessThanOrEquals':
|
||||
$part[$item['field'] . '<'] = $item['value'];
|
||||
$part[$attribute . '<'] = $item['value'];
|
||||
break;
|
||||
case 'in':
|
||||
$part[$item['field'] . '='] = $item['value'];
|
||||
$part[$attribute . '='] = $item['value'];
|
||||
break;
|
||||
case 'notIn':
|
||||
$part[$item['field'] . '!='] = $item['value'];
|
||||
$part[$attribute . '!='] = $item['value'];
|
||||
break;
|
||||
case 'isNull':
|
||||
$part[$item['field'] . '='] = null;
|
||||
$part[$attribute . '='] = null;
|
||||
break;
|
||||
case 'isNotNull':
|
||||
case 'ever':
|
||||
$part[$item['field'] . '!='] = null;
|
||||
$part[$attribute . '!='] = null;
|
||||
break;
|
||||
case 'isTrue':
|
||||
$part[$item['field'] . '='] = true;
|
||||
$part[$attribute . '='] = true;
|
||||
break;
|
||||
case 'isFalse':
|
||||
$part[$item['field'] . '='] = false;
|
||||
$part[$attribute . '='] = false;
|
||||
break;
|
||||
case 'today':
|
||||
$part[$item['field'] . '='] = date('Y-m-d');
|
||||
$part[$attribute . '='] = date('Y-m-d');
|
||||
break;
|
||||
case 'past':
|
||||
$part[$item['field'] . '<'] = date('Y-m-d');
|
||||
$part[$attribute . '<'] = date('Y-m-d');
|
||||
break;
|
||||
case 'future':
|
||||
$part[$item['field'] . '>='] = date('Y-m-d');
|
||||
$part[$attribute . '>='] = date('Y-m-d');
|
||||
break;
|
||||
case 'lastSevenDays':
|
||||
$dt1 = new \DateTime();
|
||||
$dt2 = clone $dt1;
|
||||
$dt2->modify('-7 days');
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt2->format('Y-m-d'),
|
||||
$item['field'] . '<=' => $dt1->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt2->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt1->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'lastXDays':
|
||||
@@ -986,8 +1029,8 @@ class Base
|
||||
|
||||
$dt2->modify('-'.$number.' days');
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt2->format('Y-m-d'),
|
||||
$item['field'] . '<=' => $dt1->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt2->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt1->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'nextXDays':
|
||||
@@ -996,22 +1039,22 @@ class Base
|
||||
$number = strval(intval($item['value']));
|
||||
$dt2->modify('+'.$number.' days');
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt1->format('Y-m-d'),
|
||||
$item['field'] . '<=' => $dt2->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt1->format('Y-m-d'),
|
||||
$attribute . '<=' => $dt2->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'currentMonth':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->modify('first day of this month')->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->modify('first day of this month')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'lastMonth':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->modify('first day of last month')->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->modify('first day of last month')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1M'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'currentQuarter':
|
||||
@@ -1019,8 +1062,8 @@ class Base
|
||||
$quarter = ceil($dt->format('m') / 3);
|
||||
$dt->modify('first day of January this year');
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'lastQuarter':
|
||||
@@ -1033,29 +1076,29 @@ class Base
|
||||
$dt->sub('P1Y');
|
||||
}
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->add(new \DateInterval('P'.(($quarter - 1) * 3).'M'))->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P3M'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'currentYear':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->modify('first day of January this year')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'lastYear':
|
||||
$dt = new \DateTime();
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'),
|
||||
$item['field'] . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
$attribute . '>=' => $dt->modify('first day of January last year')->format('Y-m-d'),
|
||||
$attribute . '<' => $dt->add(new \DateInterval('P1Y'))->format('Y-m-d'),
|
||||
);
|
||||
break;
|
||||
case 'between':
|
||||
if (is_array($item['value'])) {
|
||||
$part['AND'] = array(
|
||||
$item['field'] . '>=' => $item['value'][0],
|
||||
$item['field'] . '<=' => $item['value'][1],
|
||||
$attribute . '>=' => $item['value'][0],
|
||||
$attribute . '<=' => $item['value'][1],
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
37
application/Espo/Core/Templates/Controllers/BasePlus.php
Normal file
37
application/Espo/Core/Templates/Controllers/BasePlus.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Controllers;
|
||||
|
||||
|
||||
class BasePlus extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
35
application/Espo/Core/Templates/Controllers/Company.php
Normal file
35
application/Espo/Core/Templates/Controllers/Company.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Controllers;
|
||||
|
||||
class Company extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
|
||||
}
|
||||
36
application/Espo/Core/Templates/Entities/BasePlus.php
Normal file
36
application/Espo/Core/Templates/Entities/BasePlus.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Entities;
|
||||
|
||||
class BasePlus extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
36
application/Espo/Core/Templates/Entities/Company.php
Normal file
36
application/Espo/Core/Templates/Entities/Company.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Entities;
|
||||
|
||||
class Company extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
37
application/Espo/Core/Templates/Layouts/Company/detail.json
Normal file
37
application/Espo/Core/Templates/Layouts/Company/detail.json
Normal file
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"label": "Overview",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "website"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "emailAddress"
|
||||
},
|
||||
{
|
||||
"name": "phoneNumber"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "billingAddress"
|
||||
},
|
||||
{
|
||||
"name": "shippingAddress"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "description",
|
||||
"fullWidth": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"label": "",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "website",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "emailAddress",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "phoneNumber",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "billingAddressCountry",
|
||||
"fullWidth": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -6,7 +6,9 @@
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
false
|
||||
{
|
||||
"name": "parent"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -26,7 +28,9 @@
|
||||
{
|
||||
"name": "duration"
|
||||
},
|
||||
false
|
||||
{
|
||||
"name": "reminders"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "parent",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "description",
|
||||
|
||||
33
application/Espo/Core/Templates/Layouts/Person/detail.json
Normal file
33
application/Espo/Core/Templates/Layouts/Person/detail.json
Normal file
@@ -0,0 +1,33 @@
|
||||
[
|
||||
{
|
||||
"label": "Overview",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "emailAddress"
|
||||
},
|
||||
{
|
||||
"name": "phoneNumber"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "address"
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "description",
|
||||
"fullWidth": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
[
|
||||
{
|
||||
"label": "",
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "emailAddress",
|
||||
"fullWidth": true
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "phoneNumber",
|
||||
"fullWidth": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -4,6 +4,7 @@
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"aclPortal": true,
|
||||
"aclPortalLevelList": ["all", "account", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"],
|
||||
"sidePanels": {
|
||||
"detail": [
|
||||
{
|
||||
"name": "activities",
|
||||
"label": "Activities",
|
||||
"view": "crm:views/record/panels/activities",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "history",
|
||||
"label": "History",
|
||||
"view": "crm:views/record/panels/history",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "tasks",
|
||||
"label": "Tasks",
|
||||
"view": "crm:views/record/panels/tasks",
|
||||
"aclScope": "Task"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"trim": true
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "link",
|
||||
"required": true,
|
||||
"view": "views/fields/assigned-user"
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple",
|
||||
"view": "views/fields/teams"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"meetings": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
"name": {
|
||||
"columns": ["name", "deleted"]
|
||||
},
|
||||
"assignedUser": {
|
||||
"columns": ["assignedUserId", "deleted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"aclPortal": true,
|
||||
"aclPortalLevelList": ["all", "account", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"],
|
||||
"sidePanels": {
|
||||
"detail": [
|
||||
{
|
||||
"name": "activities",
|
||||
"label": "Activities",
|
||||
"view": "crm:views/record/panels/activities",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "history",
|
||||
"label": "History",
|
||||
"view": "crm:views/record/panels/history",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "tasks",
|
||||
"label": "Tasks",
|
||||
"view": "crm:views/record/panels/tasks",
|
||||
"aclScope": "Task"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
141
application/Espo/Core/Templates/Metadata/Company/entityDefs.json
Normal file
141
application/Espo/Core/Templates/Metadata/Company/entityDefs.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"trim": true
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"website": {
|
||||
"type": "url"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
"typeList": ["Office", "Mobile", "Fax", "Other"],
|
||||
"defaultType": "Office"
|
||||
},
|
||||
"billingAddress": {
|
||||
"type": "address"
|
||||
},
|
||||
"billingAddressStreet": {
|
||||
"type": "text",
|
||||
"maxLength": 255,
|
||||
"dbType": "varchar"
|
||||
},
|
||||
"billingAddressCity": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"billingAddressState": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"billingAddressCountry": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"billingAddressPostalCode": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"shippingAddress": {
|
||||
"type": "address",
|
||||
"view": "crm:views/account/fields/shipping-address"
|
||||
},
|
||||
"shippingAddressStreet": {
|
||||
"type": "text",
|
||||
"maxLength": 255,
|
||||
"dbType": "varchar"
|
||||
},
|
||||
"shippingAddressCity": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"shippingAddressState": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"shippingAddressCountry": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"shippingAddressPostalCode": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "link",
|
||||
"required": false,
|
||||
"view": "views/fields/assigned-user"
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple",
|
||||
"view": "views/fields/teams"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"assignedUser": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"meetings": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"sortBy": "createdAt",
|
||||
"asc": false
|
||||
},
|
||||
"indexes": {
|
||||
"name": {
|
||||
"columns": ["name", "deleted"]
|
||||
},
|
||||
"assignedUser": {
|
||||
"columns": ["assignedUserId", "deleted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
11
application/Espo/Core/Templates/Metadata/Company/scopes.json
Normal file
11
application/Espo/Core/Templates/Metadata/Company/scopes.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": true,
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"aclPortal": true,
|
||||
"aclPortalLevelList": ["all", "account", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
}
|
||||
@@ -1,4 +1,20 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"]
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"],
|
||||
"activityDefs": {
|
||||
"activitiesCreate": true,
|
||||
"historyCreate": true
|
||||
},
|
||||
"filterList": [
|
||||
{
|
||||
"name":"planned"
|
||||
},
|
||||
{
|
||||
"name":"held",
|
||||
"style": "success"
|
||||
},
|
||||
{
|
||||
"name":"todays"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"reminders": {
|
||||
"type": "jsonArray",
|
||||
"notStorable": true,
|
||||
"view": "crm:views/meeting/fields/reminders"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"aclPortal": true,
|
||||
"aclPortalLevelList": ["all", "account", "contact", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"calendar": true,
|
||||
"notifications": true
|
||||
"activity": true,
|
||||
"notifications": true,
|
||||
"activityStatusList": ["Planned"],
|
||||
"historyStatusList": ["Held", "Not Held"],
|
||||
"statusField": "status"
|
||||
}
|
||||
@@ -1,4 +1,26 @@
|
||||
{
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"]
|
||||
"controller": "controllers/record",
|
||||
"boolFilterList": ["onlyMy"],
|
||||
"sidePanels": {
|
||||
"detail": [
|
||||
{
|
||||
"name": "activities",
|
||||
"label": "Activities",
|
||||
"view": "crm:views/record/panels/activities",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "history",
|
||||
"label": "History",
|
||||
"view": "crm:views/record/panels/history",
|
||||
"aclScope": "Activities"
|
||||
},
|
||||
{
|
||||
"name": "tasks",
|
||||
"label": "Tasks",
|
||||
"view": "crm:views/record/panels/tasks",
|
||||
"aclScope": "Task"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,24 @@
|
||||
"entity": "Team",
|
||||
"relationName": "EntityTeam",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"meetings": {
|
||||
"type": "hasMany",
|
||||
"entity": "Meeting",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"calls": {
|
||||
"type": "hasMany",
|
||||
"entity": "Call",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
},
|
||||
"tasks": {
|
||||
"type": "hasChildren",
|
||||
"entity": "Task",
|
||||
"foreign": "parent",
|
||||
"layoutRelationshipsDisabled": true
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"tab": true,
|
||||
"acl": true,
|
||||
"aclPortal": true,
|
||||
"aclPortalLevelList": ["all", "account", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
|
||||
37
application/Espo/Core/Templates/Repositories/BasePlus.php
Normal file
37
application/Espo/Core/Templates/Repositories/BasePlus.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Repositories;
|
||||
|
||||
|
||||
class BasePlus extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
35
application/Espo/Core/Templates/Repositories/Company.php
Normal file
35
application/Espo/Core/Templates/Repositories/Company.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Repositories;
|
||||
|
||||
class Company extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
|
||||
}
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Templates\Repositories;
|
||||
|
||||
class Event extends \Espo\Core\ORM\Repositories\RDB
|
||||
class Event extends \Espo\Core\Repositories\Event
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
57
application/Espo/Core/Templates/SelectManagers/Event.php
Normal file
57
application/Espo/Core/Templates/SelectManagers/Event.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\SelectManagers;
|
||||
|
||||
class Event extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
protected function filterPlanned(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => 'Planned'
|
||||
);
|
||||
}
|
||||
|
||||
protected function filterHeld(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'status' => 'Held'
|
||||
);
|
||||
}
|
||||
|
||||
protected function filterTodays(&$result)
|
||||
{
|
||||
$result['whereClause'][] = $this->convertDateTimeWhere(array(
|
||||
'type' => 'today',
|
||||
'attribute' => 'dateStart',
|
||||
'timeZone' => $this->getUserTimeZone()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
37
application/Espo/Core/Templates/Services/BasePlus.php
Normal file
37
application/Espo/Core/Templates/Services/BasePlus.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
|
||||
class BasePlus extends \Espo\Services\Record
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
42
application/Espo/Core/Templates/Services/Company.php
Normal file
42
application/Espo/Core/Templates/Services/Company.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Company extends \Espo\Services\Record
|
||||
{
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
return array(
|
||||
'name' => $entity->get('name')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,19 @@
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Event extends \Espo\Services\Record
|
||||
{
|
||||
public function loadAdditionalFields(Entity $entity)
|
||||
{
|
||||
parent::loadAdditionalFields($entity);
|
||||
$this->loadRemindersField($entity);
|
||||
}
|
||||
|
||||
protected function loadRemindersField(Entity $entity)
|
||||
{
|
||||
$reminders = $this->getRepository()->getEntityReminderList($entity);
|
||||
$entity->set('reminders', $reminders);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Templates\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Person extends \Espo\Services\Record
|
||||
{
|
||||
|
||||
5
application/Espo/Core/Templates/i18n/cs_CZ/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/cs_CZ/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/cs_CZ/BasePlus.json
Normal file
5
application/Espo/Core/Templates/i18n/cs_CZ/BasePlus.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/cs_CZ/Company.json
Normal file
5
application/Espo/Core/Templates/i18n/cs_CZ/Company.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/cs_CZ/Event.json
Normal file
5
application/Espo/Core/Templates/i18n/cs_CZ/Event.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/cs_CZ/Person.json
Normal file
5
application/Espo/Core/Templates/i18n/cs_CZ/Person.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/de_DE/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/de_DE/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen"
|
||||
}
|
||||
}
|
||||
10
application/Espo/Core/Templates/i18n/de_DE/BasePlus.json
Normal file
10
application/Espo/Core/Templates/i18n/de_DE/BasePlus.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen"
|
||||
}
|
||||
}
|
||||
15
application/Espo/Core/Templates/i18n/de_DE/Company.json
Normal file
15
application/Espo/Core/Templates/i18n/de_DE/Company.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"fields": {
|
||||
"billingAddress": "Rechnungsadresse",
|
||||
"shippingAddress": "Lieferadresse",
|
||||
"website": "Webseite"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen"
|
||||
}
|
||||
}
|
||||
35
application/Espo/Core/Templates/i18n/de_DE/Event.json
Normal file
35
application/Espo/Core/Templates/i18n/de_DE/Event.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"fields": {
|
||||
"parent": "Bezieht sich auf",
|
||||
"dateStart": "Startdatum",
|
||||
"dateEnd": "Enddatum",
|
||||
"duration": "Dauer",
|
||||
"status": "Status"
|
||||
},
|
||||
"links": {
|
||||
"parent": "Bezieht sich auf"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Geplant",
|
||||
"Held": "Durchgeführt",
|
||||
"Not Held": "Nicht durchgeführt"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen",
|
||||
"Schedule {entityType}": "{entityTypeTranslated} planen",
|
||||
"Log {entityType}": "Log {entityTypeTranslated}",
|
||||
"Set Held": "Auf durchgeführt setzen",
|
||||
"Set Not Held": "Auf nicht durchgeführt setzen"
|
||||
},
|
||||
"massActions": {
|
||||
"setHeld": "Auf durchgeführt setzen",
|
||||
"setNotHeld": "Auf nicht durchgeführt setzen"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Geplant",
|
||||
"held": "Durchgeführt",
|
||||
"todays": "Heutige"
|
||||
}
|
||||
}
|
||||
13
application/Espo/Core/Templates/i18n/de_DE/Person.json
Normal file
13
application/Espo/Core/Templates/i18n/de_DE/Person.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresse"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "{entityTypeTranslated} erstellen"
|
||||
}
|
||||
}
|
||||
9
application/Espo/Core/Templates/i18n/en_US/Base.json
Normal file
9
application/Espo/Core/Templates/i18n/en_US/Base.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"fields": {
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Create {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
12
application/Espo/Core/Templates/i18n/en_US/BasePlus.json
Normal file
12
application/Espo/Core/Templates/i18n/en_US/BasePlus.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"fields": {
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Calls",
|
||||
"tasks": "Tasks"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Create {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
15
application/Espo/Core/Templates/i18n/en_US/Company.json
Normal file
15
application/Espo/Core/Templates/i18n/en_US/Company.json
Normal 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}"
|
||||
}
|
||||
}
|
||||
36
application/Espo/Core/Templates/i18n/en_US/Event.json
Normal file
36
application/Espo/Core/Templates/i18n/en_US/Event.json
Normal 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"
|
||||
}
|
||||
}
|
||||
13
application/Espo/Core/Templates/i18n/en_US/Person.json
Normal file
13
application/Espo/Core/Templates/i18n/en_US/Person.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Address"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Calls",
|
||||
"tasks": "Tasks"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Create {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/es_ES/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/es_ES/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Vytvořit {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/es_ES/BasePlus.json
Normal file
5
application/Espo/Core/Templates/i18n/es_ES/BasePlus.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Crear {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/es_ES/Company.json
Normal file
5
application/Espo/Core/Templates/i18n/es_ES/Company.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Crear {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/es_ES/Event.json
Normal file
5
application/Espo/Core/Templates/i18n/es_ES/Event.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Crear {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/es_ES/Person.json
Normal file
5
application/Espo/Core/Templates/i18n/es_ES/Person.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Crear {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/fr_FR/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/fr_FR/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Créer un {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/fr_FR/BasePlus.json
Normal file
5
application/Espo/Core/Templates/i18n/fr_FR/BasePlus.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Créer un {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/fr_FR/Company.json
Normal file
5
application/Espo/Core/Templates/i18n/fr_FR/Company.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Créer un {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/fr_FR/Event.json
Normal file
5
application/Espo/Core/Templates/i18n/fr_FR/Event.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Créer un {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/fr_FR/Person.json
Normal file
5
application/Espo/Core/Templates/i18n/fr_FR/Person.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Créer un {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/id_ID/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/id_ID/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Buat {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/id_ID/BasePlus.json
Normal file
5
application/Espo/Core/Templates/i18n/id_ID/BasePlus.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Buat {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/id_ID/Company.json
Normal file
5
application/Espo/Core/Templates/i18n/id_ID/Company.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Buat {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/id_ID/Event.json
Normal file
5
application/Espo/Core/Templates/i18n/id_ID/Event.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Buat {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user