mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-10 12:47:02 +00:00
Compare commits
244 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a2ba73412 | ||
|
|
cbee8cc541 | ||
|
|
22f186af29 | ||
|
|
77ab385db8 | ||
|
|
13495ee32e | ||
|
|
254ce7d5d2 | ||
|
|
4e1053d037 | ||
|
|
2fcc6e168f | ||
|
|
96ea0ae690 | ||
|
|
aa66001981 | ||
|
|
173ff943a4 | ||
|
|
79ac73bb95 | ||
|
|
2143832c25 | ||
|
|
c656e36265 | ||
|
|
8564adca6a | ||
|
|
19c9922115 | ||
|
|
b22a6f204a | ||
|
|
daa0cf9fc9 | ||
|
|
6365aa04df | ||
|
|
ff20e077b1 | ||
|
|
1106aaf18e | ||
|
|
872ba225b4 | ||
|
|
95c15efa92 | ||
|
|
c179cec8a0 | ||
|
|
97f96396c5 | ||
|
|
e05cac1261 | ||
|
|
f9d8894f37 | ||
|
|
5bd5a76722 | ||
|
|
2fbb4f3725 | ||
|
|
61e1b18eb9 | ||
|
|
08c1710326 | ||
|
|
39d3baf1b5 | ||
|
|
fd4753d190 | ||
|
|
29745367f7 | ||
|
|
25bb4f08ba | ||
|
|
40a13c16eb | ||
|
|
8ad2c425bf | ||
|
|
2b6c9bc57e | ||
|
|
bebbee5abd | ||
|
|
99156499f1 | ||
|
|
9ad81581bb | ||
|
|
1b4cd1478b | ||
|
|
5fdf2e4403 | ||
|
|
319b523117 | ||
|
|
62823646b0 | ||
|
|
3c16758605 | ||
|
|
4705d2e38d | ||
|
|
05b405f76e | ||
|
|
f54b2788de | ||
|
|
6ac0c7b301 | ||
|
|
c44cc9906e | ||
|
|
ecd5671e1a | ||
|
|
c83f729eea | ||
|
|
10afd2dfef | ||
|
|
8229b8320f | ||
|
|
50493cf725 | ||
|
|
3937b80254 | ||
|
|
bcb7aaf13c | ||
|
|
f9d7ec7f47 | ||
|
|
b7d43edf22 | ||
|
|
0f0060f0f6 | ||
|
|
036bad3912 | ||
|
|
c9d675798b | ||
|
|
10703750f9 | ||
|
|
4d942851d3 | ||
|
|
1434c31b2f | ||
|
|
74405b2842 | ||
|
|
1070d18085 | ||
|
|
b729c13c7a | ||
|
|
1c99327bfd | ||
|
|
61279b0e59 | ||
|
|
31cb17a41d | ||
|
|
f9349d5545 | ||
|
|
c3703494eb | ||
|
|
d94ff7c4fa | ||
|
|
01e0bf4d6d | ||
|
|
ca5f1a47fd | ||
|
|
54af8ee7c6 | ||
|
|
8572b565f3 | ||
|
|
ea1cc1c65a | ||
|
|
4610c61bd4 | ||
|
|
02f04ea9c4 | ||
|
|
1ce4db6be4 | ||
|
|
888291a99d | ||
|
|
3f9773a4cf | ||
|
|
a223c721aa | ||
|
|
5bce57b1f8 | ||
|
|
3666c34845 | ||
|
|
0dceccac46 | ||
|
|
59a2644e0f | ||
|
|
6c33a7304d | ||
|
|
e89f01a525 | ||
|
|
7181b45461 | ||
|
|
b7ce52f476 | ||
|
|
d2a8cd961f | ||
|
|
c152dbf4bf | ||
|
|
594a111bc3 | ||
|
|
005be2fe5a | ||
|
|
8f194e6d9e | ||
|
|
e3a5cb464c | ||
|
|
779f0df83e | ||
|
|
086d9bdbb6 | ||
|
|
9dcaa46bc7 | ||
|
|
3bca95a4da | ||
|
|
be09935287 | ||
|
|
410c338da8 | ||
|
|
be0606ede6 | ||
|
|
f54a84420a | ||
|
|
6a0d1e3b8b | ||
|
|
33127cd1bb | ||
|
|
5cd03312e1 | ||
|
|
391c0dcaf8 | ||
|
|
0b7b9599d3 | ||
|
|
b45ff69376 | ||
|
|
1836d0a127 | ||
|
|
048e156e59 | ||
|
|
a85be60e30 | ||
|
|
90d5c9eca6 | ||
|
|
1c8f0c7d9c | ||
|
|
f8d1c9ce05 | ||
|
|
28052bac23 | ||
|
|
c361a940eb | ||
|
|
fbc1e936db | ||
|
|
84661f88fd | ||
|
|
10b4c88872 | ||
|
|
10ddc7d941 | ||
|
|
b1c63e2cb9 | ||
|
|
3f6544d03b | ||
|
|
a9f211dfd5 | ||
|
|
f2427abbf4 | ||
|
|
a6187f9838 | ||
|
|
384600ed95 | ||
|
|
6d06f03ef9 | ||
|
|
0829c714fd | ||
|
|
9e2251755f | ||
|
|
436f871be2 | ||
|
|
8d09f8e9b2 | ||
|
|
e4d43a6790 | ||
|
|
bb3d9d3466 | ||
|
|
bd208e259c | ||
|
|
1a1cfec3e5 | ||
|
|
1e95e98549 | ||
|
|
82056b5650 | ||
|
|
c0d118ee98 | ||
|
|
da13177292 | ||
|
|
4cc38ca564 | ||
|
|
fa945c981e | ||
|
|
63d76c6b71 | ||
|
|
9b749a8f67 | ||
|
|
1ce1d5c79a | ||
|
|
0c2dd73334 | ||
|
|
7331f14e0a | ||
|
|
d44f2fd7cd | ||
|
|
4da7eddc69 | ||
|
|
3ebe30a9c0 | ||
|
|
e959361df9 | ||
|
|
7773506361 | ||
|
|
9db652a501 | ||
|
|
27ebfd7f23 | ||
|
|
96f80e0008 | ||
|
|
5f3c208161 | ||
|
|
d402f33c74 | ||
|
|
879da4dca2 | ||
|
|
3e862790e6 | ||
|
|
425e36bff6 | ||
|
|
705ecbdf72 | ||
|
|
c658d67ac8 | ||
|
|
c46055b469 | ||
|
|
66e4b89d1a | ||
|
|
38b9f1d68c | ||
|
|
19b8a4e771 | ||
|
|
937ffea4ab | ||
|
|
1ae49a3d47 | ||
|
|
c76daf50db | ||
|
|
9e5c797243 | ||
|
|
662c4afcdf | ||
|
|
9b50b97e79 | ||
|
|
d00cefc37f | ||
|
|
1f94c13e25 | ||
|
|
75801ea717 | ||
|
|
b845f5086e | ||
|
|
8c8e6ec551 | ||
|
|
a4aecee18d | ||
|
|
3c0d445824 | ||
|
|
c36c6dc42d | ||
|
|
f7f8f14725 | ||
|
|
9402bf4baf | ||
|
|
12fcdcdb2d | ||
|
|
61735ed4aa | ||
|
|
3c65d252b6 | ||
|
|
6423859195 | ||
|
|
53df34d6f2 | ||
|
|
ed762c9be3 | ||
|
|
f05f14b12a | ||
|
|
183b5cb29b | ||
|
|
07193b1fb2 | ||
|
|
b24f8f538b | ||
|
|
a259174415 | ||
|
|
3d93c2a4b1 | ||
|
|
8c112791ef | ||
|
|
0ff4553c94 | ||
|
|
1e6094b5ee | ||
|
|
a646d97aec | ||
|
|
57f4980802 | ||
|
|
2477f5b696 | ||
|
|
6463422e81 | ||
|
|
3daf041698 | ||
|
|
933456ecdf | ||
|
|
ab18f72f69 | ||
|
|
28465cf2bf | ||
|
|
4cef5c547e | ||
|
|
057abf8024 | ||
|
|
20de5658e9 | ||
|
|
4a2ed2e0f7 | ||
|
|
3dc574bc48 | ||
|
|
d43a42b646 | ||
|
|
823c46a6df | ||
|
|
919c0bc8ae | ||
|
|
54d62a19cd | ||
|
|
18304fb710 | ||
|
|
cc1afbed5d | ||
|
|
a195acfee9 | ||
|
|
e22ec4c20b | ||
|
|
7c8f4f9db8 | ||
|
|
d28ff1e438 | ||
|
|
5af7499fdb | ||
|
|
6588d783cc | ||
|
|
f1b8279d50 | ||
|
|
252d31ffac | ||
|
|
8a98cca4fa | ||
|
|
0b8486c1a7 | ||
|
|
b0bd0664f9 | ||
|
|
286fd7e0ce | ||
|
|
42f5d28369 | ||
|
|
ea5d873cc3 | ||
|
|
ee8608469e | ||
|
|
f541625318 | ||
|
|
ee70fb8483 | ||
|
|
f79e093f52 | ||
|
|
254d9280b9 | ||
|
|
ddd9c765b8 | ||
|
|
080f204eed | ||
|
|
f30325849a | ||
|
|
8caa5b3e60 |
@@ -6,4 +6,4 @@ Before we can merge your pull request you need to accept our CLA [here](https://
|
||||
|
||||
## Issues
|
||||
|
||||
We don't provide developer help or any kind of support on github. Please use our [forum](http://forum.espocrm.com/) for this.
|
||||
We don't provide developer help or any kind of support on github. Please use our [forum](https://forum.espocrm.com) for this.
|
||||
|
||||
63
application/Espo/Acl/EmailAddress.php
Normal file
63
application/Espo/Acl/EmailAddress.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class EmailAddress extends \Espo\Core\Acl\Base
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
$id = $entity->id;
|
||||
|
||||
$isFobidden = false;
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$entityWithSameAddressList = $repository->getEntityListByAddressId($id, $excludeEntity);
|
||||
foreach ($entityWithSameAddressList as $e) {
|
||||
if (!$this->getAclManager()->check($user, $e, 'edit')) {
|
||||
$isFobidden = true;
|
||||
if (
|
||||
$e->get('isPortalUser') && $excludeEntity->getEntityType() === 'Contact' &&
|
||||
$e->get('contactId') === $excludeEntity->id
|
||||
) {
|
||||
$isFobidden = false;
|
||||
}
|
||||
if ($isFobidden) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !$isFobidden;
|
||||
}
|
||||
}
|
||||
|
||||
63
application/Espo/Acl/PhoneNumber.php
Normal file
63
application/Espo/Acl/PhoneNumber.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Acl;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class PhoneNumber extends \Espo\Core\Acl\Base
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
$id = $entity->id;
|
||||
|
||||
$isFobidden = false;
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('PhoneNumber');
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$entityWithSameNumberList = $repository->getEntityListByPhoneNumberId($id, $excludeEntity);
|
||||
foreach ($entityWithSameNumberList as $e) {
|
||||
if (!$this->getAclManager()->check($user, $e, 'edit')) {
|
||||
$isFobidden = true;
|
||||
if (
|
||||
$e->get('isPortalUser') && $excludeEntity->getEntityType() === 'Contact' &&
|
||||
$e->get('contactId') === $excludeEntity->id
|
||||
) {
|
||||
$isFobidden = false;
|
||||
}
|
||||
if ($isFobidden) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !$isFobidden;
|
||||
}
|
||||
}
|
||||
56
application/Espo/AclPortal/EmailAddress.php
Normal file
56
application/Espo/AclPortal/EmailAddress.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\AclPortal;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class EmailAddress extends \Espo\Core\AclPortal\Base
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
$id = $entity->id;
|
||||
|
||||
$isFobidden = false;
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$entityWithSameAddressList = $repository->getEntityListByAddressId($id, $excludeEntity);
|
||||
foreach ($entityWithSameAddressList as $e) {
|
||||
if (!$this->getAclManager()->check($user, $e, 'edit')) {
|
||||
$isFobidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !$isFobidden;
|
||||
}
|
||||
}
|
||||
57
application/Espo/AclPortal/PhoneNumber.php
Normal file
57
application/Espo/AclPortal/PhoneNumber.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\AclPortal;
|
||||
|
||||
use \Espo\Entities\User as EntityUser;
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class PhoneNumber extends \Espo\Core\AclPortal\Base
|
||||
{
|
||||
public function checkEditInEntity(EntityUser $user, Entity $entity, Entity $excludeEntity)
|
||||
{
|
||||
$id = $entity->id;
|
||||
|
||||
$isFobidden = false;
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('PhoneNumber');
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$entityWithSameNumberList = $repository->getEntityListByPhoneNumberId($id, $excludeEntity);
|
||||
foreach ($entityWithSameNumberList as $e) {
|
||||
if (!$this->getAclManager()->check($user, $e, 'edit')) {
|
||||
$isFobidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !$isFobidden;
|
||||
}
|
||||
}
|
||||
67
application/Espo/Controllers/AuthLogRecord.php
Normal file
67
application/Espo/Controllers/AuthLogRecord.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class AuthLogRecord extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionMassUpdate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionCreate($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionCreateLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function actionRemoveLink($params, $data, $request)
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
55
application/Espo/Controllers/DataPrivacy.php
Normal file
55
application/Espo/Controllers/DataPrivacy.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class DataPrivacy extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
protected function checkControllerAccess()
|
||||
{
|
||||
if ($this->getAcl()->get('dataPrivacyPermission') === 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
public function postActionErase($params, $data)
|
||||
{
|
||||
if (empty($data->entityType) || empty($data->id) || empty($data->fieldList) || !is_array($data->fieldList)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getServiceFactory()->create('DataPrivacy')->erase($data->entityType, $data->id, $data->fieldList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,6 +83,17 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
if (isset($data['textFilterFields']) && is_array($data['textFilterFields'])) {
|
||||
$params['textFilterFields'] = $data['textFilterFields'];
|
||||
}
|
||||
if (!empty($data['color'])) {
|
||||
$params['color'] = $data['color'];
|
||||
}
|
||||
if (!empty($data['iconClass'])) {
|
||||
$params['iconClass'] = $data['iconClass'];
|
||||
}
|
||||
|
||||
$params['kanbanViewMode'] = !empty($data['kanbanViewMode']);
|
||||
if (!empty($data['kanbanStatusIgnoreList'])) {
|
||||
$params['kanbanStatusIgnoreList'] = $data['kanbanStatusIgnoreList'];
|
||||
}
|
||||
|
||||
$result = $this->getContainer()->get('entityManagerUtil')->create($name, $type, $params);
|
||||
|
||||
@@ -324,4 +335,16 @@ class EntityManager extends \Espo\Core\Controllers\Base
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionResetToDefault($params, $data, $request)
|
||||
{
|
||||
if (empty($data->scope)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getContainer()->get('entityManagerUtil')->resetToDefaults($data->scope);
|
||||
$this->getContainer()->get('dataManager')->clearCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ class Base implements Injectable
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasAttribute('assignedUsersIds') && $entity->hasRelation('assignedUsers')) {
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
if ($entity->hasLinkMultipleId('assignedUsers', $user->id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ class Table
|
||||
if (isset($this->data->$permission)) {
|
||||
return $this->data->$permission;
|
||||
}
|
||||
return null;
|
||||
return 'no';
|
||||
}
|
||||
|
||||
public function getLevel($scope, $action)
|
||||
@@ -210,6 +210,7 @@ class Table
|
||||
$this->applyDisabled($aclTable, $fieldTable);
|
||||
$this->applyMandatory($aclTable, $fieldTable);
|
||||
$this->applyAdditional($aclTable, $fieldTable, $valuePermissionLists);
|
||||
$this->applyReadOnlyFields($fieldTable);
|
||||
} else {
|
||||
$aclTable = (object) [];
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
@@ -714,4 +715,28 @@ class Table
|
||||
{
|
||||
$this->fileManager->putPhpContents($this->cacheFilePath, $this->data, true);
|
||||
}
|
||||
|
||||
protected function applyReadOnlyFields(&$fieldTable)
|
||||
{
|
||||
// TODO Enable in 5.4.0
|
||||
return;
|
||||
$scopeList = $this->getScopeWithAclList();
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!property_exists($fieldTable, $scope)) continue;
|
||||
$fieldList = array_keys($this->getMetadata()->get(['entityDefs', $scope, 'fields'], []));
|
||||
foreach ($fieldList as $field) {
|
||||
if ($this->getMetadata()->get(['entityDefs', $scope, 'fields', $field, 'readOnly'])) {
|
||||
if (property_exists($fieldTable->$scope, $field)) {
|
||||
$fieldTable->$scope->$field->edit = 'no';
|
||||
} else {
|
||||
$fieldTable->$scope->$field = (object) [];
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$fieldTable->$scope->$field->$action = 'yes';
|
||||
}
|
||||
$fieldTable->$scope->$field->edit = 'no';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ class Application
|
||||
public function runClient()
|
||||
{
|
||||
$this->getContainer()->get('clientManager')->display();
|
||||
exit;
|
||||
}
|
||||
|
||||
public function runEntryPoint($entryPoint, $data = array(), $final = false)
|
||||
|
||||
@@ -160,6 +160,48 @@ class Record extends Base
|
||||
);
|
||||
}
|
||||
|
||||
public function getActionListKanban($params, $data, $request)
|
||||
{
|
||||
if (!$this->getAcl()->check($this->name, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$where = $request->get('where');
|
||||
$offset = $request->get('offset');
|
||||
$maxSize = $request->get('maxSize');
|
||||
$asc = $request->get('asc', 'true') === 'true';
|
||||
$sortBy = $request->get('sortBy');
|
||||
$q = $request->get('q');
|
||||
$textFilter = $request->get('textFilter');
|
||||
|
||||
if (empty($maxSize)) {
|
||||
$maxSize = self::MAX_SIZE_LIMIT;
|
||||
}
|
||||
if (!empty($maxSize) && $maxSize > self::MAX_SIZE_LIMIT) {
|
||||
throw new Forbidden("Max should should not exceed " . self::MAX_SIZE_LIMIT . ". Use pagination (offset, limit).");
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'where' => $where,
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'asc' => $asc,
|
||||
'sortBy' => $sortBy,
|
||||
'q' => $q,
|
||||
'textFilter' => $textFilter
|
||||
);
|
||||
|
||||
$this->fetchListParamsFromRequest($params, $request, $data);
|
||||
|
||||
$result = $this->getRecordService()->getListKanban($params);
|
||||
|
||||
return (object) [
|
||||
'total' => $result->total,
|
||||
'list' => $result->collection->getValueMapList(),
|
||||
'additionalData' => $result->additionalData
|
||||
];
|
||||
}
|
||||
|
||||
protected function fetchListParamsFromRequest(&$params, $request, $data)
|
||||
{
|
||||
if ($request->get('primaryFilter')) {
|
||||
|
||||
@@ -292,6 +292,8 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
} else if ($type == 'int') {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
|
||||
} else if ($type == 'float') {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
|
||||
} else if ($type == 'currency') {
|
||||
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
|
||||
@@ -534,6 +536,9 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
if (!array_key_exists($name, $typesCache)) {
|
||||
break;
|
||||
}
|
||||
$type = $typesCache[$name];
|
||||
|
||||
switch ($type) {
|
||||
@@ -546,6 +551,11 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
->getNumberFormat()
|
||||
->setFormatCode('0');
|
||||
} break;
|
||||
case 'float': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
|
||||
} break;
|
||||
case 'date': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Formula\Functions\StringGroup;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class ContainsType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
if (!property_exists($item, 'value') || !is_array($item->value)) {
|
||||
throw new Error('Value for \'String\\Contains\' item is not an array.');
|
||||
}
|
||||
if (count($item->value) < 2) {
|
||||
throw new Error('Bad arguments passed to \'String\\Contains\'.');
|
||||
}
|
||||
$string = $this->evaluate($item->value[0]);
|
||||
$needle = $this->evaluate($item->value[1]);
|
||||
|
||||
if (!is_string($string)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($string, $needle) !== false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Formula\Functions\StringGroup;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class LengthType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
if (!property_exists($item, 'value')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!is_array($item->value)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (count($item->value) < 1) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$value = $this->evaluate($item->value[0]);
|
||||
|
||||
if (!is_string($value)) {
|
||||
$value = strval($value);
|
||||
}
|
||||
|
||||
return mb_strlen($value);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class Htmlizer
|
||||
foreach ($list as $item) {
|
||||
$v = $item;
|
||||
if ($item instanceof \StdClass) {
|
||||
$v = json_decode(json_encode($v), true);
|
||||
$v = json_decode(json_encode($v, \JSON_PRESERVE_ZERO_FRACTION), true);
|
||||
}
|
||||
if (is_array($v)) {
|
||||
foreach ($v as $k => $w) {
|
||||
@@ -139,7 +139,7 @@ class Htmlizer
|
||||
if (!empty($data[$field])) {
|
||||
$value = $data[$field];
|
||||
if ($value instanceof \StdClass) {
|
||||
$data[$field] = json_decode(json_encode($value), true);
|
||||
$data[$field] = json_decode(json_encode($value, \JSON_PRESERVE_ZERO_FRACTION), true);
|
||||
}
|
||||
foreach ($data[$field] as $k => $w) {
|
||||
$keyRaw = $k . '_RAW';
|
||||
@@ -260,13 +260,20 @@ class Htmlizer
|
||||
|
||||
$data = $this->getDataFromEntity($entity, $skipLinks);
|
||||
|
||||
if (!array_key_exists('today', $data)) {
|
||||
$data['today'] = $this->dateTime->getTodayString();
|
||||
}
|
||||
|
||||
if (!array_key_exists('now', $data)) {
|
||||
$data['now'] = $this->dateTime->getNowString();
|
||||
}
|
||||
|
||||
foreach ($additionalData as $k => $value) {
|
||||
$data[$k] = $value;
|
||||
}
|
||||
|
||||
$html = $renderer($data);
|
||||
|
||||
|
||||
$html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html);
|
||||
|
||||
if ($this->getEntityManager()) {
|
||||
|
||||
@@ -179,9 +179,10 @@ class MailMimeParser
|
||||
} else {
|
||||
$email->set('isHtml', false);
|
||||
$email->set('body', $bodyPlain);
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
if (!$email->get('body') && $email->hasBodyPlain()) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
|
||||
@@ -165,9 +165,10 @@ class PhpMimeMailParser
|
||||
} else {
|
||||
$email->set('isHtml', false);
|
||||
$email->set('body', $bodyPlain);
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
if (!$email->get('body') && $email->hasBodyPlain()) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ class ZendMail
|
||||
$this->importPartDataToEmail($email, $zendMessage, $inlineIds, 'text/plain', $inlineAttachmentList);
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
if (!$email->get('body') && $email->hasBodyPlain()) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class ZendMail
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
$bodyPlain = '';
|
||||
if ($email->get('bodyPlain')) {
|
||||
if ($email->hasBodyPlain()) {
|
||||
$bodyPlain .= $email->get('bodyPlain') . "\n";
|
||||
}
|
||||
$bodyPlain .= $content;
|
||||
|
||||
@@ -260,7 +260,7 @@ class Sender
|
||||
$attachment = new MimePart(file_get_contents($fileName));
|
||||
$attachment->disposition = Mime::DISPOSITION_ATTACHMENT;
|
||||
$attachment->encoding = Mime::ENCODING_BASE64;
|
||||
$attachment->filename = $a->get('name');
|
||||
$attachment->filename ='=?utf-8?B?' . base64_encode($a->get('name')) . '?=';
|
||||
if ($a->get('type')) {
|
||||
$attachment->type = $a->get('type');
|
||||
}
|
||||
|
||||
@@ -92,11 +92,27 @@ class Base implements Injectable
|
||||
|
||||
public function process(Entity $entity)
|
||||
{
|
||||
if (!$entity->get('assignedUserId')) return;
|
||||
if (!$entity->isAttributeChanged('assignedUserId')) return;
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
$userIdList = $entity->getLinkMultipleIdList('assignedUsers');
|
||||
$fetchedAssignedUserIdList = $entity->getFetched('assignedUsersIds');
|
||||
if (!is_array($fetchedAssignedUserIdList)) {
|
||||
$fetchedAssignedUserIdList = [];
|
||||
}
|
||||
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
foreach ($userIdList as $userId) {
|
||||
if (in_array($userId, $fetchedAssignedUserIdList)) continue;
|
||||
$this->processForUser($entity, $userId);
|
||||
}
|
||||
} else {
|
||||
if (!$entity->get('assignedUserId')) return;
|
||||
if (!$entity->isAttributeChanged('assignedUserId')) return;
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
$this->processForUser($entity, $assignedUserId);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processForUser(Entity $entity, $assignedUserId)
|
||||
{
|
||||
if ($entity->hasAttribute('createdById') && $entity->hasAttribute('modifiedById')) {
|
||||
if ($entity->isNew()) {
|
||||
$isNotSelfAssignment = $assignedUserId !== $entity->get('createdById');
|
||||
|
||||
@@ -61,7 +61,11 @@ class Entity extends \Espo\ORM\Entity
|
||||
$foreignEntity = $repository->select($select)->where(['id' => $parentId])->findOne();
|
||||
if ($foreignEntity) {
|
||||
$this->set($field . 'Name', $foreignEntity->get('name'));
|
||||
} else {
|
||||
$this->set($field . 'Name', null);
|
||||
}
|
||||
} else {
|
||||
$this->set($field . 'Name', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +147,8 @@ class Entity extends \Espo\ORM\Entity
|
||||
}
|
||||
|
||||
$this->set($idsAttribute, $ids);
|
||||
$this->setFetched($idsAttribute, $ids);
|
||||
|
||||
$this->set($field . 'Names', $names);
|
||||
if ($hasType) {
|
||||
$this->set($field . 'Types', $types);
|
||||
|
||||
@@ -99,6 +99,6 @@ class Application extends \Espo\Core\Application
|
||||
$this->getContainer()->get('clientManager')->display(null, 'html/portal.html', array(
|
||||
'portalId' => $this->getPortal()->id
|
||||
));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -484,9 +484,9 @@ class Base
|
||||
{
|
||||
if ($this->hasAssignedUsersField()) {
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin('assignedUsers', $result);
|
||||
$this->addLeftJoin(['assignedUsers', 'assignedUsersAccess'], $result);
|
||||
$result['whereClause'][] = array(
|
||||
'assignedUsers.id' => $this->getUser()->id
|
||||
'assignedUsersAccess.id' => $this->getUser()->id
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1558,19 +1558,25 @@ class Base
|
||||
protected function boolFilterOnlyMy(&$result)
|
||||
{
|
||||
if (!$this->checkIsPortal()) {
|
||||
if ($this->hasAssignedUserField()) {
|
||||
$result['whereClause'][] = array(
|
||||
if ($this->hasAssignedUsersField()) {
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin(['assignedUsers', 'assignedUsersAccess'], $result);
|
||||
$result['whereClause'][] = [
|
||||
'assignedUsersAccess.id' => $this->getUser()->id
|
||||
];
|
||||
} else if ($this->hasAssignedUserField()) {
|
||||
$result['whereClause'][] = [
|
||||
'assignedUserId' => $this->getUser()->id
|
||||
);
|
||||
];
|
||||
} else {
|
||||
$result['whereClause'][] = array(
|
||||
$result['whereClause'][] = [
|
||||
'createdById' => $this->getUser()->id
|
||||
);
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$result['whereClause'][] = array(
|
||||
$result['whereClause'][] = [
|
||||
'createdById' => $this->getUser()->id
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "personName"
|
||||
"type": "personName",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
@@ -24,15 +25,18 @@
|
||||
"type": "text"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
"type": "email",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
"typeList": ["Mobile", "Office", "Home", "Fax", "Other"],
|
||||
"defaultType": "Mobile"
|
||||
"defaultType": "Mobile",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"address": {
|
||||
"type": "address"
|
||||
"type": "address",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"addressStreet": {
|
||||
"type": "text",
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"aclPortalLevelList": ["all", "account", "contact", "own", "no"],
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true
|
||||
"notifications": true,
|
||||
"hasPersonalData": true
|
||||
}
|
||||
5
application/Espo/Core/Templates/i18n/hr_HR/Base.json
Normal file
5
application/Espo/Core/Templates/i18n/hr_HR/Base.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Napravi {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
10
application/Espo/Core/Templates/i18n/hr_HR/BasePlus.json
Normal file
10
application/Espo/Core/Templates/i18n/hr_HR/BasePlus.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"links": {
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Kreiraj {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
15
application/Espo/Core/Templates/i18n/hr_HR/Company.json
Normal file
15
application/Espo/Core/Templates/i18n/hr_HR/Company.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"fields": {
|
||||
"billingAddress": "Adresa za naplatu",
|
||||
"shippingAddress": "Adresa za dostavu",
|
||||
"website": "Sajt"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Kreiraj {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
35
application/Espo/Core/Templates/i18n/hr_HR/Event.json
Normal file
35
application/Espo/Core/Templates/i18n/hr_HR/Event.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"fields": {
|
||||
"parent": "Nadređen",
|
||||
"dateStart": "Početni datum",
|
||||
"dateEnd": "Završni datum",
|
||||
"duration": "Trajanje",
|
||||
"reminders": "Podsjetnici"
|
||||
},
|
||||
"links": {
|
||||
"parent": "Nadređen"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planiran",
|
||||
"Held": "Održan",
|
||||
"Not Held": "Nije održan"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Kreiraj {entityTypeTranslated}",
|
||||
"Schedule {entityType}": "Zakaži {entityTypeTranslated}",
|
||||
"Log {entityType}": "Zabilježi {entitiTipeTranslated}",
|
||||
"Set Held": "Postavi kao održano",
|
||||
"Set Not Held": "Postavi kao nije održano"
|
||||
},
|
||||
"massActions": {
|
||||
"setHeld": "Postavi kao održano",
|
||||
"setNotHeld": "Postavi kao nije održano"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planiran",
|
||||
"held": "Održan",
|
||||
"todays": "Današnji"
|
||||
}
|
||||
}
|
||||
13
application/Espo/Core/Templates/i18n/hr_HR/Person.json
Normal file
13
application/Espo/Core/Templates/i18n/hr_HR/Person.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresa"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Kreiraj {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"links": {
|
||||
"meetings": "Spotkania",
|
||||
"calls": "Połączenia",
|
||||
"tasks": "Zadania"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"fields": {
|
||||
"billingAddress": "Adres rozliczeniowy",
|
||||
"shippingAddress": "Adres dostawy",
|
||||
"website": "Strona internetowa"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Spotkania",
|
||||
"calls": "Połączenia",
|
||||
"tasks": "Zadania"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
"fields": {
|
||||
"dateStart": "Data rozpoczęcia",
|
||||
"dateEnd": "Data zakończenia",
|
||||
"duration": "Czas trwania",
|
||||
"reminders": "Przypomnienia"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planowane"
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}",
|
||||
"Schedule {entityType}": "Zaplanuj {entityTypeTranslated} "
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planowane"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
"fields": {
|
||||
"address": "Adres"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Spotkania",
|
||||
"calls": "Rozmowy",
|
||||
"tasks": "Zadania"
|
||||
},
|
||||
"labels": {
|
||||
"Create {entityType}": "Utwórz {entityTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,9 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('before');
|
||||
if (!isset($data['skipBeforeScript']) || !$data['skipBeforeScript']) {
|
||||
$this->runScript('before');
|
||||
}
|
||||
|
||||
/* remove files defined in a manifest "deleteBeforeCopy" */
|
||||
$this->deleteFiles('deleteBeforeCopy', true);
|
||||
@@ -89,8 +91,10 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
$this->deleteFiles('vendor');
|
||||
$this->copyFiles('vendor');
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
if (!isset($data['skipSystemRebuild']) || !$data['skipSystemRebuild']) {
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
}
|
||||
|
||||
//afterInstallFiles
|
||||
@@ -99,7 +103,9 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
|
||||
/* run before install script */
|
||||
$this->runScript('after');
|
||||
if (!isset($data['skipAfterScript']) || !$data['skipAfterScript']) {
|
||||
$this->runScript('after');
|
||||
}
|
||||
|
||||
$this->afterRunAction();
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
$this->beforeRunAction();
|
||||
|
||||
/* run before install script */
|
||||
if (!isset($data['isNotRunScriptBefore']) || !$data['isNotRunScriptBefore']) {
|
||||
if (!isset($data['skipBeforeScript']) || !$data['skipBeforeScript']) {
|
||||
$this->runScript('beforeUninstall');
|
||||
}
|
||||
|
||||
@@ -71,12 +71,14 @@ class Uninstall extends \Espo\Core\Upgrades\Actions\Base
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
if (!isset($data['skipSystemRebuild']) || !$data['skipSystemRebuild']) {
|
||||
if (!$this->systemRebuild()) {
|
||||
$this->throwErrorAndRemovePackage('Error occurred while EspoCRM rebuild.');
|
||||
}
|
||||
}
|
||||
|
||||
/* run after uninstall script */
|
||||
if (!isset($data['isNotRunScriptAfter']) || !$data['isNotRunScriptAfter']) {
|
||||
if (!isset($data['skipAfterScript']) || !$data['skipAfterScript']) {
|
||||
$this->runScript('afterUninstall');
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,8 @@ class Install extends \Espo\Core\Upgrades\Actions\Base\Install
|
||||
|
||||
$this->executeAction(ExtensionManager::UNINSTALL, array(
|
||||
'id' => $extensionEntity->get('id'),
|
||||
'isNotRunScriptAfter' => true,
|
||||
'skipSystemRebuild' => true,
|
||||
'skipAfterScript' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ class AdminNotificationManager
|
||||
$latestVersion = $config->get('latestVersion');
|
||||
if (isset($latestVersion)) {
|
||||
$currentVersion = $config->get('version');
|
||||
if ($currentVersion === 'dev') return;
|
||||
if (version_compare($latestVersion, $currentVersion, '>')) {
|
||||
return array(
|
||||
'currentVersion' => $currentVersion,
|
||||
|
||||
@@ -58,7 +58,7 @@ class Auth extends \Slim\Middleware
|
||||
|
||||
$espoAuth = $req->headers('HTTP_ESPO_AUTHORIZATION');
|
||||
if (isset($espoAuth)) {
|
||||
list($authUsername, $authPassword) = explode(':', base64_decode($espoAuth));
|
||||
list($authUsername, $authPassword) = explode(':', base64_decode($espoAuth), 2);
|
||||
}
|
||||
|
||||
if (!isset($authUsername)) {
|
||||
@@ -84,7 +84,12 @@ class Auth extends \Slim\Middleware
|
||||
if (isset($routeConditions['auth']) && $routeConditions['auth'] === false) {
|
||||
|
||||
if ($authUsername && $authPassword) {
|
||||
$isAuthenticated = $this->auth->login($authUsername, $authPassword);
|
||||
try {
|
||||
$isAuthenticated = $this->auth->login($authUsername, $authPassword);
|
||||
} catch (\Exception $e) {
|
||||
$this->processException($e);
|
||||
return;
|
||||
}
|
||||
if ($isAuthenticated) {
|
||||
$this->next->call();
|
||||
return;
|
||||
@@ -105,8 +110,12 @@ class Auth extends \Slim\Middleware
|
||||
}
|
||||
|
||||
if ($authUsername && $authPassword) {
|
||||
|
||||
$isAuthenticated = $this->auth->login($authUsername, $authPassword);
|
||||
try {
|
||||
$isAuthenticated = $this->auth->login($authUsername, $authPassword);
|
||||
} catch (\Exception $e) {
|
||||
$this->processException($e);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($isAuthenticated) {
|
||||
$this->next->call();
|
||||
@@ -121,23 +130,31 @@ class Auth extends \Slim\Middleware
|
||||
}
|
||||
}
|
||||
|
||||
protected function processException(\Exception $e)
|
||||
{
|
||||
$response = $this->app->response();
|
||||
|
||||
if ($e->getMessage()) {
|
||||
$response->headers->set('X-Status-Reason', $e->getMessage());
|
||||
}
|
||||
$response->setStatus($e->getCode());
|
||||
}
|
||||
|
||||
protected function processUnauthorized()
|
||||
{
|
||||
$res = $this->app->response();
|
||||
$response = $this->app->response();
|
||||
|
||||
if ($this->showDialog) {
|
||||
$res->header('WWW-Authenticate', 'Basic realm=""');
|
||||
} else {
|
||||
$res->header('WWW-Authenticate');
|
||||
$response->headers->set('WWW-Authenticate', 'Basic realm=""');
|
||||
}
|
||||
$res->status(401);
|
||||
$response->setStatus(401);
|
||||
}
|
||||
|
||||
protected function isXMLHttpRequest()
|
||||
{
|
||||
$req = $this->app->request();
|
||||
$request = $this->app->request();
|
||||
|
||||
$httpXRequestedWith = $req->headers('HTTP_X_REQUESTED_WITH');
|
||||
$httpXRequestedWith = $request->headers('HTTP_X_REQUESTED_WITH');
|
||||
|
||||
if (isset($httpXRequestedWith) && strtolower($httpXRequestedWith) == 'xmlhttprequest') {
|
||||
return true;
|
||||
@@ -145,6 +162,4 @@ class Auth extends \Slim\Middleware
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Api;
|
||||
|
||||
class Output
|
||||
{
|
||||
private $slim;
|
||||
@@ -96,8 +97,8 @@ class Output
|
||||
ob_clean();
|
||||
|
||||
if (!empty( $this->slim)) {
|
||||
$this->getSlim()->response()->status($statusCode);
|
||||
$this->getSlim()->response()->header('X-Status-Reason', $text);
|
||||
$this->getSlim()->response()->setStatus($statusCode);
|
||||
$this->getSlim()->response()->headers->set('X-Status-Reason', $text);
|
||||
|
||||
if ($isPrint) {
|
||||
$status = $this->getCodeDesc($statusCode);
|
||||
@@ -139,4 +140,3 @@ class Output
|
||||
return preg_replace('/"(.*?password.*?)":".*?"/i', '"$1":"*****"', $inputData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ class Auth
|
||||
|
||||
const ACCESS_ANY = 3;
|
||||
|
||||
const FAILED_ATTEMPTS_PERIOD = '60 seconds';
|
||||
|
||||
const MAX_FAILED_ATTEMPT_NUMBER = 10;
|
||||
|
||||
private $portal;
|
||||
|
||||
public function __construct(\Espo\Core\Container $container, $allowAnyAccess = false)
|
||||
@@ -117,20 +121,34 @@ class Auth
|
||||
|
||||
public function login($username, $password)
|
||||
{
|
||||
$authToken = $this->getEntityManager()->getRepository('AuthToken')->where(array(
|
||||
'token' => $password,
|
||||
'isActive' => true
|
||||
))->findOne();
|
||||
$isByTokenOnly = false;
|
||||
if ($this->request->headers->get('HTTP_ESPO_AUTHORIZATION_BY_TOKEN') === 'true') {
|
||||
$isByTokenOnly = true;
|
||||
}
|
||||
|
||||
if (!$isByTokenOnly) {
|
||||
$this->checkFailedAttemptsLimit($username);
|
||||
}
|
||||
|
||||
$authToken = $this->getEntityManager()->getRepository('AuthToken')->where([
|
||||
'token' => $password
|
||||
])->findOne();
|
||||
|
||||
$authTokenIsFound = false;
|
||||
|
||||
if ($authToken) {
|
||||
$authTokenIsFound = true;
|
||||
}
|
||||
|
||||
if ($authToken && $authToken->get('isActive')) {
|
||||
if (!$this->allowAnyAccess) {
|
||||
if ($this->isPortal() && $authToken->get('portalId') !== $this->getPortal()->id) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login to portal with a token not related to portal.");
|
||||
return false;
|
||||
$GLOBALS['log']->info("AUTH: Trying to login to portal with a token not related to portal.");
|
||||
return;
|
||||
}
|
||||
if (!$this->isPortal() && $authToken->get('portalId')) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login to crm with a token related to portal.");
|
||||
return false;
|
||||
$GLOBALS['log']->info("AUTH: Trying to login to crm with a token related to portal.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($this->allowAnyAccess) {
|
||||
@@ -141,77 +159,190 @@ class Auth
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$authToken = null;
|
||||
}
|
||||
|
||||
if ($isByTokenOnly && !$authToken) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login as user '{$username}' by token but token is not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->authentication->login($username, $password, $authToken);
|
||||
|
||||
if ($user) {
|
||||
if (!$user->isActive()) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login as user '".$user->get('userName')."' which is not active.");
|
||||
return false;
|
||||
}
|
||||
$authLogRecord = null;
|
||||
|
||||
if (!$user->isAdmin() && !$this->isPortal() && $user->get('isPortalUser')) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login to crm as a portal user '".$user->get('userName')."'.");
|
||||
return false;
|
||||
}
|
||||
if (!$authTokenIsFound) {
|
||||
$authLogRecord = $this->createAuthLogRecord($username, $user);
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() && $this->isPortal() && !$user->get('isPortalUser')) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login to portal as user '".$user->get('userName')."' which is not portal user.");
|
||||
return false;
|
||||
}
|
||||
if (!$user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isPortal()) {
|
||||
if (!$user->isAdmin() && !$this->getEntityManager()->getRepository('Portal')->isRelated($this->getPortal(), 'users', $user)) {
|
||||
$GLOBALS['log']->debug("AUTH: Trying to login to portal as user '".$user->get('userName')."' which is portal user but does not belongs to portal.");
|
||||
return false;
|
||||
if (!$user->isActive()) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login as user '".$user->get('userName')."' which is not active.");
|
||||
$this->logDenied($authLogRecord, 'INACTIVE_USER');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() && !$this->isPortal() && $user->get('isPortalUser')) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login to crm as a portal user '".$user->get('userName')."'.");
|
||||
$this->logDenied($authLogRecord, 'IS_PORTAL_USER');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() && $this->isPortal() && !$user->get('isPortalUser')) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login to portal as user '".$user->get('userName')."' which is not portal user.");
|
||||
$this->logDenied($authLogRecord, 'IS_NOT_PORTAL_USER');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isPortal()) {
|
||||
if (!$user->isAdmin() && !$this->getEntityManager()->getRepository('Portal')->isRelated($this->getPortal(), 'users', $user)) {
|
||||
$GLOBALS['log']->info("AUTH: Trying to login to portal as user '".$user->get('userName')."' which is portal user but does not belongs to portal.");
|
||||
$this->logDenied($authLogRecord, 'USER_IS_NOT_IN_PORTAL');
|
||||
return;
|
||||
}
|
||||
$user->set('portalId', $this->getPortal()->id);
|
||||
} else {
|
||||
$user->loadLinkMultipleField('teams');
|
||||
}
|
||||
|
||||
$user->set('ipAddress', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$this->getEntityManager()->setUser($user);
|
||||
$this->getContainer()->setUser($user);
|
||||
|
||||
if ($this->request->headers->get('HTTP_ESPO_AUTHORIZATION')) {
|
||||
if (!$authToken) {
|
||||
$authToken = $this->getEntityManager()->getEntity('AuthToken');
|
||||
$token = $this->generateToken();
|
||||
$authToken->set('token', $token);
|
||||
$authToken->set('hash', $user->get('password'));
|
||||
$authToken->set('ipAddress', $_SERVER['REMOTE_ADDR']);
|
||||
$authToken->set('userId', $user->id);
|
||||
if ($this->isPortal()) {
|
||||
$authToken->set('portalId', $this->getPortal()->id);
|
||||
}
|
||||
$user->set('portalId', $this->getPortal()->id);
|
||||
} else {
|
||||
$user->loadLinkMultipleField('teams');
|
||||
}
|
||||
|
||||
$user->set('ipAddress', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$this->getEntityManager()->setUser($user);
|
||||
$this->getContainer()->setUser($user);
|
||||
|
||||
if ($this->request->headers->get('HTTP_ESPO_AUTHORIZATION')) {
|
||||
if (!$authToken) {
|
||||
$authToken = $this->getEntityManager()->getEntity('AuthToken');
|
||||
$token = $this->createToken($user);
|
||||
$authToken->set('token', $token);
|
||||
$authToken->set('hash', $user->get('password'));
|
||||
$authToken->set('ipAddress', $_SERVER['REMOTE_ADDR']);
|
||||
$authToken->set('userId', $user->id);
|
||||
if ($this->isPortal()) {
|
||||
$authToken->set('portalId', $this->getPortal()->id);
|
||||
if ($this->getConfig()->get('authTokenPreventConcurrent')) {
|
||||
$concurrentAuthTokenList = $this->getEntityManager()->getRepository('AuthToken')->select(['id'])->where([
|
||||
'userId' => $user->id,
|
||||
'isActive' => true
|
||||
])->find();
|
||||
foreach ($concurrentAuthTokenList as $concurrentAuthToken) {
|
||||
$concurrentAuthToken->set('isActive', false);
|
||||
$this->getEntityManager()->saveEntity($concurrentAuthToken);
|
||||
}
|
||||
}
|
||||
$authToken->set('lastAccess', date('Y-m-d H:i:s'));
|
||||
|
||||
$this->getEntityManager()->saveEntity($authToken);
|
||||
$user->set('token', $authToken->get('token'));
|
||||
$user->set('authTokenId', $authToken->id);
|
||||
}
|
||||
}
|
||||
$authToken->set('lastAccess', date('Y-m-d H:i:s'));
|
||||
|
||||
return true;
|
||||
$this->getEntityManager()->saveEntity($authToken);
|
||||
$user->set('token', $authToken->get('token'));
|
||||
$user->set('authTokenId', $authToken->id);
|
||||
|
||||
if ($authLogRecord) {
|
||||
$authLogRecord->set('authTokenId', $authToken->id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($authLogRecord) {
|
||||
$this->getEntityManager()->saveEntity($authLogRecord);
|
||||
}
|
||||
|
||||
if ($authToken && !$authLogRecord) {
|
||||
$authLogRecord = $this->getEntityManager()->getRepository('AuthLogRecord')->select(['id'])->where([
|
||||
'authTokenId' => $authToken->id
|
||||
])->order('requestTime', true)->findOne();
|
||||
}
|
||||
|
||||
if ($authLogRecord) {
|
||||
$user->set('authLogRecordId', $authLogRecord->id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkFailedAttemptsLimit($username = null)
|
||||
{
|
||||
$failedAttemptsPeriod = $this->getConfig()->get('authFailedAttemptsPeriod', self::FAILED_ATTEMPTS_PERIOD);
|
||||
$maxFailedAttempts = $this->getConfig()->get('authMaxFailedAttemptNumber', self::MAX_FAILED_ATTEMPT_NUMBER);
|
||||
|
||||
$requestTimeFrom = (new \DateTime('@' . intval($_SERVER['REQUEST_TIME_FLOAT'])))->modify('-' . $failedAttemptsPeriod);
|
||||
|
||||
$failAttemptCount = $this->getEntityManager()->getRepository('AuthLogRecord')->where([
|
||||
'requestTime>' => $requestTimeFrom->format('U'),
|
||||
'ipAddress' => $_SERVER['REMOTE_ADDR'],
|
||||
'isDenied' => true
|
||||
])->count();
|
||||
|
||||
if ($failAttemptCount > $maxFailedAttempts) {
|
||||
$GLOBALS['log']->warning("AUTH: Max failed login attempts exceeded for IP '".$_SERVER['REMOTE_ADDR']."'.");
|
||||
throw new Forbidden("Max failed login attempts exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function createToken($user)
|
||||
protected function generateToken()
|
||||
{
|
||||
return md5(uniqid($user->get('id')));
|
||||
$length = 16;
|
||||
|
||||
if (function_exists('random_bytes')) {
|
||||
return bin2hex(random_bytes($length));
|
||||
}
|
||||
if (function_exists('mcrypt_create_iv')) {
|
||||
return bin2hex(mcrypt_create_iv($length, \MCRYPT_DEV_URANDOM));
|
||||
}
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return bin2hex(openssl_random_pseudo_bytes($length));
|
||||
}
|
||||
}
|
||||
|
||||
public function destroyAuthToken($token)
|
||||
{
|
||||
$authToken = $this->getEntityManager()->getRepository('AuthToken')->where(array('token' => $token))->findOne();
|
||||
$authToken = $this->getEntityManager()->getRepository('AuthToken')->select(['id', 'isActive'])->where(['token' => $token])->findOne();
|
||||
if ($authToken) {
|
||||
$authToken->set('isActive', false);
|
||||
$this->getEntityManager()->saveEntity($authToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createAuthLogRecord($username, $user)
|
||||
{
|
||||
if ($username === '**logout') return;
|
||||
|
||||
$authLogRecord = $this->getEntityManager()->getEntity('AuthLogRecord');
|
||||
|
||||
$authLogRecord->set([
|
||||
'username' => $username,
|
||||
'ipAddress' => $_SERVER['REMOTE_ADDR'],
|
||||
'requestTime' => $_SERVER['REQUEST_TIME_FLOAT'],
|
||||
'requestMethod' => $this->request->getMethod(),
|
||||
'requestUrl' => $this->request->getUrl() . $this->request->getPath()
|
||||
]);
|
||||
|
||||
if ($this->isPortal()) {
|
||||
$authLogRecord->set('portalId', $this->getPortal()->id);
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
$authLogRecord->set('userId', $user->id);
|
||||
} else {
|
||||
$authLogRecord->set('isDenied', true);
|
||||
$authLogRecord->set('denialReason', 'CREDENTIALS');
|
||||
$this->getEntityManager()->saveEntity($authLogRecord);
|
||||
}
|
||||
|
||||
return $authLogRecord;
|
||||
}
|
||||
|
||||
protected function logDenied($authLogRecord, $denialReason)
|
||||
{
|
||||
if (!$authLogRecord) return;
|
||||
|
||||
$authLogRecord->set('denialReason', $denialReason);
|
||||
$this->getEntityManager()->saveEntity($authLogRecord);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ class LDAP extends Base
|
||||
|
||||
$ldapClient = $this->getLdapClient();
|
||||
|
||||
//login LDAP system user (ldapUsername, ldapPassword)
|
||||
/* Login LDAP system user (ldapUsername, ldapPassword) */
|
||||
try {
|
||||
$ldapClient->bind();
|
||||
} catch (\Exception $e) {
|
||||
@@ -119,17 +119,30 @@ class LDAP extends Base
|
||||
if (!isset($adminUser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('LDAP: Administrator ['.$username.'] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
if (!isset($adminUser)) {
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
$GLOBALS['log']->debug('Found DN for ['.$username.']: ['.$userDn.'].');
|
||||
try {
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Error while finding DN for ['.$username.'], details: ' . $e->getMessage() . '.');
|
||||
}
|
||||
|
||||
if (!isset($userDn)) {
|
||||
$GLOBALS['log']->error('LDAP: Authentication failed for user ['.$username.'], details: user is not found.');
|
||||
return;
|
||||
|
||||
$adminUser = $this->adminLogin($username, $password);
|
||||
if (!isset($adminUser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('LDAP: Administrator ['.$username.'] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
$GLOBALS['log']->debug('User ['.$username.'] is found with this DN ['.$userDn.'].');
|
||||
|
||||
try {
|
||||
$ldapClient->bind($userDn, $password);
|
||||
} catch (\Exception $e) {
|
||||
@@ -179,7 +192,7 @@ class LDAP extends Base
|
||||
$user = $this->getEntityManager()->getRepository('User')->findOne(array(
|
||||
'whereClause' => array(
|
||||
'userName' => $username,
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
return $user;
|
||||
@@ -201,7 +214,7 @@ class LDAP extends Base
|
||||
'userName' => $username,
|
||||
'password' => $hash,
|
||||
'isAdmin' => 1
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
return $user;
|
||||
|
||||
@@ -109,8 +109,5 @@ class ClientManager
|
||||
}
|
||||
|
||||
echo $html;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ class Converter
|
||||
'maxLength' => 'len',
|
||||
'len' => 'len',
|
||||
'notNull' => 'notNull',
|
||||
'exportDisabled' => 'notExportable',
|
||||
'autoincrement' => 'autoincrement',
|
||||
'entity' => 'entity',
|
||||
'notStorable' => 'notStorable',
|
||||
|
||||
@@ -37,20 +37,23 @@ class AttachmentMultiple extends Base
|
||||
$entityType => array (
|
||||
'fields' => array(
|
||||
$fieldName.'Ids' => array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
'orderBy' => [['createdAt', 'ASC'], ['name', 'ASC']],
|
||||
'isLinkMultipleIdList' => true
|
||||
),
|
||||
$fieldName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true
|
||||
),
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleNameMap' => true
|
||||
)
|
||||
)
|
||||
),
|
||||
'unset' => array(
|
||||
$entityType => array(
|
||||
'fields.'.$fieldName,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $data;
|
||||
|
||||
@@ -85,6 +85,11 @@ class Email extends Base
|
||||
'type' => 'text',
|
||||
'notStorable' => true
|
||||
),
|
||||
$fieldName .'IsOptedOut' => array(
|
||||
'type' => 'bool',
|
||||
'notStorable' => true,
|
||||
'select' => 'emailAddresses.opt_out'
|
||||
)
|
||||
),
|
||||
'relations' => array(
|
||||
'emailAddresses' => array(
|
||||
|
||||
@@ -37,14 +37,15 @@ class LinkMultiple extends Base
|
||||
$entityName => array (
|
||||
'fields' => array(
|
||||
$fieldName.'Ids' => array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonArray',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleIdList' => true,
|
||||
'relation' => $fieldName
|
||||
),
|
||||
$fieldName.'Names' => array(
|
||||
'type' => 'varchar',
|
||||
'notStorable' => true
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
'isLinkMultipleNameMap' => true
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -67,7 +68,7 @@ class LinkMultiple extends Base
|
||||
$columns = $this->getMetadata()->get("entityDefs.{$entityName}.fields.{$fieldName}.columns");
|
||||
if (!empty($columns)) {
|
||||
$data[$entityName]['fields'][$fieldName . 'Columns'] = array(
|
||||
'type' => 'varchar',
|
||||
'type' => 'jsonObject',
|
||||
'notStorable' => true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ class Converter
|
||||
'foreign'
|
||||
);
|
||||
|
||||
protected $maxIndexLength;
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Database\Schema\Schema $databaseSchema)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
@@ -119,6 +121,15 @@ class Converter
|
||||
return $this->databaseSchema;
|
||||
}
|
||||
|
||||
protected function getMaxIndexLength()
|
||||
{
|
||||
if (!isset($this->maxIndexLength)) {
|
||||
$this->maxIndexLength = $this->getDatabaseSchema()->getMaxIndexLength();
|
||||
}
|
||||
|
||||
return $this->maxIndexLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema convertation process
|
||||
*
|
||||
@@ -164,7 +175,7 @@ class Converter
|
||||
$schema = $this->getSchema(true);
|
||||
|
||||
$indexList = SchemaUtils::getIndexList($ormMeta);
|
||||
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $this->getDatabaseSchema()->getMaxIndexLength());
|
||||
$fieldListExceededIndexMaxLength = SchemaUtils::getFieldListExceededIndexMaxLength($ormMeta, $this->getMaxIndexLength());
|
||||
|
||||
$tables = array();
|
||||
foreach ($ormMeta as $entityName => $entityParams) {
|
||||
@@ -358,7 +369,9 @@ class Converter
|
||||
case 'id':
|
||||
case 'foreignId':
|
||||
case 'foreignType':
|
||||
$fieldParams['utf8mb3'] = true;
|
||||
if ($this->getMaxIndexLength() < 3072) {
|
||||
$fieldParams['utf8mb3'] = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
|
||||
@@ -189,4 +189,38 @@ class DateTime
|
||||
{
|
||||
return date($this->getInternalDateFormat());
|
||||
}
|
||||
|
||||
public function getTodayString($timezone = null)
|
||||
{
|
||||
if ($timezone) {
|
||||
$timezoneObj = new \DateTimeZone($timezone);
|
||||
} else {
|
||||
$timezoneObj = $this->timezone;
|
||||
}
|
||||
|
||||
$dateTime = new \DateTime();
|
||||
$dateTime->setTimezone($timezoneObj);
|
||||
|
||||
return $dateTime->format($this->getPhpDateFormat());
|
||||
}
|
||||
|
||||
public function getNowString($timezone = null, $format = null)
|
||||
{
|
||||
if ($timezone) {
|
||||
$timezoneObj = new \DateTimeZone($timezone);
|
||||
} else {
|
||||
$timezoneObj = $this->timezone;
|
||||
}
|
||||
|
||||
$dateTime = new \DateTime();
|
||||
$dateTime->setTimezone($timezoneObj);
|
||||
|
||||
if ($format) {
|
||||
$phpFormat = $this->convertFormatToPhp($format);
|
||||
} else {
|
||||
$phpFormat = $this->getPhpDateTimeFormat();
|
||||
}
|
||||
|
||||
return $dateTime->format($phpFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +283,10 @@ class EntityManager
|
||||
$scopesData['isNotRemovable'] = true;
|
||||
}
|
||||
|
||||
if (!empty($params['kanbanStatusIgnoreList'])) {
|
||||
$scopesData['kanbanStatusIgnoreList'] = $params['kanbanStatusIgnoreList'];
|
||||
}
|
||||
|
||||
$this->getMetadata()->set('scopes', $name, $scopesData);
|
||||
|
||||
$filePath = $templatePath . "/Metadata/{$type}/entityDefs.json";
|
||||
@@ -302,6 +306,18 @@ class EntityManager
|
||||
$clientDefsContents = str_replace('{'.$key.'}', $value, $clientDefsContents);
|
||||
}
|
||||
$clientDefsData = Json::decode($clientDefsContents, true);
|
||||
|
||||
if (array_key_exists('color', $params)) {
|
||||
$clientDefsData['color'] = $params['color'];
|
||||
}
|
||||
|
||||
if (array_key_exists('iconClass', $params)) {
|
||||
$clientDefsData['iconClass'] = $params['iconClass'];
|
||||
}
|
||||
|
||||
if (!empty($params['kanbanViewMode'])) {
|
||||
$clientDefsData['kanbanViewMode'] = true;
|
||||
}
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
|
||||
$this->getBaseLanguage()->set('Global', 'scopeNames', $name, $labelSingular);
|
||||
@@ -387,6 +403,32 @@ class EntityManager
|
||||
$this->getMetadata()->set('entityDefs', $name, $entityDefsData);
|
||||
}
|
||||
|
||||
if (array_key_exists('kanbanStatusIgnoreList', $data)) {
|
||||
$scopeData['kanbanStatusIgnoreList'] = $data['kanbanStatusIgnoreList'];
|
||||
$this->getMetadata()->set('scopes', $name, $scopeData);
|
||||
}
|
||||
|
||||
if (array_key_exists('kanbanViewMode', $data)) {
|
||||
$clientDefsData = [
|
||||
'kanbanViewMode' => $data['kanbanViewMode']
|
||||
];
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
}
|
||||
|
||||
if (array_key_exists('color', $data)) {
|
||||
$clientDefsData = [
|
||||
'color' => $data['color']
|
||||
];
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
}
|
||||
|
||||
if (array_key_exists('iconClass', $data)) {
|
||||
$clientDefsData = [
|
||||
'iconClass' => $data['iconClass']
|
||||
];
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
}
|
||||
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
if ($isCustom) {
|
||||
@@ -948,4 +990,33 @@ class EntityManager
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public function resetToDefaults($scope)
|
||||
{
|
||||
if ($this->isCustom($scope)) {
|
||||
throw new Error("Can't reset to defaults custom entity type '{$scope}.'");
|
||||
}
|
||||
|
||||
$this->getMetadata()->delete('scopes', $scope, [
|
||||
'disabled',
|
||||
'stream',
|
||||
'statusField',
|
||||
'kanbanStatusIgnoreList'
|
||||
]);
|
||||
$this->getMetadata()->delete('clientDefs', $scope, [
|
||||
'iconClass',
|
||||
'statusField',
|
||||
'kanbanViewMode'
|
||||
]);
|
||||
$this->getMetadata()->delete('entityDefs', $scope, [
|
||||
'collection.sortBy',
|
||||
'collection.asc',
|
||||
'collection.textFilterFields'
|
||||
]);
|
||||
$this->getMetadata()->save();
|
||||
|
||||
$this->getLanguage()->delete('Global', 'scopeNames', $scope);
|
||||
$this->getLanguage()->delete('Global', 'scopeNamesPlural', $scope);
|
||||
$this->getLanguage()->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ return array (
|
||||
'da_DK',
|
||||
'de_DE',
|
||||
'es_ES',
|
||||
'hr_HR',
|
||||
'fr_FR',
|
||||
'id_ID',
|
||||
'it_IT',
|
||||
@@ -113,6 +114,7 @@ return array (
|
||||
'assignmentNotificationsEntityList' => ['Meeting', 'Call', 'Task', 'Email'],
|
||||
"portalStreamEmailNotifications" => true,
|
||||
'streamEmailNotificationsEntityList' => ['Case'],
|
||||
'streamEmailNotificationsTypeList' => ['Post', 'Status', 'EmailReceived'],
|
||||
'emailMessageMaxSize' => 10,
|
||||
'notificationsCheckInterval' => 10,
|
||||
'disabledCountQueryEntityList' => ['Email'],
|
||||
@@ -165,6 +167,7 @@ return array (
|
||||
'aclAllowDeleteCreated' => false,
|
||||
'inlineAttachmentUploadMaxSize' => 20,
|
||||
'textFilterUseContainsForVarchar' => false,
|
||||
'tabColorsDisabled' => false,
|
||||
'isInstalled' => false
|
||||
);
|
||||
|
||||
|
||||
3
application/Espo/Core/defaults/layouts/kanban.json
Normal file
3
application/Espo/Core/defaults/layouts/kanban.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{"name":"name", "link": true}
|
||||
]
|
||||
35
application/Espo/Entities/AuthLogRecord.php
Normal file
35
application/Espo/Entities/AuthLogRecord.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Entities;
|
||||
|
||||
class AuthLogRecord extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
@@ -72,11 +72,20 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getBodyPlain()
|
||||
{
|
||||
return $this->getBodyPlain();
|
||||
}
|
||||
|
||||
public function hasBodyPlain()
|
||||
{
|
||||
return !empty($this->valuesContainer['bodyPlain']);
|
||||
}
|
||||
|
||||
public function getBodyPlain()
|
||||
{
|
||||
$bodyPlain = $this->get('bodyPlain');
|
||||
if (!empty($bodyPlain)) {
|
||||
return $bodyPlain;
|
||||
if (!empty($this->valuesContainer['bodyPlain'])) {
|
||||
return $this->valuesContainer['bodyPlain'];
|
||||
}
|
||||
|
||||
$body = $this->get('body');
|
||||
@@ -84,6 +93,34 @@ class Email extends \Espo\Core\ORM\Entity
|
||||
$breaks = array("<br />","<br>","<br/>","<br />","<br />","<br/>","<br>");
|
||||
$body = str_ireplace($breaks, "\r\n", $body);
|
||||
$body = strip_tags($body);
|
||||
|
||||
$reList = [
|
||||
'/&(quot|#34);/i',
|
||||
'/&(amp|#38);/i',
|
||||
'/&(lt|#60);/i',
|
||||
'/&(gt|#62);/i',
|
||||
'/&(nbsp|#160);/i',
|
||||
'/&(iexcl|#161);/i',
|
||||
'/&(cent|#162);/i',
|
||||
'/&(pound|#163);/i',
|
||||
'/&(copy|#169);/i',
|
||||
'/&(reg|#174);/i'
|
||||
];
|
||||
$replaceList = [
|
||||
'',
|
||||
'&',
|
||||
'<',
|
||||
'>',
|
||||
' ',
|
||||
chr(161),
|
||||
chr(162),
|
||||
chr(163),
|
||||
chr(169),
|
||||
chr(174)
|
||||
];
|
||||
|
||||
$body = preg_replace($reList, $replaceList, $body);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,17 +34,14 @@ class Note extends \Espo\Core\ORM\Entity
|
||||
public function loadAttachments()
|
||||
{
|
||||
$data = $this->get('data');
|
||||
if (!empty($data) && !empty($data->attachmentsIds)) {
|
||||
if (!empty($data) && !empty($data->attachmentsIds) && is_array($data->attachmentsIds)) {
|
||||
$attachmentsIds = $data->attachmentsIds;
|
||||
$collection = array();
|
||||
foreach ($attachmentsIds as $id) {
|
||||
$attachment = $this->entityManager->getEntity('Attachment', $id);
|
||||
if ($attachment) {
|
||||
$collection[] = $attachment;
|
||||
}
|
||||
}
|
||||
$collection = $this->entityManager->getRepository('Attachment')->select(['id', 'name', 'type'])->order('createdAt')->where([
|
||||
'id' => $attachmentsIds
|
||||
])->find();
|
||||
} else {
|
||||
$collection = $this->get('attachments');
|
||||
$this->loadLinkMultipleField('attachments');
|
||||
return;
|
||||
}
|
||||
|
||||
$ids = array();
|
||||
|
||||
@@ -197,6 +197,7 @@ class Image extends \Espo\Core\EntryPoints\Base
|
||||
break;
|
||||
}
|
||||
|
||||
$targetImage = imagerotate($targetImage, array_values([0, 0, 0, 180, 0, 0, -90, 0, 90])[@exif_read_data($filePath)['Orientation'] ?: 0], 0);
|
||||
|
||||
return $targetImage;
|
||||
}
|
||||
|
||||
@@ -41,27 +41,49 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base
|
||||
if (
|
||||
$this->getConfig()->get('assignmentEmailNotifications')
|
||||
&&
|
||||
$entity->has('assignedUserId')
|
||||
(
|
||||
$entity->has('assignedUserId')
|
||||
||
|
||||
$entity->hasLinkMultipleField('assignedUsers') && $entity->has('assignedUsersIds')
|
||||
)
|
||||
&&
|
||||
in_array($entity->getEntityType(), $this->getConfig()->get('assignmentEmailNotificationsEntityList', []))
|
||||
) {
|
||||
if ($entity->has('assignedUsersIds')) {
|
||||
$userIdList = $entity->getLinkMultipleIdList('assignedUsers');
|
||||
$fetchedAssignedUserIdList = $entity->getFetched('assignedUsersIds');
|
||||
if (!is_array($fetchedAssignedUserIdList)) {
|
||||
$fetchedAssignedUserIdList = [];
|
||||
}
|
||||
|
||||
$userId = $entity->get('assignedUserId');
|
||||
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isAttributeChanged('assignedUserId')) {
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
$job->set(array(
|
||||
'serviceName' => 'EmailNotification',
|
||||
'methodName' => 'notifyAboutAssignmentJob',
|
||||
'data' => json_encode(array(
|
||||
'userId' => $userId,
|
||||
'assignerUserId' => $this->getUser()->id,
|
||||
'entityId' => $entity->id,
|
||||
'entityType' => $entity->getEntityType()
|
||||
)),
|
||||
'executeTime' => date('Y-m-d H:i:s'),
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($job);
|
||||
foreach ($userIdList as $userId) {
|
||||
if (in_array($userId, $fetchedAssignedUserIdList)) continue;
|
||||
if ($this->getUser()->id === $userId) continue;
|
||||
$this->createJob($entity, $userId);
|
||||
}
|
||||
} else {
|
||||
$userId = $entity->get('assignedUserId');
|
||||
if (!empty($userId) && $userId != $this->getUser()->id && $entity->isAttributeChanged('assignedUserId')) {
|
||||
$this->createJob($entity, $userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createJob(Entity $entity, $userId)
|
||||
{
|
||||
$job = $this->getEntityManager()->getEntity('Job');
|
||||
$job->set(array(
|
||||
'serviceName' => 'EmailNotification',
|
||||
'methodName' => 'notifyAboutAssignmentJob',
|
||||
'data' => json_encode(array(
|
||||
'userId' => $userId,
|
||||
'assignerUserId' => $this->getUser()->id,
|
||||
'entityId' => $entity->id,
|
||||
'entityType' => $entity->getEntityType()
|
||||
)),
|
||||
'executeTime' => date('Y-m-d H:i:s'),
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,9 @@ class NextNumber extends \Espo\Core\Hooks\Base
|
||||
'entityType' => $entity->getEntityType()
|
||||
))->findOne();
|
||||
if (!$nextNumber) {
|
||||
$this->getEntityManager()->getPdo()->query('UNLOCK TABLES');
|
||||
continue;
|
||||
$nextNumber = $this->getEntityManager()->getEntity('NextNumber');
|
||||
$nextNumber->set('entityType', $entity->getEntityType());
|
||||
$nextNumber->set('fieldName', $fieldName);
|
||||
}
|
||||
$entity->set($fieldName, $this->composeNumberAttribute($nextNumber));
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class Notifications extends \Espo\Core\Hooks\Base
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
if (!$this->checkHasStream($entityType)) {
|
||||
if (!$this->checkHasStream($entityType) || $entity->hasLinkMultipleField('assignedUsers')) {
|
||||
if (in_array($entityType, $this->getConfig()->get('assignmentNotificationsEntityList', []))) {
|
||||
$notificator = $this->getNotificator($entityType);
|
||||
$notificator->process($entity);
|
||||
|
||||
@@ -183,12 +183,23 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
if ($this->checkHasStream($entity)) {
|
||||
|
||||
$hasAssignedUsersField = false;
|
||||
if ($entity->hasLinkMultipleField('assignedUsers')) {
|
||||
$hasAssignedUsersField = true;
|
||||
}
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$userIdList = [];
|
||||
|
||||
$assignedUserId = $entity->get('assignedUserId');
|
||||
$createdById = $entity->get('createdById');
|
||||
|
||||
$assignedUserIdList = [];
|
||||
if ($hasAssignedUsersField) {
|
||||
$assignedUserIdList = $entity->getLinkMultipleIdList('assignedUsers');
|
||||
}
|
||||
|
||||
if (
|
||||
!$this->getUser()->isSystem()
|
||||
&&
|
||||
@@ -210,6 +221,15 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
) {
|
||||
$userIdList[] = $createdById;
|
||||
}
|
||||
|
||||
if ($hasAssignedUsersField) {
|
||||
foreach ($assignedUserIdList as $userId) {
|
||||
if (!empty($userId) && !in_array($userId, $userIdList)) {
|
||||
$userIdList[] = $userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($assignedUserId) && !in_array($assignedUserId, $userIdList)) {
|
||||
$userIdList[] = $assignedUserId;
|
||||
}
|
||||
@@ -273,6 +293,27 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
$this->getStreamService()->noteStatus($entity, $field);
|
||||
}
|
||||
}
|
||||
|
||||
$assignedUserIdList = [];
|
||||
if ($hasAssignedUsersField) {
|
||||
$assignedUserIdList = $entity->getLinkMultipleIdList('assignedUsers');
|
||||
}
|
||||
|
||||
if ($hasAssignedUsersField) {
|
||||
$fetchedAssignedUserIdList = $entity->getFetched('assignedUsersIds');
|
||||
if (!is_array($fetchedAssignedUserIdList)) {
|
||||
$fetchedAssignedUserIdList = [];
|
||||
}
|
||||
foreach ($assignedUserIdList as $userId) {
|
||||
if (in_array($userId, $fetchedAssignedUserIdList)) {
|
||||
continue;
|
||||
}
|
||||
$this->getStreamService()->followEntity($entity, $userId);
|
||||
if ($this->getUser()->id === $userId) {
|
||||
$entity->set('isFollowed', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$methodName = 'isChangedWithAclAffect';
|
||||
|
||||
@@ -39,6 +39,8 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
|
||||
protected $cleanupAuthTokenPeriod = '1 month';
|
||||
|
||||
protected $cleanupAuthLogPeriod = '2 months';
|
||||
|
||||
protected $cleanupNotificationsPeriod = '2 months';
|
||||
|
||||
protected $cleanupRemovedNotesPeriod = '2 months';
|
||||
@@ -61,6 +63,7 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$this->cleanupNotifications();
|
||||
$this->cleanupActionHistory();
|
||||
$this->cleanupAuthToken();
|
||||
$this->cleanupAuthLog();
|
||||
$this->cleanupUpgradeBackups();
|
||||
$this->cleanupUniqueIds();
|
||||
}
|
||||
@@ -155,6 +158,20 @@ class Cleanup extends \Espo\Core\Jobs\Base
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
protected function cleanupAuthLog()
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$period = '-' . $this->getConfig()->get('cleanupAuthLogPeriod', $this->cleanupAuthLogPeriod);
|
||||
$datetime = new \DateTime();
|
||||
$datetime->modify($period);
|
||||
|
||||
$query = "DELETE FROM `auth_log_record` WHERE DATE(created_at) < " . $pdo->quote($datetime->format('Y-m-d')) . "";
|
||||
|
||||
$sth = $pdo->prepare($query);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
protected function getCleanupJobFromDate()
|
||||
{
|
||||
$period = '-' . $this->getConfig()->get('cleanupJobPeriod', $this->cleanupJobPeriod);
|
||||
|
||||
@@ -31,29 +31,29 @@ namespace Espo\Modules\Crm\Business\Event;
|
||||
|
||||
class Ics
|
||||
{
|
||||
private $_d_end;
|
||||
private $dEnd;
|
||||
|
||||
private $_d_start;
|
||||
private $dStart;
|
||||
|
||||
private $_s_address;
|
||||
private $sAddress;
|
||||
|
||||
private $_s_description;
|
||||
private $sDescription;
|
||||
|
||||
private $_s_html;
|
||||
private $sHtml;
|
||||
|
||||
private $_s_who;
|
||||
private $sWho;
|
||||
|
||||
private $_s_email;
|
||||
private $sEmail;
|
||||
|
||||
private $_s_uri;
|
||||
private $sUri;
|
||||
|
||||
private $_s_uid;
|
||||
private $sUid;
|
||||
|
||||
private $_s_summary;
|
||||
private $sSummary;
|
||||
|
||||
private $_s_output;
|
||||
private $sOutput;
|
||||
|
||||
private $_s_prodid;
|
||||
private $sProdid;
|
||||
|
||||
public function __construct($prodid, array $attributes = array())
|
||||
{
|
||||
@@ -61,7 +61,7 @@ class Ics
|
||||
throw new \Exception('PRODID is required');
|
||||
}
|
||||
|
||||
$this->_s_prodid = $prodid;
|
||||
$this->sProdid = $prodid;
|
||||
|
||||
foreach ($attributes as $key => $value) {
|
||||
$this->$key = $value;
|
||||
@@ -72,43 +72,43 @@ class Ics
|
||||
{
|
||||
switch ($name) {
|
||||
case 'startDate':
|
||||
$this->_d_start = $value;
|
||||
$this->dStart = $value;
|
||||
break;
|
||||
|
||||
case 'endDate':
|
||||
$this->_d_end = $value;
|
||||
$this->dEnd = $value;
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
$this->_s_address = $value;
|
||||
$this->sAddress = $value;
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
$this->_s_summary = $value;
|
||||
$this->sSummary = $value;
|
||||
break;
|
||||
|
||||
case 'who':
|
||||
$this->_s_who = $value;
|
||||
$this->sWho = $value;
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
$this->_s_email = $value;
|
||||
$this->sEmail = $value;
|
||||
break;
|
||||
|
||||
case 'uri':
|
||||
$this->_s_uri = $value;
|
||||
$this->sUri = $value;
|
||||
break;
|
||||
|
||||
case 'uid':
|
||||
$this->_s_uid = $value;
|
||||
$this->sUid = $value;
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$this->_s_description = $value;
|
||||
$this->sDescription = $value;
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
$this->_s_html = $value;
|
||||
$this->sHtml = $value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -119,82 +119,86 @@ class Ics
|
||||
switch ($name)
|
||||
{
|
||||
case 'startDate':
|
||||
return $this->_d_start;
|
||||
return $this->dStart;
|
||||
break;
|
||||
|
||||
case 'endDate':
|
||||
return $this->_d_end;
|
||||
return $this->dEnd;
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
return $this->_s_address;
|
||||
return $this->sAddress;
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
return $this->_s_summary;
|
||||
return $this->sSummary;
|
||||
break;
|
||||
|
||||
case 'uri':
|
||||
return $this->_s_uri;
|
||||
return $this->sUri;
|
||||
break;
|
||||
|
||||
case 'who':
|
||||
return $this->_s_who;
|
||||
return $this->sWho;
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
return $this->_s_email;
|
||||
return $this->sEmail;
|
||||
break;
|
||||
|
||||
case 'uid':
|
||||
return $this->_s_uid;
|
||||
return $this->sUid;
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
return $this->_s_description;
|
||||
return $this->sDescription;
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
return $this->_s_html;
|
||||
return $this->sHtml;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
($this->_s_output) ? $this->_s_output : $this->_generate();
|
||||
($this->sOutput) ? $this->sOutput : $this->generate();
|
||||
|
||||
return $this->_s_output;
|
||||
return $this->sOutput;
|
||||
}
|
||||
|
||||
private function _generate()
|
||||
private function generate()
|
||||
{
|
||||
$this->_s_output = "BEGIN:VCALENDAR\n".
|
||||
$this->sOutput = "BEGIN:VCALENDAR\n".
|
||||
"VERSION:2.0\n".
|
||||
"PRODID:-".$this->_s_prodid."\n".
|
||||
"PRODID:-".$this->sProdid."\n".
|
||||
"METHOD:REQUEST\n".
|
||||
"BEGIN:VEVENT\n".
|
||||
"DTSTART:".$this->_dateToCal($this->startDate)."\n".
|
||||
"DTEND:".$this->_dateToCal($this->endDate)."\n".
|
||||
"SUMMARY:New ".$this->_escapeString($this->summary)."\n".
|
||||
"LOCATION:".$this->_escapeString($this->address)."\n".
|
||||
"ORGANIZER;CN=".$this->_escapeString($this->who).":MAILTO:" . $this->_escapeString($this->email)."\n".
|
||||
"DESCRIPTION:".$this->_escapeString($this->description)."\n".
|
||||
"DTSTART:".$this->dateToCal($this->startDate)."\n".
|
||||
"DTEND:".$this->dateToCal($this->endDate)."\n".
|
||||
"SUMMARY:New ".$this->escapeString($this->summary)."\n".
|
||||
"LOCATION:".$this->escapeString($this->address)."\n".
|
||||
"ORGANIZER;CN=".$this->escapeString($this->who).":MAILTO:" . $this->escapeString($this->email)."\n".
|
||||
"DESCRIPTION:".$this->escapeString($this->formatMultiline($this->description))."\n".
|
||||
"UID:".$this->uid."\n".
|
||||
"SEQUENCE:0\n".
|
||||
"DTSTAMP:".$this->_dateToCal(time())."\n".
|
||||
"DTSTAMP:".$this->dateToCal(time())."\n".
|
||||
"END:VEVENT\n".
|
||||
"END:VCALENDAR";
|
||||
}
|
||||
|
||||
private function _dateToCal($timestamp)
|
||||
private function dateToCal($timestamp)
|
||||
{
|
||||
return date('Ymd\THis\Z', ($timestamp) ? $timestamp : time());
|
||||
}
|
||||
|
||||
private function _escapeString($string)
|
||||
private function escapeString($string)
|
||||
{
|
||||
return preg_replace('/([\,;])/','\\\$1', ($string) ? $string : '');
|
||||
}
|
||||
}
|
||||
|
||||
private function formatMultiline($string)
|
||||
{
|
||||
return str_replace(["\r\n", "\n"], "\\n", $string);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +65,23 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
|
||||
$userId = $request->get('userId');
|
||||
$userIdList = $request->get('userIdList');
|
||||
$teamIdList = $request->get('teamIdList');
|
||||
|
||||
if ($teamIdList) {
|
||||
$teamIdList = explode(',', $teamIdList);
|
||||
return $userResultList = $service->getEventsForTeams($teamIdList, $from, $to, $scopeList);
|
||||
}
|
||||
|
||||
if ($userIdList) {
|
||||
$userIdList = explode(',', $userIdList);
|
||||
|
||||
$resultList = [];
|
||||
foreach ($userIdList as $userId) {
|
||||
$userResultList = $service->getEvents($userId, $from, $to, $scopeList);
|
||||
try {
|
||||
$userResultList = $service->getEvents($userId, $from, $to, $scopeList);
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
foreach ($userResultList as $item) {
|
||||
$item['userId'] = $userId;
|
||||
$resultList[] = $item;
|
||||
@@ -101,6 +111,8 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
|
||||
$entityTypeList = $request->get('entityTypeList');
|
||||
|
||||
$futureDays = intval($request->get('futureDays'));
|
||||
|
||||
if (empty($maxSize)) {
|
||||
$maxSize = $this->maxSizeLimit;
|
||||
}
|
||||
@@ -111,7 +123,7 @@ class Activities extends \Espo\Core\Controllers\Base
|
||||
return $service->getUpcomingActivities($userId, array(
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize
|
||||
), $entityTypeList);
|
||||
), $entityTypeList, $futureDays);
|
||||
}
|
||||
|
||||
public function actionPopupNotifications()
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Espo\Modules\Crm\Controllers;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Opportunity extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
@@ -89,4 +90,26 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
|
||||
return $this->getService('Opportunity')->reportSalesPipeline($dateFilter, $dateFrom, $dateTo);
|
||||
}
|
||||
|
||||
public function postActionMassConvertCurrency($params, $data, $request)
|
||||
{
|
||||
if (empty($data->field)) throw new BadRequest();
|
||||
if (!$this->getAcl()->checkScope($this->name, 'edit')) throw new Forbidden();
|
||||
|
||||
$params = array();
|
||||
if (property_exists($data, 'where') && !empty($data->byWhere)) {
|
||||
$params['where'] = json_decode(json_encode($data->where), true);
|
||||
if (property_exists($data, 'selectData')) {
|
||||
$params['selectData'] = json_decode(json_encode($data->selectData), true);
|
||||
}
|
||||
} else if (property_exists($data, 'ids')) {
|
||||
$params['ids'] = $data->ids;
|
||||
}
|
||||
|
||||
if (empty($data->currencyRates)) throw new BadRequest();
|
||||
if (empty($data->targetCurrency)) throw new BadRequest();
|
||||
if (empty($data->baseCurrency)) throw new BadRequest();
|
||||
|
||||
return $this->getRecordService()->massConvertCurrency($data->field, $data->targetCurrency, $params, $data->baseCurrency, $data->currencyRates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +60,12 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base
|
||||
|
||||
public function prepare($scheduledJob, $executeTime)
|
||||
{
|
||||
$collection = $this->getEntityManager()->getRepository('EmailAccount')->where(array(
|
||||
$collection = $this->getEntityManager()->getRepository('EmailAccount')->join([['assignedUser', 'assignedUserAdditional']])->where([
|
||||
'status' => 'Active',
|
||||
'useImap' => true
|
||||
))->find();
|
||||
'useImap' => true,
|
||||
'assignedUserAdditional.isActive' => true
|
||||
])->find();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$running = $this->getEntityManager()->getRepository('Job')->where(array(
|
||||
'scheduledJobId' => $scheduledJob->id,
|
||||
|
||||
@@ -42,16 +42,35 @@ class Meeting extends \Espo\Core\Repositories\Event
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$parentType = $entity->get('parentType');
|
||||
if ($parentId && $parentType) {
|
||||
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
|
||||
|
||||
if ($entity->isAttributeChanged('parentId') || $entity->isAttributeChanged('parentType')) {
|
||||
$parent = null;
|
||||
if ($parentId && $parentType) {
|
||||
if ($this->getEntityManager()->hasRepository($parentType)) {
|
||||
$columnList = ['id', 'name'];
|
||||
if ($this->getEntityManager()->getMetadata()->get($parentType, ['fields', 'accountId'])) {
|
||||
$columnList[] = 'accountId';
|
||||
}
|
||||
if ($parentType === 'Lead') {
|
||||
$columnList[] = 'status';
|
||||
$columnList[] = 'createdAccountId';
|
||||
$columnList[] = 'createdAccountName';
|
||||
}
|
||||
$parent = $this->getEntityManager()->getRepository($parentType)->select($columnList)->get($parentId);
|
||||
}
|
||||
}
|
||||
$accountId = null;
|
||||
$accountName = null;
|
||||
|
||||
if ($parent) {
|
||||
$accountId = null;
|
||||
if ($parent->getEntityType() == 'Account') {
|
||||
$accountId = $parent->id;
|
||||
$accountName = $parent->get('name');
|
||||
} else if ($parent->getEntityType() == 'Lead') {
|
||||
if ($parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$accountId = $parent->get('createdAccountId');
|
||||
$accountName = $parent->get('createdAccountName');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +79,18 @@ class Meeting extends \Espo\Core\Repositories\Event
|
||||
}
|
||||
if ($accountId) {
|
||||
$entity->set('accountId', $accountId);
|
||||
$entity->set('accountName', $accountName);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$entity->get('accountId')
|
||||
&&
|
||||
!$entity->get('accountName')
|
||||
) {
|
||||
$account = $this->getEntityManager()->getRepository('Account')->select(['id', 'name'])->get($entity->get('accountId'));
|
||||
if ($account) {
|
||||
$entity->set('accountName', $account->get('name'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,28 +106,58 @@ class Task extends \Espo\Core\Repositories\Event
|
||||
if (!$entity->isNew() && $entity->isAttributeChanged('parentId')) {
|
||||
$entity->set('accountId', null);
|
||||
$entity->set('contactId', null);
|
||||
$entity->set('accountName', null);
|
||||
$entity->set('contactName', null);
|
||||
}
|
||||
|
||||
$parentId = $entity->get('parentId');
|
||||
$parentType = $entity->get('parentType');
|
||||
if ($parentId && $parentType) {
|
||||
$parent = $this->getEntityManager()->getEntity($parentType, $parentId);
|
||||
|
||||
if ($entity->isAttributeChanged('parentId') || $entity->isAttributeChanged('parentType')) {
|
||||
$parent = null;
|
||||
if ($parentId && $parentType) {
|
||||
if ($this->getEntityManager()->hasRepository($parentType)) {
|
||||
$columnList = ['id', 'name'];
|
||||
if ($this->getEntityManager()->getMetadata()->get($parentType, ['fields', 'accountId'])) {
|
||||
$columnList[] = 'accountId';
|
||||
}
|
||||
if ($this->getEntityManager()->getMetadata()->get($parentType, ['fields', 'contactId'])) {
|
||||
$columnList[] = 'contactId';
|
||||
}
|
||||
if ($parentType === 'Lead') {
|
||||
$columnList[] = 'status';
|
||||
$columnList[] = 'createdAccountId';
|
||||
$columnList[] = 'createdAccountName';
|
||||
$columnList[] = 'createdContactId';
|
||||
$columnList[] = 'createdContactName';
|
||||
}
|
||||
$parent = $this->getEntityManager()->getRepository($parentType)->select($columnList)->get($parentId);
|
||||
}
|
||||
}
|
||||
|
||||
$accountId = null;
|
||||
$contactId = null;
|
||||
$accountName = null;
|
||||
$contactName = null;
|
||||
|
||||
if ($parent) {
|
||||
$accountId = null;
|
||||
$contactId = null;
|
||||
if ($parent->getEntityType() == 'Account') {
|
||||
$accountId = $parent->id;
|
||||
$accountName = $parent->get('name');
|
||||
} else if ($parent->getEntityType() == 'Lead') {
|
||||
if ($parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$accountId = $parent->get('createdAccountId');
|
||||
$accountName = $parent->get('createdAccountName');
|
||||
}
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contactId = $parent->get('createdContactId');
|
||||
$contactName = $parent->get('createdContactName');
|
||||
}
|
||||
}
|
||||
} else if ($parent->getEntityType() == 'Contact') {
|
||||
$contactId = $parent->id;
|
||||
$contactName = $parent->get('name');
|
||||
}
|
||||
|
||||
if (!$accountId && $parent->get('accountId') && $parent->getRelationParam('account', 'entity') == 'Account') {
|
||||
@@ -136,16 +166,38 @@ class Task extends \Espo\Core\Repositories\Event
|
||||
if (!$contactId && $parent->get('contactId') && $parent->getRelationParam('contact', 'entity') == 'Contact') {
|
||||
$contactId = $parent->get('contactId');
|
||||
}
|
||||
}
|
||||
|
||||
if ($accountId) {
|
||||
$entity->set('accountId', $accountId);
|
||||
$entity->set('accountId', $accountId);
|
||||
$entity->set('accountName', $accountName);
|
||||
|
||||
$entity->set('contactId', $contactId);
|
||||
$entity->set('contactName', $contactName);
|
||||
|
||||
if (
|
||||
$entity->get('accountId')
|
||||
&&
|
||||
!$entity->get('accountName')
|
||||
) {
|
||||
$account = $this->getEntityManager()->getRepository('Account')->select(['id', 'name'])->get($entity->get('accountId'));
|
||||
if ($account) {
|
||||
$entity->set('accountName', $account->get('name'));
|
||||
}
|
||||
if ($contactId) {
|
||||
$entity->set('contactId', $contactId);
|
||||
}
|
||||
|
||||
if (
|
||||
$entity->get('contactId')
|
||||
&&
|
||||
!$entity->get('contactName')
|
||||
) {
|
||||
$contact = $this->getEntityManager()->getRepository('Contact')->select(['id', 'name'])->get($entity->get('contactId'));
|
||||
if ($contact) {
|
||||
$entity->set('contactName', $contact->get('name'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
parent::beforeSave($entity, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"layouts": {
|
||||
"detailConvert": "Convert Lead",
|
||||
"listForAccount": "List (for Account)"
|
||||
"listForAccount": "List (for Account)",
|
||||
"listForContact": "List (for Contact)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"time": "time",
|
||||
"User List": "User List",
|
||||
"Manage Users": "Manage Users",
|
||||
"View Calendar": "View Calendar"
|
||||
"View Calendar": "View Calendar",
|
||||
"Create Shared View": "Create Shared View"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"futureDays": "Next X Days"
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,6 @@
|
||||
"Real Estate": "Bienes Raices",
|
||||
"Service": "Servicio",
|
||||
"Sports": "Deportes",
|
||||
"Software": "Software",
|
||||
"Technology": "Tecnología",
|
||||
"Telecommunications": "Telecomunicaciones",
|
||||
"Television": "Televisión",
|
||||
@@ -82,7 +81,6 @@
|
||||
"Mass Media": "Medios masivos",
|
||||
"Mining": "Minería",
|
||||
"Music": "Música",
|
||||
"Marketing": "Marketing",
|
||||
"Petroleum": "Petróleo",
|
||||
"Retail": "Menudeo",
|
||||
"Shipping": "Entrega",
|
||||
|
||||
@@ -35,9 +35,7 @@
|
||||
"options": {
|
||||
"type": {
|
||||
"Email": "Correo electrónico",
|
||||
"Web": "Web",
|
||||
"Television": "Televisión",
|
||||
"Radio": "Radio",
|
||||
"Newsletter": "Periódico",
|
||||
"Mail": "Correo"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"fields": {
|
||||
"url": "URL",
|
||||
"urlToUse": "Código para insertar en lugar de la URL",
|
||||
"campaign": "Campaña"
|
||||
},
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"priority": "Prioridad",
|
||||
"type": "Tipo",
|
||||
"description": "Descripción",
|
||||
"inboundEmail": "Cuenta de Correo",
|
||||
"lead": "Referencia"
|
||||
"lead": "Referencia",
|
||||
"attachments": "Adjuntos",
|
||||
"inboundEmail": "Cuenta de Correo de Grupo"
|
||||
},
|
||||
"links": {
|
||||
"inboundEmail": "Cuenta de Correo",
|
||||
"account": "Cuenta",
|
||||
"contact": "Contacto (Primario)",
|
||||
"Contacts": "Contactos",
|
||||
@@ -22,7 +22,9 @@
|
||||
"tasks": "Tareas",
|
||||
"emails": "Correos",
|
||||
"articles": "Artículos de la Base de Conocimientos",
|
||||
"lead": "Referencia"
|
||||
"lead": "Referencia",
|
||||
"attachments": "Adjuntos",
|
||||
"inboundEmail": "Cuenta de Correo de Grupo"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -35,7 +37,6 @@
|
||||
},
|
||||
"priority": {
|
||||
"Low": "Baja",
|
||||
"Normal": "Normal",
|
||||
"High": "Alta",
|
||||
"Urgent": "Urgente"
|
||||
},
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"casesPrimary": "Casos (Primario)",
|
||||
"portalUser": "Usuario del Portal",
|
||||
"originalLead": "Referencia Original",
|
||||
"documents": "Documentos"
|
||||
"documents": "Documentos",
|
||||
"tasksPrimary": "Tareas (extendidas)"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Crear Contacto"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -32,7 +32,6 @@
|
||||
"": "Ninguno",
|
||||
"Contract": "Contrato",
|
||||
"NDA": "AdC",
|
||||
"EULA": "EULA",
|
||||
"License Agreement": "Contrato de Licencia"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"inboundEmail": "Cuenta de correo",
|
||||
"targetLists": "Lista de Objetivos",
|
||||
"excludingTargetLists": "Listas de Objetivos Excluídas",
|
||||
"optOutEntirely": "Confirmación Completada"
|
||||
"optOutEntirely": "Confirmación Completada",
|
||||
"smtpAccount": "Cuenta SMTP"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Listas de Objetivos",
|
||||
@@ -35,7 +36,10 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create MassEmail": "Crear correo masivo",
|
||||
"Send Test": "Enviar prueba"
|
||||
"Send Test": "Enviar prueba",
|
||||
"System SMTP": "Sistema SMTP",
|
||||
"system": "sistema",
|
||||
"group": "grupo"
|
||||
},
|
||||
"messages": {
|
||||
"selectAtLeastOneTarget": "Seleccione al menos un objetivo.",
|
||||
@@ -44,10 +48,10 @@
|
||||
"tooltips": {
|
||||
"optOutEntirely": "Los correos de destinatarios que cancelaron su suscripción serán marcados como rechazados y ya no recibirán correos masivos.",
|
||||
"targetLists": "Los objetivos que deben recibir los mensajes.",
|
||||
"excludingTargetLists": "Los objetivos que no deben recibir mensajes."
|
||||
"excludingTargetLists": "Los objetivos que no deben recibir mensajes.",
|
||||
"storeSentEmails": "Los correos se guardarán en el CRM."
|
||||
},
|
||||
"presetFilters": {
|
||||
"actual": "Actual",
|
||||
"complete": "Completo"
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,6 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospección",
|
||||
"Qualification": "Calificación",
|
||||
"Proposal": "Cotización con Propuesta",
|
||||
"Negotiation": "Negociación",
|
||||
"Needs Analysis": "Análisis de Necesidades",
|
||||
"Value Proposition": "Propuesta de Valor",
|
||||
"Id. Decision Makers": "Id. Tomadores de Decisiones",
|
||||
@@ -36,7 +34,9 @@
|
||||
"Proposal/Price Quote": "Cotización con Propuesta/Precio",
|
||||
"Negotiation/Review": "Negociación/Revisión",
|
||||
"Closed Won": "Cerrado Ganado",
|
||||
"Closed Lost": "Cerrado Perdido"
|
||||
"Closed Lost": "Cerrado Perdido",
|
||||
"Proposal": "Cotización con Propuesta",
|
||||
"Negotiation": "Negociación"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
"options": {
|
||||
"type": {
|
||||
"Email": "Correo electrónico",
|
||||
"Web": "Web",
|
||||
"Television": "Televisión",
|
||||
"Radio": "Radio",
|
||||
"Newsletter": "Newsletter"
|
||||
"Television": "Televisión"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -13,10 +13,13 @@
|
||||
"account": "Cuenta",
|
||||
"dateCompleted": "Fecha de completado",
|
||||
"attachments": "Adjuntos",
|
||||
"reminders": "Recordatorios"
|
||||
"reminders": "Recordatorios",
|
||||
"contact": "Contacto"
|
||||
},
|
||||
"links": {
|
||||
"attachments": "Adjuntos"
|
||||
"attachments": "Adjuntos",
|
||||
"account": "Cuenta",
|
||||
"contact": "Contacto"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
@@ -28,7 +31,6 @@
|
||||
},
|
||||
"priority": {
|
||||
"Low": "Baja",
|
||||
"Normal": "Normal",
|
||||
"High": "Alta",
|
||||
"Urgent": "Urgente"
|
||||
}
|
||||
|
||||
102
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Account.json
Normal file
102
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Account.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Naziv",
|
||||
"website": "Site",
|
||||
"phoneNumber": "Telefon",
|
||||
"billingAddress": "Adresa za naplatu",
|
||||
"shippingAddress": "Adresa za dostavu",
|
||||
"description": "Opis",
|
||||
"sicCode": "Sic kod",
|
||||
"industry": "Industrija",
|
||||
"type": "Vrsta",
|
||||
"contactRole": "Naslov",
|
||||
"campaign": "Kampanja",
|
||||
"targetLists": "Liste meta",
|
||||
"targetList": "Lista meta",
|
||||
"originalLead": "Originalni izvor",
|
||||
"contactIsInactive": "Neaktivno"
|
||||
},
|
||||
"links": {
|
||||
"contacts": "Kontakti",
|
||||
"opportunities": "Prilike",
|
||||
"cases": "Predmeti",
|
||||
"documents": "Dokumenti",
|
||||
"meetingsPrimary": "Sastanci (prošireno)",
|
||||
"callsPrimary": "Pozivi (prošireno)",
|
||||
"tasksPrimary": "Zadaci (prošireno)",
|
||||
"emailsPrimary": "E-pošta (prošireno)",
|
||||
"targetLists": "Liste meta",
|
||||
"campaignLogRecords": "Dnevnik kampanje",
|
||||
"campaign": "Kampanja",
|
||||
"portalUsers": "Korisnici portala",
|
||||
"originalLead": "Originalni izvor"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Customer": "Klijent",
|
||||
"Investor": "Investitor",
|
||||
"Reseller": "Distributer"
|
||||
},
|
||||
"industry": {
|
||||
"Agriculture": "Poljoprivreda",
|
||||
"Advertising": "Oglašavanje",
|
||||
"Apparel & Accessories": "Odjeća i pribor",
|
||||
"Automotive": "Automobili",
|
||||
"Banking": "Bankarstvo",
|
||||
"Biotechnology": "Biotehnologija",
|
||||
"Building Materials & Equipment": "Građevinski materijal i oprema",
|
||||
"Chemical": "Kemijska",
|
||||
"Computer": "Računala",
|
||||
"Education": "Obrazovanje",
|
||||
"Electronics": "Elektronika",
|
||||
"Energy": "Energija",
|
||||
"Entertainment & Leisure": "Zabava & opuštanje",
|
||||
"Finance": "Financije",
|
||||
"Food & Beverage": "Hrana i piće",
|
||||
"Grocery": "Mješovita roba",
|
||||
"Healthcare": "Zdravstvena zaštita",
|
||||
"Insurance": "Osiguranje",
|
||||
"Legal": "Pravni poslovi",
|
||||
"Manufacturing": "Proizvodnja",
|
||||
"Publishing": "Izdavaštvo",
|
||||
"Real Estate": "Nekretnine",
|
||||
"Service": "Usluge",
|
||||
"Sports": "Sportovi",
|
||||
"Software": "Softver",
|
||||
"Technology": "Tehnologija",
|
||||
"Telecommunications": "Telekomunikacije",
|
||||
"Television": "Televizija",
|
||||
"Transportation": "Transport",
|
||||
"Venture Capital": "Preduzetnički kapital",
|
||||
"Aerospace": "Avijacija",
|
||||
"Architecture": "Arhitektura",
|
||||
"Construction": "Izgradnja",
|
||||
"Defense": "Obrana",
|
||||
"Creative": "Kreativna ind",
|
||||
"Culture": "Kultura",
|
||||
"Electric Power": "Električna energija",
|
||||
"Hospitality": "Uslužna djelatnost",
|
||||
"Mass Media": "Masovni mediji",
|
||||
"Mining": "Rudarstvo",
|
||||
"Music": "Muzika",
|
||||
"Petroleum": "Naftna",
|
||||
"Retail": "Maloprodaja",
|
||||
"Shipping": "Dostava",
|
||||
"Support": "Podrška",
|
||||
"Testing, Inspection & Certification": "Testiranje, inspekcija i certificiranje",
|
||||
"Wholesale": "Veleprodaja",
|
||||
"Water": "Voda",
|
||||
"Travel": "Putovanje"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Account": "Kreiraj Tvrtku",
|
||||
"Copy Billing": "Kopiranje Naplatne adrese",
|
||||
"Set Primary": "Podesi kao primarno"
|
||||
},
|
||||
"presetFilters": {
|
||||
"customers": "Klijenti",
|
||||
"partners": "Partneri",
|
||||
"recentlyCreated": "Nedavno kreirano"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"layouts": {
|
||||
"detailConvert": "Pretvori izvor",
|
||||
"listForAccount": "Lista (za Tvrtke)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"modes": {
|
||||
"month": "Mjesec",
|
||||
"week": "Tjedan",
|
||||
"agendaWeek": "Tjedan",
|
||||
"day": "Dan",
|
||||
"agendaDay": "Dan",
|
||||
"timeline": "Vremenska linija"
|
||||
},
|
||||
"labels": {
|
||||
"Today": "Danas",
|
||||
"Create": "Kreiraj",
|
||||
"Shared": "Dijeljeno",
|
||||
"Add User": "Dodaj korisnika",
|
||||
"current": "trenutno",
|
||||
"time": "vrijeme",
|
||||
"User List": "Lista korisnika",
|
||||
"Manage Users": "Upravljanje korisnicima",
|
||||
"View Calendar": "Prikaži kalendar"
|
||||
}
|
||||
}
|
||||
49
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Call.json
Normal file
49
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Call.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"parent": "Nadređeni",
|
||||
"dateStart": "Početni datum",
|
||||
"dateEnd": "Završni datum",
|
||||
"direction": "Smjer",
|
||||
"duration": "Trajanje",
|
||||
"description": "Opis",
|
||||
"users": "Korisnici",
|
||||
"contacts": "Kontakti",
|
||||
"leads": "Izvori",
|
||||
"reminders": "Podsjetnici",
|
||||
"account": "Tvrtka",
|
||||
"acceptanceStatus": "Status prihvaćanja"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Planned": "Planiran",
|
||||
"Held": "Održan",
|
||||
"Not Held": "Nije održano"
|
||||
},
|
||||
"direction": {
|
||||
"Outbound": "Odlazna",
|
||||
"Inbound": "Dolazna"
|
||||
},
|
||||
"acceptanceStatus": {
|
||||
"None": "Nema",
|
||||
"Accepted": "Prihvaćeno",
|
||||
"Declined": "Odbijeno",
|
||||
"Tentative": "Tentativni"
|
||||
}
|
||||
},
|
||||
"massActions": {
|
||||
"setHeld": "Postavi kao održano",
|
||||
"setNotHeld": "Postavi kao nije održano"
|
||||
},
|
||||
"labels": {
|
||||
"Create Call": "Postavi poziv",
|
||||
"Set Held": "Postavi kao održano",
|
||||
"Set Not Held": "Postavi kao nije održano",
|
||||
"Send Invitations": "Pošalji Pozivnice"
|
||||
},
|
||||
"presetFilters": {
|
||||
"planned": "Planirano",
|
||||
"held": "Održan",
|
||||
"todays": "Današnji"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"description": "Opis",
|
||||
"type": "Tip",
|
||||
"startDate": "Datum početka",
|
||||
"endDate": "Završni datum",
|
||||
"targetLists": "Liste meta",
|
||||
"excludingTargetLists": "Isključujući liste meta",
|
||||
"sentCount": "Poslano",
|
||||
"openedCount": "Otvoren",
|
||||
"clickedCount": "Kliknuto",
|
||||
"optedOutCount": "Ne želi",
|
||||
"leadCreatedCount": "Kreiran izvor",
|
||||
"revenue": "Prihod",
|
||||
"revenueConverted": "Prihod (konvertiran)",
|
||||
"budget": "Budžet",
|
||||
"budgetConverted": "Budžet (konvertiran)"
|
||||
},
|
||||
"links": {
|
||||
"targetLists": "Liste meta",
|
||||
"excludingTargetLists": "Isključujući liste meta",
|
||||
"accounts": "Tvrtke",
|
||||
"contacts": "Kontakti",
|
||||
"leads": "Izvori",
|
||||
"opportunities": "Prilike",
|
||||
"campaignLogRecords": "Dnevnik",
|
||||
"massEmails": "Masovna e-pošta",
|
||||
"trackingUrls": "Praćenje URL-ova"
|
||||
},
|
||||
"options": {
|
||||
"type": {
|
||||
"Email": "E-pošta",
|
||||
"Television": "Televizija",
|
||||
"Mail": "Pošta"
|
||||
},
|
||||
"status": {
|
||||
"Planning": "Planiranje",
|
||||
"Active": "Aktivna",
|
||||
"Inactive": "Neaktivna",
|
||||
"Complete": "Gotova"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Campaign": "Napravite kampanju",
|
||||
"Target Lists": "Liste meta",
|
||||
"Statistics": "Statistika",
|
||||
"hard": "tvrd",
|
||||
"soft": "mek",
|
||||
"Unsubscribe": "Odjavi se",
|
||||
"Mass Emails": "Masovna e-pošta",
|
||||
"Email Templates": "Šablone e-pošte",
|
||||
"Unsubscribe again": "Ponovo se odjavite",
|
||||
"Subscribe again": "Ponovo se prijavite",
|
||||
"Create Target List": "Napravite listu meta"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Aktivne"
|
||||
},
|
||||
"messages": {
|
||||
"unsubscribed": "Odjavljeni ste sa naše liste.",
|
||||
"subscribedAgain": "Ponovo ste pretplaćeni."
|
||||
},
|
||||
"tooltips": {
|
||||
"targetLists": "Mete koje trebaju primati poruke.",
|
||||
"excludingTargetLists": "Mete koje ne trebaju primati poruke."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"fields": {
|
||||
"action": "Akcija",
|
||||
"actionDate": "Datum",
|
||||
"data": "Podaci",
|
||||
"campaign": "Kampanja",
|
||||
"parent": "Meta",
|
||||
"object": "Objekt",
|
||||
"application": "Aplikacija",
|
||||
"queueItem": "U redu čekanja",
|
||||
"stringData": "String podatak",
|
||||
"stringAdditionalData": "String dodatni podataci",
|
||||
"isTest": "Je Test"
|
||||
},
|
||||
"links": {
|
||||
"queueItem": "U redu čekanja",
|
||||
"parent": "Nadređeni",
|
||||
"object": "Objekt",
|
||||
"campaign": "Kampanja"
|
||||
},
|
||||
"options": {
|
||||
"action": {
|
||||
"Sent": "Poslano",
|
||||
"Opened": "Otvorena",
|
||||
"Opted Out": "Ne želi",
|
||||
"Clicked": "Kliknuto",
|
||||
"Lead Created": "Izvor kreiran"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"All": "Sve"
|
||||
},
|
||||
"presetFilters": {
|
||||
"sent": "Poslano",
|
||||
"opened": "Otvorena",
|
||||
"optedOut": "Ne želi",
|
||||
"clicked": "Kliknuto",
|
||||
"leadCreated": "Izvor Kreiran"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"url": "URL adresa",
|
||||
"urlToUse": "Kod za ubacivanje umjesto URL",
|
||||
"campaign": "Kampanja"
|
||||
},
|
||||
"links": {
|
||||
"campaign": "Kampanja"
|
||||
},
|
||||
"labels": {
|
||||
"Create CampaignTrackingUrl": "Kreirati URL za praćenje"
|
||||
}
|
||||
}
|
||||
58
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Case.json
Normal file
58
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Case.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"number": "Broj",
|
||||
"account": "Tvrtka",
|
||||
"contact": "Kontakt",
|
||||
"contacts": "Kontakti",
|
||||
"priority": "Prioritet",
|
||||
"type": "Vrsta",
|
||||
"description": "Opis",
|
||||
"lead": "Izvor",
|
||||
"attachments": "Prilozi",
|
||||
"inboundEmail": "Grupni Email račun"
|
||||
},
|
||||
"links": {
|
||||
"account": "Tvrtka",
|
||||
"contact": "Kontakt (osnovni)",
|
||||
"Contacts": "Kontakti",
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci",
|
||||
"emails": "E-poruke",
|
||||
"articles": "Baza znanja",
|
||||
"lead": "Izvor",
|
||||
"attachments": "Prilozi",
|
||||
"inboundEmail": "Grupni Email račun"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"New": "Novi",
|
||||
"Assigned": "Dodijeljen",
|
||||
"Pending": "Neriješen",
|
||||
"Closed": "Zatvoren",
|
||||
"Rejected": "Odbijen",
|
||||
"Duplicate": "Duplikat"
|
||||
},
|
||||
"priority": {
|
||||
"Low": "Nizak",
|
||||
"Normal": "Normalan",
|
||||
"High": "Visok",
|
||||
"Urgent": "Hitan"
|
||||
},
|
||||
"type": {
|
||||
"Question": "Pitanje"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Case": "Napravi prijavu problema",
|
||||
"Close": "Zatvori",
|
||||
"Reject": "Odbijen",
|
||||
"Closed": "Zatvoren",
|
||||
"Rejected": "Odbijen"
|
||||
},
|
||||
"presetFilters": {
|
||||
"open": "Otvoren",
|
||||
"closed": "Zatvoren"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"emailAddress": "E-mail",
|
||||
"title": "Naslov",
|
||||
"accountRole": "Naslov",
|
||||
"account": "Tvrtka",
|
||||
"accounts": "Tvrtke",
|
||||
"phoneNumber": "Telefon",
|
||||
"accountType": "Vrsta Tvrtke",
|
||||
"doNotCall": "Ne zovi",
|
||||
"address": "Adresa",
|
||||
"opportunityRole": "Uloga prilika",
|
||||
"description": "Opis",
|
||||
"campaign": "Kampanja",
|
||||
"targetLists": "Liste meta",
|
||||
"targetList": "Lista meta",
|
||||
"portalUser": "Korisnik portala",
|
||||
"originalLead": "Originalni izvor",
|
||||
"acceptanceStatus": "Status prihvaćanja",
|
||||
"accountIsInactive": "Tvrtka Neaktivna",
|
||||
"acceptanceStatusMeetings": "Status prihvaćanja (Sastanak)",
|
||||
"acceptanceStatusCalls": "Status prihvaćanja (pozivi)"
|
||||
},
|
||||
"links": {
|
||||
"opportunities": "Prilike",
|
||||
"cases": "Predmeti",
|
||||
"targetLists": "Liste meta",
|
||||
"campaignLogRecords": "Dnevnik kampanje",
|
||||
"campaign": "Kampanja",
|
||||
"account": "Tvrtka (osnovno)",
|
||||
"accounts": "Tvrtke",
|
||||
"casesPrimary": "Prijave problema (osnovni)",
|
||||
"portalUser": "Korisnik portala",
|
||||
"originalLead": "Originalni izvor",
|
||||
"documents": "Dokumenti",
|
||||
"tasksPrimary": "Zadaci (prošireno)"
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Napravi kontakt"
|
||||
},
|
||||
"options": {
|
||||
"opportunityRole": {
|
||||
"": "Nema",
|
||||
"Decision Maker": "Donositelj odluka",
|
||||
"Influencer": "Utjecajna osoba"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"portalUsers": "Korisnici portala",
|
||||
"notPortalUsers": "Nisu korisnici portala",
|
||||
"accountActive": "Aktivan"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create Document": "Napravi dokument",
|
||||
"Details": "Detaljnije"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"file": "Datoteka",
|
||||
"type": "Vrsta",
|
||||
"publishDate": "Datum objave",
|
||||
"expirationDate": "Rok upotrebe",
|
||||
"description": "Opis",
|
||||
"accounts": "Tvrtke",
|
||||
"folder": "Mapa"
|
||||
},
|
||||
"links": {
|
||||
"accounts": "Tvrtke",
|
||||
"opportunities": "Prilike",
|
||||
"folder": "Mape",
|
||||
"leads": "Izvori",
|
||||
"contacts": "Kontakti"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Active": "Aktivan",
|
||||
"Draft": "Nacrt",
|
||||
"Expired": "Istekao",
|
||||
"Canceled": "Otkazan"
|
||||
},
|
||||
"type": {
|
||||
"": "Nema",
|
||||
"Contract": "Ugovor",
|
||||
"License Agreement": "Ugovor o licenci"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Aktivan",
|
||||
"draft": "Nacrt"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create DocumentFolder": "Kreiranje mape za dokumente",
|
||||
"Manage Categories": "Upravljanje mapama",
|
||||
"Documents": "Dokumenti"
|
||||
},
|
||||
"links": {
|
||||
"documents": "Dokumenti"
|
||||
}
|
||||
}
|
||||
10
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Email.json
Normal file
10
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Email.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create Lead": "Napraviti izvor",
|
||||
"Create Contact": "Napraviti kontakt",
|
||||
"Create Task": "Napraviti zadatak",
|
||||
"Create Case": "Kreiranje prijave problema",
|
||||
"Add to Contact": "Dodaj u Kontakt",
|
||||
"Add to Lead": "Dodaj u Izvor"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"target": "Meta",
|
||||
"sentAt": "Datum slanja",
|
||||
"attemptCount": "Pokušaji",
|
||||
"emailAddress": "Email adresa",
|
||||
"massEmail": "Masovna e-pošta",
|
||||
"isTest": "Je test"
|
||||
},
|
||||
"links": {
|
||||
"target": "Meta",
|
||||
"massEmail": "Masovna e-pošta"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"Pending": "Čeka",
|
||||
"Sent": "Poslano",
|
||||
"Failed": "Neuspješno",
|
||||
"Sending": "Slanje"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"pending": "Čeka",
|
||||
"sent": "Poslano",
|
||||
"failed": "Neuspješno"
|
||||
}
|
||||
}
|
||||
118
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Global.json
Normal file
118
application/Espo/Modules/Crm/Resources/i18n/hr_HR/Global.json
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"links": {
|
||||
"parent": "Roditelj",
|
||||
"contacts": "Kontakti",
|
||||
"opportunities": "Prilike",
|
||||
"leads": "Izvori",
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci",
|
||||
"emails": "E-poruke",
|
||||
"accounts": "Tvrtke",
|
||||
"cases": "Prijave problema",
|
||||
"documents": "Dokumenti",
|
||||
"account": "Tvrtka",
|
||||
"opportunity": "Prilika",
|
||||
"contact": "Kontakt"
|
||||
},
|
||||
"scopeNames": {
|
||||
"Account": "Tvrtka",
|
||||
"Contact": "Kontakt",
|
||||
"Lead": "Izvor",
|
||||
"Target": "Meta",
|
||||
"Opportunity": "Prilika",
|
||||
"Meeting": "Sastanak",
|
||||
"Calendar": "Kalendar",
|
||||
"Call": "Poziv",
|
||||
"Task": "Zadatak",
|
||||
"Case": "Prijava problema",
|
||||
"Document": "Dokument",
|
||||
"DocumentFolder": "Mapa dokumenta",
|
||||
"Campaign": "Kampanja",
|
||||
"TargetList": "Lista meta",
|
||||
"MassEmail": "Masovna e-pošta",
|
||||
"EmailQueueItem": "Email sa liste čekanja",
|
||||
"CampaignTrackingUrl": "URL za praćenje",
|
||||
"Activities": "Aktivnosti",
|
||||
"KnowledgeBaseArticle": "Baza znanja članak",
|
||||
"KnowledgeBaseCategory": "Baza znanja kategorija",
|
||||
"CampaignLogRecord": "Upis dnevnika kampanje"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Account": "Tvrtke",
|
||||
"Contact": "Kontakti",
|
||||
"Lead": "Izvori",
|
||||
"Target": "Mete",
|
||||
"Opportunity": "Prilike",
|
||||
"Meeting": "Sastanci",
|
||||
"Calendar": "Kalendar",
|
||||
"Call": "Pozivi",
|
||||
"Task": "Zadaci",
|
||||
"Case": "Prijave problema",
|
||||
"Document": "Dokumenti",
|
||||
"DocumentFolder": "Mape Dokumenata",
|
||||
"Campaign": "Kampanje",
|
||||
"TargetList": "Liste meta",
|
||||
"MassEmail": "Masovne e-pošta",
|
||||
"EmailQueueItem": "Email sa liste za čekanje",
|
||||
"CampaignTrackingUrl": "Praćenje URL-ova",
|
||||
"Activities": "Aktivnosti",
|
||||
"KnowledgeBaseArticle": "Baza znanja",
|
||||
"KnowledgeBaseCategory": "Baza znanja Kategorije",
|
||||
"CampaignLogRecord": "Upisi dnevnika kampanje"
|
||||
},
|
||||
"dashlets": {
|
||||
"Leads": "Moji izvori",
|
||||
"Opportunities": "Mojie prilike",
|
||||
"Tasks": "Moji zadaci",
|
||||
"Cases": "Moji prijavljeni problemi",
|
||||
"Calendar": "Kalendar",
|
||||
"Calls": "Moji pozivi",
|
||||
"Meetings": "Moji sastanci",
|
||||
"OpportunitiesByStage": "Prilike po fazama",
|
||||
"OpportunitiesByLeadSource": "Prilike po izvorima",
|
||||
"SalesByMonth": "Prodaja po mjesecima",
|
||||
"SalesPipeline": "Slijed prodaja",
|
||||
"Activities": "Moje aktivnosti"
|
||||
},
|
||||
"labels": {
|
||||
"Create InboundEmail": "Napravite dolazni Email",
|
||||
"Activities": "Aktivnosti",
|
||||
"History": "Povijest",
|
||||
"Attendees": "Uzvanici",
|
||||
"Schedule Meeting": "Zakaži sastanak",
|
||||
"Schedule Call": "Zakaži poziv",
|
||||
"Compose Email": "Sastavi e-poštu",
|
||||
"Log Meeting": "Zabilježi sastanak",
|
||||
"Log Call": "Zabilježi poziv",
|
||||
"Archive Email": "Arhiva E-pošte",
|
||||
"Create Task": "Napravi Zadatak",
|
||||
"Tasks": "Zadaci"
|
||||
},
|
||||
"fields": {
|
||||
"billingAddressCity": "Grad",
|
||||
"addressCity": "Grad",
|
||||
"billingAddressCountry": "Zemlja",
|
||||
"addressCountry": "Zemlja",
|
||||
"billingAddressPostalCode": "Poštanski broj",
|
||||
"addressPostalCode": "Poštanski broj",
|
||||
"billingAddressState": "Zemlja",
|
||||
"addressState": "Zemlja",
|
||||
"billingAddressStreet": "Ulica",
|
||||
"addressStreet": "Ulica",
|
||||
"billingAddressMap": "Mapa",
|
||||
"addressMap": "Mapa",
|
||||
"shippingAddressCity": "Grad (Dostava)",
|
||||
"shippingAddressStreet": "Ulica (Dostava)",
|
||||
"shippingAddressCountry": "Država (Dostava)",
|
||||
"shippingAddressState": "Zemlja (Dostava)",
|
||||
"shippingAddressPostalCode": "Poštanski broj (dostava)",
|
||||
"shippingAddressMap": "Karta (Dostava)"
|
||||
},
|
||||
"options": {
|
||||
"reminderTypes": {
|
||||
"Popup": "Iskačući",
|
||||
"Email": "E-porukom"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create KnowledgeBaseArticle": "Kreiraj članak",
|
||||
"Any": "Bilo koji",
|
||||
"Send in Email": "Pošalji u e-pošti",
|
||||
"Move Up": "Pomakni gore",
|
||||
"Move Down": "Pomakni dole",
|
||||
"Move to Top": "Pomakni na vrh",
|
||||
"Move to Bottom": "Pomakni na dno"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Ime",
|
||||
"type": "Vrsta",
|
||||
"attachments": "Prilozi",
|
||||
"publishDate": "Datum objave",
|
||||
"expirationDate": "Rok isteka",
|
||||
"description": "Opis",
|
||||
"body": "Sadržaj",
|
||||
"categories": "Kategorije",
|
||||
"language": "Jezik",
|
||||
"portals": "Portali"
|
||||
},
|
||||
"links": {
|
||||
"cases": "Prijave problema",
|
||||
"opportunities": "Prilike",
|
||||
"categories": "Kategorije",
|
||||
"portals": "Portali"
|
||||
},
|
||||
"options": {
|
||||
"status": {
|
||||
"In Review": "U pregledu",
|
||||
"Draft": "Nacrt",
|
||||
"Archived": "Arhiviran",
|
||||
"Published": "Objavljen"
|
||||
},
|
||||
"type": {
|
||||
"Article": "Članak"
|
||||
}
|
||||
},
|
||||
"presetFilters": {
|
||||
"published": "Objavljen"
|
||||
},
|
||||
"tooltips": {
|
||||
"portals": "Članak će biti dostupan samo u određenim portalima."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create KnowledgeBaseCategory": "Napravi Kategoriju",
|
||||
"Manage Categories": "Izmjena Kategorije",
|
||||
"Articles": "Članci"
|
||||
},
|
||||
"links": {
|
||||
"articles": "Članci"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user