data privacy
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -10,7 +10,8 @@
|
||||
"type": "url"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
"type": "email",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "personName"
|
||||
"type": "personName",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
@@ -71,18 +72,21 @@
|
||||
"type": "text"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
"type": "email",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
"typeList": ["Mobile", "Office", "Home", "Fax", "Other"],
|
||||
"defaultType": "Mobile"
|
||||
"defaultType": "Mobile",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"doNotCall": {
|
||||
"type": "bool"
|
||||
},
|
||||
"address": {
|
||||
"type": "address"
|
||||
"type": "address",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"addressStreet": {
|
||||
"type": "text",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": {
|
||||
"type": "personName"
|
||||
"type": "personName",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"salutationName": {
|
||||
"type": "enum",
|
||||
@@ -59,7 +60,8 @@
|
||||
"type": "url"
|
||||
},
|
||||
"address": {
|
||||
"type": "address"
|
||||
"type": "address",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"addressStreet": {
|
||||
"type": "text",
|
||||
@@ -83,12 +85,14 @@
|
||||
"trim": true
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "email"
|
||||
"type": "email",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "phone",
|
||||
"typeList": ["Mobile", "Office", "Home", "Fax", "Other"],
|
||||
"defaultType": "Mobile"
|
||||
"defaultType": "Mobile",
|
||||
"isPersonalData": true
|
||||
},
|
||||
"doNotCall": {
|
||||
"type": "bool"
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"stream": true,
|
||||
"importable": true,
|
||||
"notifications": true,
|
||||
"object": true
|
||||
"object": true,
|
||||
"hasPersonalData": true
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"stream": true,
|
||||
"importable": true,
|
||||
"notifications": true,
|
||||
"object": true
|
||||
"object": true,
|
||||
"hasPersonalData": true
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
"importable": true,
|
||||
"notifications": true,
|
||||
"object": true,
|
||||
"statusField": "status"
|
||||
"statusField": "status",
|
||||
"hasPersonalData": true
|
||||
}
|
||||
|
||||
@@ -222,6 +222,7 @@ class MassEmail extends \Espo\Services\Record
|
||||
foreach ($entityList as $target) {
|
||||
$emailAddress = $target->get('emailAddress');
|
||||
if (!$target->get('emailAddress')) continue;
|
||||
if (strpos($emailAddress, 'ERASED:') === 0) continue;
|
||||
$emailAddressRecord = $this->getEntityManager()->getRepository('EmailAddress')->getByAddress($emailAddress);
|
||||
if ($emailAddressRecord) {
|
||||
if ($emailAddressRecord->get('invalid') || $emailAddressRecord->get('optOut')) {
|
||||
|
||||
@@ -41,32 +41,43 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('user');
|
||||
$this->addDependency('acl');
|
||||
$this->addDependency('aclManager');
|
||||
}
|
||||
|
||||
public function getIdListFormAddressList(array $arr = [])
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getIds($arr);
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
public function getIds(array $arr = [])
|
||||
public function getIdListFormAddressList(array $addressList = [])
|
||||
{
|
||||
return $this->getIds($addressList);
|
||||
}
|
||||
|
||||
public function getIds(array $addressList = [])
|
||||
{
|
||||
$ids = array();
|
||||
if (!empty($arr)) {
|
||||
$a = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $arr);
|
||||
$eas = $this->where(array(
|
||||
'lower' => array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $arr)
|
||||
))->find();
|
||||
if (!empty($addressList)) {
|
||||
$lowerAddressList = [];
|
||||
foreach ($addressList as $address) {
|
||||
$lowerAddressList[] = trim(strtolower($address));
|
||||
}
|
||||
|
||||
$eaCollection = $this->where([
|
||||
[
|
||||
'lower' => $lowerAddressList
|
||||
]
|
||||
])->find();
|
||||
|
||||
$ids = array();
|
||||
$exist = array();
|
||||
foreach ($eas as $ea) {
|
||||
foreach ($eaCollection as $ea) {
|
||||
$ids[] = $ea->id;
|
||||
$exist[] = $ea->get('lower');
|
||||
}
|
||||
foreach ($arr as $address) {
|
||||
foreach ($addressList as $address) {
|
||||
$address = trim($address);
|
||||
if (empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
||||
continue;
|
||||
}
|
||||
@@ -118,6 +129,42 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
return $this->where(array('lower' => strtolower($address)))->findOne();
|
||||
}
|
||||
|
||||
public function getEntityListByAddressId($emailAddressId, $exceptionEntity = null)
|
||||
{
|
||||
$entityList = [];
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
SELECT entity_email_address.entity_type AS 'entityType', entity_email_address.entity_id AS 'entityId'
|
||||
FROM entity_email_address
|
||||
WHERE
|
||||
entity_email_address.email_address_id = ".$pdo->quote($emailAddressId)." AND
|
||||
entity_email_address.deleted = 0
|
||||
";
|
||||
if ($exceptionEntity) {
|
||||
$sql .= "
|
||||
AND (
|
||||
entity_email_address.entity_type <> " .$pdo->quote($exceptionEntity->getEntityType()) . "
|
||||
OR
|
||||
entity_email_address.entity_id <> " .$pdo->quote($exceptionEntity->id) . "
|
||||
)
|
||||
";
|
||||
}
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
while ($row = $sth->fetch()) {
|
||||
if (empty($row['entityType']) || empty($row['entityId'])) continue;
|
||||
if (!$this->getEntityManager()->hasRepository($row['entityType'])) continue;
|
||||
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
|
||||
if ($entity) {
|
||||
$entityList[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $entityList;
|
||||
}
|
||||
|
||||
public function getEntityByAddressId($emailAddressId, $entityType = null, $onlyName = false)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
@@ -219,14 +266,14 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
|
||||
$hash = array();
|
||||
foreach ($emailAddressData as $row) {
|
||||
$key = $row->emailAddress;
|
||||
$key = trim($row->emailAddress);
|
||||
if (!empty($key)) {
|
||||
$key = strtolower($key);
|
||||
$hash[$key] = [
|
||||
'primary' => !empty($row->primary) ? true : false,
|
||||
'optOut' => !empty($row->optOut) ? true : false,
|
||||
'invalid' => !empty($row->invalid) ? true : false,
|
||||
'emailAddress' => $row->emailAddress
|
||||
'emailAddress' => trim($row->emailAddress)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -249,6 +296,7 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
$toUpdate = array();
|
||||
$toRemove = array();
|
||||
|
||||
$revertData = [];
|
||||
|
||||
foreach ($hash as $key => $data) {
|
||||
$new = true;
|
||||
@@ -261,9 +309,10 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
if (array_key_exists($key, $hashPrev)) {
|
||||
$new = false;
|
||||
$changed =
|
||||
$hash[$key]['optOut'] != $hashPrev[$key]['optOut'] ||
|
||||
$hash[$key]['invalid'] != $hashPrev[$key]['invalid'] ||
|
||||
$hash[$key]['emailAddress'] !== $hashPrev[$key]['emailAddress'];
|
||||
$hash[$key]['optOut'] != $hashPrev[$key]['optOut'] ||
|
||||
$hash[$key]['invalid'] != $hashPrev[$key]['invalid'] ||
|
||||
$hash[$key]['emailAddress'] !== $hashPrev[$key]['emailAddress'];
|
||||
|
||||
if ($hash[$key]['primary']) {
|
||||
if ($hash[$key]['primary'] == $hashPrev[$key]['primary']) {
|
||||
$primary = false;
|
||||
@@ -303,12 +352,7 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
foreach ($toUpdate as $address) {
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
if ($emailAddress) {
|
||||
$skipSave = false;
|
||||
if (!$this->getInjection('user')->isAdmin()) {
|
||||
if ($this->getEntityByAddressId($emailAddress->id, 'User', true)) {
|
||||
$skipSave = true;
|
||||
}
|
||||
}
|
||||
$skipSave = $this->checkChangeIsForbidden($emailAddress, $entity);
|
||||
if (!$skipSave) {
|
||||
$emailAddress->set(array(
|
||||
'optOut' => $hash[$address]['optOut'],
|
||||
@@ -316,6 +360,11 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
'name' => $hash[$address]['emailAddress']
|
||||
));
|
||||
$this->save($emailAddress);
|
||||
} else {
|
||||
$revertData[$address] = [
|
||||
'optOut' => $emailAddress->get('optOut'),
|
||||
'invalid' => $emailAddress->get('invalid')
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,17 +381,25 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
));
|
||||
$this->save($emailAddress);
|
||||
} else {
|
||||
if (
|
||||
$emailAddress->get('optOut') != $hash[$address]['optOut'] ||
|
||||
$emailAddress->get('invalid') != $hash[$address]['invalid'] ||
|
||||
$emailAddress->get('emailAddress') != $hash[$address]['emailAddress']
|
||||
) {
|
||||
$emailAddress->set(array(
|
||||
'optOut' => $hash[$address]['optOut'],
|
||||
'invalid' => $hash[$address]['invalid'],
|
||||
'name' => $hash[$address]['emailAddress']
|
||||
));
|
||||
$this->save($emailAddress);
|
||||
$skipSave = $this->checkChangeIsForbidden($emailAddress, $entity);
|
||||
if (!$skipSave) {
|
||||
if (
|
||||
$emailAddress->get('optOut') != $hash[$address]['optOut'] ||
|
||||
$emailAddress->get('invalid') != $hash[$address]['invalid'] ||
|
||||
$emailAddress->get('emailAddress') != $hash[$address]['emailAddress']
|
||||
) {
|
||||
$emailAddress->set(array(
|
||||
'optOut' => $hash[$address]['optOut'],
|
||||
'invalid' => $hash[$address]['invalid'],
|
||||
'name' => $hash[$address]['emailAddress']
|
||||
));
|
||||
$this->save($emailAddress);
|
||||
}
|
||||
} else {
|
||||
$revertData[$address] = [
|
||||
'optOut' => $emailAddress->get('optOut'),
|
||||
'invalid' => $emailAddress->get('invalid')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +448,20 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($revertData)) {
|
||||
foreach ($emailAddressData as $row) {
|
||||
if (!empty($revertData[$row->emailAddress])) {
|
||||
$row->optOut = $revertData[$row->emailAddress]['optOut'];
|
||||
$row->invalid = $revertData[$row->emailAddress]['invalid'];
|
||||
}
|
||||
}
|
||||
$entity->set('emailAddressData', $emailAddressData);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!$entity->has('emailAddress')) {
|
||||
return;
|
||||
}
|
||||
$entityRepository = $this->getEntityManager()->getRepository($entity->getEntityName());
|
||||
if (!empty($emailAddressValue)) {
|
||||
if ($emailAddressValue != $entity->getFetched('emailAddress')) {
|
||||
@@ -436,5 +506,9 @@ class EmailAddress extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkChangeIsForbidden($entity, $excudeEntity)
|
||||
{
|
||||
return !$this->getInjection('aclManager')->getImplementation('EmailAddress')->checkEditInEntity($this->getInjection('user'), $entity, $excudeEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,33 +41,40 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('user');
|
||||
$this->addDependency('acl');
|
||||
$this->addDependency('aclManager');
|
||||
}
|
||||
|
||||
public function getIds($arr = array())
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
public function getIds($numberList = [])
|
||||
{
|
||||
$ids = array();
|
||||
if (!empty($arr)) {
|
||||
$a = array_map(function ($item) {
|
||||
return $item;
|
||||
}, $arr);
|
||||
$phoneNumbers = $this->where(array(
|
||||
'name' => array_map(function ($item) {
|
||||
return $item;
|
||||
}, $arr)
|
||||
))->find();
|
||||
if (!empty($numberList)) {
|
||||
$phoneNumbers = $this->where([
|
||||
[
|
||||
'name' => $numberList,
|
||||
'hash' => null
|
||||
]
|
||||
])->find();
|
||||
|
||||
$ids = array();
|
||||
$exist = array();
|
||||
foreach ($phoneNumbers as $phoneNumber) {
|
||||
$ids[] = $phoneNumber->id;
|
||||
$exist[] = $phoneNumber->get('name');
|
||||
}
|
||||
foreach ($arr as $phone) {
|
||||
if (empty($phone)) {
|
||||
foreach ($numberList as $number) {
|
||||
$number = trim($number);
|
||||
if (empty($number)) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($phone, $exist)) {
|
||||
if (!in_array($number, $exist)) {
|
||||
$phoneNumber = $this->get();
|
||||
$phoneNumber->set('name', $phone);
|
||||
$phoneNumber->set('name', $number);
|
||||
$this->save($phoneNumber);
|
||||
$ids[] = $phoneNumber->id;
|
||||
}
|
||||
@@ -112,6 +119,42 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
return $this->where(array('name' => $number))->findOne();
|
||||
}
|
||||
|
||||
public function getEntityListByPhoneNumberId($phoneNumberId, $exceptionEntity = null)
|
||||
{
|
||||
$entityList = [];
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
$sql = "
|
||||
SELECT entity_phone_number.entity_type AS 'entityType', entity_phone_number.entity_id AS 'entityId'
|
||||
FROM entity_phone_number
|
||||
WHERE
|
||||
entity_phone_number.phone_number_id = ".$pdo->quote($phoneNumberId)." AND
|
||||
entity_phone_number.deleted = 0
|
||||
";
|
||||
if ($exceptionEntity) {
|
||||
$sql .= "
|
||||
AND (
|
||||
entity_phone_number.entity_type <> " .$pdo->quote($exceptionEntity->getEntityType()) . "
|
||||
OR
|
||||
entity_phone_number.entity_id <> " .$pdo->quote($exceptionEntity->id) . "
|
||||
)
|
||||
";
|
||||
}
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
while ($row = $sth->fetch()) {
|
||||
if (empty($row['entityType']) || empty($row['entityId'])) continue;
|
||||
if (!$this->getEntityManager()->hasRepository($row['entityType'])) continue;
|
||||
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
|
||||
if ($entity) {
|
||||
$entityList[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $entityList;
|
||||
}
|
||||
|
||||
public function getEntityByPhoneNumberId($phoneNumberId, $entityType = null)
|
||||
{
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
@@ -167,7 +210,7 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
|
||||
$hash = array();
|
||||
foreach ($phoneNumberData as $row) {
|
||||
$key = $row->phoneNumber;
|
||||
$key = trim($row->phoneNumber);
|
||||
if (!empty($key)) {
|
||||
$hash[$key] = array(
|
||||
'primary' => $row->primary ? true : false,
|
||||
@@ -182,7 +225,7 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
if (!empty($key)) {
|
||||
$hashPrev[$key] = array(
|
||||
'primary' => $row->primary ? true : false,
|
||||
'type' => $row->type,
|
||||
'type' => $row->type
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -192,6 +235,7 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
$toUpdate = array();
|
||||
$toRemove = array();
|
||||
|
||||
$revertData = [];
|
||||
|
||||
foreach ($hash as $key => $data) {
|
||||
$new = true;
|
||||
@@ -243,17 +287,16 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
foreach ($toUpdate as $number) {
|
||||
$phoneNumber = $this->getByNumber($number);
|
||||
if ($phoneNumber) {
|
||||
$skipSave = false;
|
||||
if (!$this->getInjection('user')->isAdmin()) {
|
||||
if ($this->getEntityByPhoneNumberId($phoneNumber->id, 'User')) {
|
||||
$skipSave = true;
|
||||
}
|
||||
}
|
||||
$skipSave = $this->checkChangeIsForbidden($phoneNumber, $entity);
|
||||
if (!$skipSave) {
|
||||
$phoneNumber->set(array(
|
||||
'type' => $hash[$number]['type'],
|
||||
));
|
||||
$this->save($phoneNumber);
|
||||
} else {
|
||||
$revertData[$number] = [
|
||||
'type' => $phoneNumber->get('type')
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,11 +312,18 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
));
|
||||
$this->save($phoneNumber);
|
||||
} else {
|
||||
if ($phoneNumber->get('type') != $hash[$number]['type']) {
|
||||
$phoneNumber->set(array(
|
||||
'type' => $hash[$number]['type'],
|
||||
));
|
||||
$this->save($phoneNumber);
|
||||
$skipSave = $this->checkChangeIsForbidden($phoneNumber, $entity);
|
||||
if (!$skipSave) {
|
||||
if ($phoneNumber->get('type') != $hash[$number]['type']) {
|
||||
$phoneNumber->set(array(
|
||||
'type' => $hash[$number]['type'],
|
||||
));
|
||||
$this->save($phoneNumber);
|
||||
}
|
||||
} else {
|
||||
$revertData[$number] = [
|
||||
'type' => $phoneNumber->get('type')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +371,20 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
$sth->execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($revertData)) {
|
||||
foreach ($phoneNumberData as $row) {
|
||||
if (!empty($revertData[$row->phoneNumber])) {
|
||||
$row->type = $revertData[$row->phoneNumber]['type'];
|
||||
}
|
||||
}
|
||||
$entity->set('phoneNumberData', $phoneNumberData);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!$entity->has('phoneNumber')) {
|
||||
return;
|
||||
}
|
||||
$entityRepository = $this->getEntityManager()->getRepository($entity->getEntityName());
|
||||
if (!empty($phoneNumberValue)) {
|
||||
if ($phoneNumberValue !== $entity->getFetched('phoneNumber')) {
|
||||
@@ -370,5 +433,9 @@ class PhoneNumber extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkChangeIsForbidden($entity, $excudeEntity)
|
||||
{
|
||||
return !$this->getInjection('aclManager')->getImplementation('PhoneNumber')->checkEditInEntity($this->getInjection('user'), $entity, $excudeEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,8 @@
|
||||
"dynamicLogicOptions": "Conditional options",
|
||||
"probabilityMap": "Stage Probabilities (%)",
|
||||
"readOnly": "Read-only",
|
||||
"maxFileSize": "Max File Size (Mb)"
|
||||
"maxFileSize": "Max File Size (Mb)",
|
||||
"isPersonalData": "Is Personal Data"
|
||||
},
|
||||
"messages": {
|
||||
"upgradeVersion": "EspoCRM will be upgraded to version <strong>{version}</strong>. Please be patient as this may take a while.",
|
||||
|
||||
@@ -227,7 +227,10 @@
|
||||
"New notifications": "New notifications",
|
||||
"Manage Categories": "Manage Categories",
|
||||
"Manage Folders": "Manage Folders",
|
||||
"Convert to": "Convert to"
|
||||
"Convert to": "Convert to",
|
||||
"View Personal Data": "View Personal Data",
|
||||
"Personal Data": "Personal Data",
|
||||
"Erase": "Erase"
|
||||
},
|
||||
"messages": {
|
||||
"pleaseWait": "Please wait...",
|
||||
@@ -283,7 +286,8 @@
|
||||
"massFollowResultSingle": "{count} record now is followed",
|
||||
"massUnfollowResultSingle": "{count} record now is not followed",
|
||||
"massFollowZeroResult": "Nothing got followed",
|
||||
"massUnfollowZeroResult": "Nothing got unfollowed"
|
||||
"massUnfollowZeroResult": "Nothing got unfollowed",
|
||||
"erasePersonalDataConfirmation": "Checked fields will be erased permanently. Are you sure?"
|
||||
},
|
||||
"boolFilters": {
|
||||
"onlyMy": "Only My",
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"userPermission": "User Permission",
|
||||
"portalPermission": "Portal Permission",
|
||||
"groupEmailAccountPermission": "Group Email Account Permission",
|
||||
"exportPermission": "Export Permission"
|
||||
"exportPermission": "Export Permission",
|
||||
"dataPrivacyPermission": "Data Privacy Permission"
|
||||
},
|
||||
"links": {
|
||||
"users": "Users",
|
||||
@@ -17,7 +18,8 @@
|
||||
"userPermission": "Allows to restrict an ability for users to view activities, calendar and stream of other users.\n\nall - can view all\n\nteam - can view activities of teammates only\n\nno - can't view",
|
||||
"portalPermission": "Defines an access to portal information, ability to post messages to portal users.",
|
||||
"groupEmailAccountPermission": "Defines an access to group email accounts, an ability to send emails from group SMTP.",
|
||||
"exportPermission": "Defines wheter users have an ability to export records."
|
||||
"exportPermission": "Defines wheter users have an ability to export records.",
|
||||
"dataPrivacyPermission": "Allows to view and erase personal data."
|
||||
},
|
||||
"labels": {
|
||||
"Access": "Access",
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
"authTokenPreventConcurrent": "Only one auth token per user",
|
||||
"scopeColorsDisabled": "Disable scope colors",
|
||||
"tabColorsDisabled": "Disable tab colors",
|
||||
"tabIconsDisabled": "Disable tab icons"
|
||||
"tabIconsDisabled": "Disable tab icons",
|
||||
"emailAddressIsOptedOutByDefault": "Mark new email addresses as opted-out"
|
||||
},
|
||||
"options": {
|
||||
"weekStart": {
|
||||
@@ -150,7 +151,8 @@
|
||||
"aclAllowDeleteCreated": "Users will be able to remove records they created even if they don't have a delete access.",
|
||||
"textFilterUseContainsForVarchar": "If not checked then 'starts with' operator is used. You can use the wildcard '%'.",
|
||||
"streamEmailNotificationsEntityList": "Email notifications about stream updates of followed records. Users will receive email notifications only for specified entity types.",
|
||||
"authTokenPreventConcurrent": "Users won't be able to be logged in on multiple devices simultaneously."
|
||||
"authTokenPreventConcurrent": "Users won't be able to be logged in on multiple devices simultaneously.",
|
||||
"emailAddressIsOptedOutByDefault": "When creating new record email addess will be marked as opted-out."
|
||||
},
|
||||
"labels": {
|
||||
"System": "System",
|
||||
|
||||
@@ -3,13 +3,22 @@
|
||||
"rows": [
|
||||
[
|
||||
{"name": "name"},
|
||||
false,
|
||||
false
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[
|
||||
{"name": "exportPermission"},
|
||||
{"name": "userPermission"}
|
||||
{"name": "userPermission"},
|
||||
{"name": "assignmentPermission"}
|
||||
],
|
||||
[
|
||||
{"name": "assignmentPermission"},
|
||||
{"name": "portalPermission"},
|
||||
{"name": "groupEmailAccountPermission"}
|
||||
{"name": "groupEmailAccountPermission"},
|
||||
{"name": "dataPrivacyPermission"}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[{"name": "exportDisabled"}, {"name": "globalSearchEntityList"}],
|
||||
[{"name": "followCreatedEntities"}, {"name": "b2cMode"}],
|
||||
[{"name": "aclStrictMode"}, {"name": "aclAllowDeleteCreated"}],
|
||||
[{"name": "textFilterUseContainsForVarchar"}, false]
|
||||
[{"name": "textFilterUseContainsForVarchar"}, {"name": "emailAddressIsOptedOutByDefault"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -112,28 +112,32 @@
|
||||
"userPermission",
|
||||
"portalPermission",
|
||||
"groupEmailAccountPermission",
|
||||
"exportPermission"
|
||||
"exportPermission",
|
||||
"dataPrivacyPermission"
|
||||
],
|
||||
"valuePermissionHighestLevels": {
|
||||
"assignmentPermission": "all",
|
||||
"userPermission": "all",
|
||||
"portalPermission": "yes",
|
||||
"groupEmailAccountPermission": "all",
|
||||
"exportPermission": "yes"
|
||||
"exportPermission": "yes",
|
||||
"dataPrivacyPermission": "yes"
|
||||
},
|
||||
"permissionsDefaults": {
|
||||
"assignmentPermission": "all",
|
||||
"userPermission": "all",
|
||||
"portalPermission": "no",
|
||||
"groupEmailAccountPermission": "no",
|
||||
"exportPermission": "yes"
|
||||
"exportPermission": "yes",
|
||||
"dataPrivacyPermission": "no"
|
||||
},
|
||||
"permissionsStrictDefaults": {
|
||||
"assignmentPermission": "no",
|
||||
"userPermission": "no",
|
||||
"portalPermission": "no",
|
||||
"groupEmailAccountPermission": "no",
|
||||
"exportPermission": "no"
|
||||
"exportPermission": "no",
|
||||
"dataPrivacyPermission": "no"
|
||||
},
|
||||
"scopeLevelTypesDefaults": {
|
||||
"boolean": true,
|
||||
|
||||
@@ -41,6 +41,13 @@
|
||||
"tooltip": true,
|
||||
"translation": "Role.options.levelList"
|
||||
},
|
||||
"dataPrivacyPermission": {
|
||||
"type": "enum",
|
||||
"options": ["not-set", "yes", "no"],
|
||||
"default": "not-set",
|
||||
"tooltip": true,
|
||||
"translation": "Role.options.levelList"
|
||||
},
|
||||
"data": {
|
||||
"type": "jsonObject"
|
||||
},
|
||||
|
||||
@@ -465,6 +465,10 @@
|
||||
},
|
||||
"tabIconsDisabled": {
|
||||
"type": "bool"
|
||||
},
|
||||
"emailAddressIsOptedOutByDefault": {
|
||||
"type": "bool",
|
||||
"tooltip": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,5 +40,6 @@
|
||||
"notMergeable":true,
|
||||
"notCreatable": false,
|
||||
"filter": true,
|
||||
"skipOrmDefs": true
|
||||
"skipOrmDefs": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -34,5 +34,6 @@
|
||||
"fieldDefs":{
|
||||
"type":"jsonArray"
|
||||
},
|
||||
"translatedOptions": true
|
||||
"translatedOptions": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -34,5 +34,6 @@
|
||||
"fieldDefs": {
|
||||
"layoutListDisabled": true
|
||||
},
|
||||
"hookClassName": "\\Espo\\Core\\Utils\\FieldManager\\Hooks\\AttachmentMultipleType"
|
||||
"hookClassName": "\\Espo\\Core\\Utils\\FieldManager\\Hooks\\AttachmentMultipleType",
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -36,5 +36,6 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"filter": true
|
||||
"filter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -60,5 +60,6 @@
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"notNull":false
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -57,5 +57,6 @@
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"notNull":false
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -70,5 +70,6 @@
|
||||
"type":"datetime",
|
||||
"notNull":false
|
||||
},
|
||||
"view": "views/fields/datetime-optional"
|
||||
"view": "views/fields/datetime-optional",
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"notStorable":true
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -37,5 +37,6 @@
|
||||
"fieldDefs":{
|
||||
"type":"varchar"
|
||||
},
|
||||
"translatedOptions": true
|
||||
"translatedOptions": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
"type": "belongsTo",
|
||||
"entity": "Attachment",
|
||||
"skipOrmDefs": true
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"type": "belongsTo",
|
||||
"entity": "Attachment",
|
||||
"skipOrmDefs": true
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -30,5 +30,6 @@
|
||||
"type":"bool"
|
||||
}
|
||||
],
|
||||
"filter": true
|
||||
"filter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@
|
||||
"fieldDefs":{
|
||||
"type":"jsonArray"
|
||||
},
|
||||
"translatedOptions": true
|
||||
"translatedOptions": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -28,5 +28,6 @@
|
||||
"notMergeable":true,
|
||||
"notCreatable":true,
|
||||
"filter":true,
|
||||
"skipOrmDefs": true
|
||||
"skipOrmDefs": true,
|
||||
"personalData": true
|
||||
}
|
||||
@@ -25,5 +25,6 @@
|
||||
"fieldDefs":{
|
||||
"notStorable":true
|
||||
},
|
||||
"translatedOptions": true
|
||||
"translatedOptions": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -36,5 +36,6 @@
|
||||
"type":"bool"
|
||||
}
|
||||
],
|
||||
"filter": true
|
||||
"filter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"type":"varchar"
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
"type":"bool"
|
||||
}
|
||||
],
|
||||
"filter": true
|
||||
"filter": true,
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@
|
||||
"filter": true,
|
||||
"fieldDefs":{
|
||||
"type":"text"
|
||||
}
|
||||
},
|
||||
"personalData": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
<?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\Services;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use \Espo\Core\Htmlizer\Htmlizer;
|
||||
|
||||
class DataPrivacy extends \Espo\Core\Services\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('fileManager');
|
||||
$this->addDependency('acl');
|
||||
$this->addDependency('aclManager');
|
||||
$this->addDependency('metadata');
|
||||
$this->addDependency('serviceFactory');
|
||||
$this->addDependency('dateTime');
|
||||
$this->addDependency('number');
|
||||
$this->addDependency('entityManager');
|
||||
$this->addDependency('defaultLanguage');
|
||||
$this->addDependency('fieldManagerUtil');
|
||||
$this->addDependency('user');
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->getInjection('metadata');
|
||||
}
|
||||
|
||||
protected function getServiceFactory()
|
||||
{
|
||||
return $this->getInjection('serviceFactory');
|
||||
}
|
||||
|
||||
protected function getFileManager()
|
||||
{
|
||||
return $this->getInjection('fileManager');
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->getInjection('entityManager');
|
||||
}
|
||||
|
||||
public function erase($entityType, $id, array $fieldList)
|
||||
{
|
||||
if ($this->getAcl()->get('dataPrivacyPermission') === 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($this->getServiceFactory()->checkExists($entityType)) {
|
||||
$service = $this->getServiceFactory()->create($entityType);
|
||||
} else {
|
||||
$service = $this->getServiceFactory()->create('Record');
|
||||
$service->setEntityType($entityType);
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity($entityType, $id);
|
||||
|
||||
if (!$entity) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if (!$this->getAcl()->check($entity, 'edit')) {
|
||||
throw new Forbidden("No edit access.");
|
||||
}
|
||||
|
||||
$forbiddenFieldList = $this->getAcl()->getScopeForbiddenFieldList($entityType, 'edit');
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if (in_array($field, $forbiddenFieldList)) {
|
||||
throw new Forbidden("Field '{$field}' is forbidden to edit.");
|
||||
}
|
||||
}
|
||||
|
||||
$service->loadAdditionalFields($entity);
|
||||
|
||||
$filedManager = $this->getInjection('fieldManagerUtil');
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
$attributeList = $filedManager->getActualAttributeList($entityType, $field);
|
||||
|
||||
if ($type === 'email') {
|
||||
$emailAddressList = $entity->get('emailAddresses');
|
||||
foreach ($emailAddressList as $emailAddress) {
|
||||
if (
|
||||
$this
|
||||
->getInjection('aclManager')
|
||||
->getImplementation('EmailAddress')
|
||||
->checkEditInEntity($this->getInjection('user'), $emailAddress, $entity)
|
||||
) {
|
||||
$hash = $this->getEntityManager()->getRepository('EmailAddress')->hashAddress($emailAddress->get('name'));
|
||||
$emailAddress->set('hash', $hash);
|
||||
$emailAddress->set('name', 'ERASED:' . $emailAddress->id);
|
||||
$emailAddress->set('optOut', true);
|
||||
$this->getEntityManager()->saveEntity($emailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
$entity->clear($field);
|
||||
$entity->clear($field . 'Data');
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($type === 'phone') {
|
||||
$phoneNumberList = $entity->get('phoneNumbers');
|
||||
foreach ($phoneNumberList as $phoneNumber) {
|
||||
if (
|
||||
$this
|
||||
->getInjection('aclManager')
|
||||
->getImplementation('PhoneNumber')
|
||||
->checkEditInEntity($this->getInjection('user'), $phoneNumber, $entity)
|
||||
) {
|
||||
$hash = $this->getEntityManager()->getRepository('PhoneNumber')->hashNumber($phoneNumber->get('name'));
|
||||
$phoneNumber->set('hash', $hash);
|
||||
$phoneNumber->set('name', 'ERASED:' . $phoneNumber->id);
|
||||
$this->getEntityManager()->saveEntity($phoneNumber);
|
||||
}
|
||||
}
|
||||
|
||||
$entity->clear($field);
|
||||
$entity->clear($field . 'Data');
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($type === 'file' || $type === 'image') {
|
||||
$attachmentId = $entity->get($field . 'Id');
|
||||
if ($attachmentId) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $attachmentId);
|
||||
$this->getEntityManager()->removeEntity($attachment);
|
||||
}
|
||||
|
||||
}
|
||||
else if ($type === 'attachmentMultiple') {
|
||||
$attachmentList = $entity->get($field);
|
||||
foreach ($attachmentList as $attachment) {
|
||||
$this->getEntityManager()->removeEntity($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($attributeList as $attribute) {
|
||||
if (in_array($entity->getAttributeType($attribute), [$entity::VARCHAR, $entity::TEXT]) && $entity->get($attribute)) {
|
||||
$entity->set($attribute, null);
|
||||
} else {
|
||||
$entity->set($attribute, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->getEntityManager()->saveEntity($entity);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function exportPdf()
|
||||
{
|
||||
|
||||
|
||||
$htmlizer = new Htmlizer(
|
||||
$this->getFileManager(),
|
||||
$this->getInjection('dateTime'),
|
||||
$this->getInjection('number'),
|
||||
$this->getAcl(),
|
||||
$this->getInjection('entityManager'),
|
||||
$this->getInjection('metadata'),
|
||||
$this->getInjection('defaultLanguage')
|
||||
);
|
||||
|
||||
$pdf = new \Espo\Core\Pdf\Tcpdf();
|
||||
|
||||
$fontFace = $this->getConfig()->get('pdfFontFace', $this->fontFace);
|
||||
|
||||
$pdf->setFont($fontFace, '', $this->fontSize, '', true);
|
||||
$pdf->setPrintHeader(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
{{#each emailAddressData}}
|
||||
<div>
|
||||
{{#unless invalid}}
|
||||
{{#unless erased}}
|
||||
<a href="javascript:" data-email-address="{{emailAddress}}" data-action="mailTo">
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
<span {{#if invalid}}style="text-decoration: line-through;"{{/if}}{{#if optOut}}style="text-decoration: line-through;"{{/if}}>{{emailAddress}}</span>
|
||||
{{#unless invalid}}
|
||||
{{#unless erased}}
|
||||
{{/unless}}
|
||||
</a>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<a href="javascript:" data-email-address="{{value}}" data-action="mailTo" title="{{value}}"{{#if isOptedOut}} style="text-decoration: line-through;"{{/if}}>{{value}}</a>
|
||||
{{#unless isErased}}<a href="javascript:" data-email-address="{{value}}" data-action="mailTo" title="{{value}}"{{#if isOptedOut}} style="text-decoration: line-through;"{{/if}}>{{/unless}}{{value}}{{#unless isErased}}</a>{{/unless}}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
{{#if phoneNumberData}}
|
||||
{{#each phoneNumberData}}
|
||||
<div>
|
||||
{{#if ../doNotCall}}<s>{{/if}}<a href="tel:{{phoneNumber}}" data-phone-number="{{phoneNumber}}" data-action="dial">{{phoneNumber}}</a>{{#if ../doNotCall}}</s>{{/if}}
|
||||
{{#if ../doNotCall}}<s>{{/if}}
|
||||
{{#unless erased}}
|
||||
<a href="tel:{{phoneNumber}}" data-phone-number="{{phoneNumber}}" data-action="dial">
|
||||
{{/unless}}
|
||||
{{phoneNumber}}
|
||||
{{#unless erased}}
|
||||
</a>
|
||||
{{/unless}}
|
||||
{{#if ../doNotCall}}</s>{{/if}}
|
||||
<span class="text-muted small">({{translateOption type scope=../../scope field=../../name}})</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{{#if doNotCall}}<s>{{/if}}<a href="tel:{{value}}" data-phone-number="{{value}}" data-action="dial" title="{{value}}">{{value}}</a>{{#if doNotCall}}</s>{{/if}}
|
||||
{{#if doNotCall}}<s>{{/if}}{{#unless isErased}}<a href="tel:{{value}}" data-phone-number="{{value}}" data-action="dial" title="{{value}}">{{/unless}}{{value}}{{#unless isErased}}</a>{{/unless}}{{#if doNotCall}}</s>{{/if}}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<div class="record">{{{record}}}</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
{{#if fieldDataList.length}}
|
||||
<table class="table table-bordered">
|
||||
{{#if editAccess}}
|
||||
<tr>
|
||||
<th width="20"><input type="checkbox" class="checkbox-all"></th>
|
||||
<th width="30%"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#each fieldDataList}}
|
||||
<tr>
|
||||
{{#if ../editAccess}}<td>{{#if editAccess}}<input type="checkbox" class="checkbox" data-name="{{name}}">{{/if}}</td>{{/if}}
|
||||
<td width="30%">{{translate name category='fields' scope=../scope}}</td>
|
||||
<td>
|
||||
<div class="field" data-name="{{name}}">{{{var key ../this}}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{else}}
|
||||
{{translate 'No Data'}}
|
||||
{{/if}}
|
||||
@@ -32,6 +32,8 @@ Espo.define('email-helper', [], function () {
|
||||
this.language = language;
|
||||
this.user = user;
|
||||
this.dateTime = dateTime;
|
||||
|
||||
this.erasedPlaceholder = 'ERASED:';
|
||||
}
|
||||
|
||||
_.extend(EmailHelper.prototype, {
|
||||
@@ -120,15 +122,39 @@ Espo.define('email-helper', [], function () {
|
||||
item = item.trim();
|
||||
if (item != this.getUser().get('emailAddress')) {
|
||||
if (isReplyOnSent) {
|
||||
attributes.to += ';' + item;
|
||||
if (attributes.to) {
|
||||
attributes.to += ';'
|
||||
}
|
||||
attributes.to += item;
|
||||
} else {
|
||||
attributes.cc += ';' + item;
|
||||
if (attributes.cc) {
|
||||
attributes.cc += ';'
|
||||
}
|
||||
attributes.cc += item;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
attributes.cc = attributes.cc.replace(/^(\; )/,"");
|
||||
}
|
||||
|
||||
if (attributes.to) {
|
||||
var toList = attributes.to.split(';');
|
||||
toList = toList.filter(function (item) {
|
||||
if (item.indexOf(this.erasedPlaceholder) === 0) return false;
|
||||
return true;
|
||||
}, this);
|
||||
attributes.to = toList.join(';');
|
||||
}
|
||||
|
||||
if (attributes.cc) {
|
||||
var ccList = attributes.cc.split(';');
|
||||
ccList = ccList.filter(function (item) {
|
||||
if (item.indexOf(this.erasedPlaceholder) === 0) return false;
|
||||
return true;
|
||||
}, this);
|
||||
attributes.cc = ccList.join(';');
|
||||
}
|
||||
|
||||
if (model.get('parentId')) {
|
||||
attributes['parentId'] = model.get('parentId');
|
||||
attributes['parentName'] = model.get('parentName');
|
||||
|
||||
@@ -102,6 +102,14 @@ Espo.define('views/admin/field-manager/edit', ['view', 'model'], function (Dep,
|
||||
this.type = model.getFieldType(this.field);
|
||||
}
|
||||
|
||||
if (
|
||||
this.getMetadata().get(['scopes', this.scope, 'hasPersonalData'])
|
||||
&&
|
||||
this.getMetadata().get(['fields', this.type, 'personalData'])
|
||||
) {
|
||||
this.hasPersonalData = true;
|
||||
}
|
||||
|
||||
Promise.race([
|
||||
new Promise(function (resolve) {
|
||||
if (this.isNew) {
|
||||
@@ -137,6 +145,13 @@ Espo.define('views/admin/field-manager/edit', ['view', 'model'], function (Dep,
|
||||
this.paramList.push(o);
|
||||
}, this);
|
||||
|
||||
if (this.hasPersonalData) {
|
||||
this.paramList.push({
|
||||
name: 'isPersonalData',
|
||||
type: 'bool'
|
||||
});
|
||||
}
|
||||
|
||||
this.paramList.forEach(function (o) {
|
||||
this.model.defs.fields[o.name] = o;
|
||||
}, this);
|
||||
@@ -159,7 +174,14 @@ Espo.define('views/admin/field-manager/edit', ['view', 'model'], function (Dep,
|
||||
rows: 1
|
||||
});
|
||||
|
||||
if (this.hasPersonalData) {
|
||||
this.createFieldView('bool', 'isPersonalData', null, {});
|
||||
}
|
||||
|
||||
this.createFieldView('text', 'tooltipText', null, {
|
||||
trim: true,
|
||||
rows: 1
|
||||
});
|
||||
|
||||
this.hasDynamicLogicPanel = false;
|
||||
if (
|
||||
|
||||
@@ -47,7 +47,7 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
var notValid = false;
|
||||
data.forEach(function (row, i) {
|
||||
var emailAddress = row.emailAddress;
|
||||
if (!re.test(emailAddress)) {
|
||||
if (!re.test(emailAddress) && emailAddress.indexOf(this.erasedPlaceholder) !== 0) {
|
||||
var msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText());
|
||||
this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input');
|
||||
notValid = true;
|
||||
@@ -76,10 +76,16 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
if (this.model.isNew() || !this.model.get(this.name)) {
|
||||
if (!emailAddressData || !emailAddressData.length) {
|
||||
emailAddressData = [{
|
||||
var optOut = false;
|
||||
if (this.model.isNew()) {
|
||||
optOut = this.emailAddressOptedOutByDefault;
|
||||
} else {
|
||||
optOut = this.model.get(this.isOptedOutFieldName)
|
||||
}
|
||||
emailAddressData = [{
|
||||
emailAddress: this.model.get(this.name) || '',
|
||||
primary: true,
|
||||
optOut: false,
|
||||
optOut: optOut,
|
||||
invalid: false
|
||||
}];
|
||||
}
|
||||
@@ -90,7 +96,7 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
}
|
||||
|
||||
if ((!emailAddressData || emailAddressData.length === 0) && this.model.get(this.name)) {
|
||||
emailAddressData = [{
|
||||
emailAddressData = [{
|
||||
emailAddress: this.model.get(this.name),
|
||||
primary: true,
|
||||
optOut: false,
|
||||
@@ -98,12 +104,22 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
}];
|
||||
}
|
||||
|
||||
if (emailAddressData) {
|
||||
emailAddressData = Espo.Utils.clone(emailAddressData);
|
||||
emailAddressData.forEach(function (item) {
|
||||
item.erased = item.emailAddress.indexOf(this.erasedPlaceholder) === 0
|
||||
}, this);
|
||||
}
|
||||
|
||||
var data = _.extend({
|
||||
emailAddressData: emailAddressData
|
||||
}, Dep.prototype.data.call(this));
|
||||
|
||||
if (this.mode === 'list' || this.mode === 'detail') {
|
||||
data.isOptedOut = this.model.get(this.isOptedOutFieldName);
|
||||
if (this.model.get(this.name)) {
|
||||
data.isErased = this.model.get(this.name).indexOf(this.erasedPlaceholder) === 0
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -177,7 +193,7 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
o = {
|
||||
emailAddress: '',
|
||||
primary: data.length ? false : true,
|
||||
optOut: false,
|
||||
optOut: this.emailAddressOptedOutByDefault,
|
||||
invalid: false,
|
||||
lower: ''
|
||||
};
|
||||
@@ -346,6 +362,10 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
setup: function () {
|
||||
this.dataFieldName = this.name + 'Data';
|
||||
this.isOptedOutFieldName = this.name + 'IsOptedOut';
|
||||
|
||||
this.erasedPlaceholder = 'ERASED:';
|
||||
|
||||
this.emailAddressOptedOutByDefault = this.getConfig().get('emailAddressIsOptedOutByDefault');
|
||||
},
|
||||
|
||||
fetchEmailAddressData: function () {
|
||||
|
||||
@@ -68,6 +68,13 @@ Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) {
|
||||
phoneNumberData = this.model.get(this.dataFieldName) || false;
|
||||
}
|
||||
|
||||
if (phoneNumberData) {
|
||||
phoneNumberData = Espo.Utils.clone(phoneNumberData);
|
||||
phoneNumberData.forEach(function (item) {
|
||||
item.erased = item.phoneNumber.indexOf(this.erasedPlaceholder) === 0
|
||||
}, this);
|
||||
}
|
||||
|
||||
if ((!phoneNumberData || phoneNumberData.length === 0) && this.model.get(this.name)) {
|
||||
phoneNumberData = [{
|
||||
phoneNumber: this.model.get(this.name),
|
||||
@@ -77,10 +84,18 @@ Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) {
|
||||
}];
|
||||
}
|
||||
|
||||
return _.extend({
|
||||
var data = _.extend({
|
||||
phoneNumberData: phoneNumberData,
|
||||
doNotCall: this.model.get('doNotCall')
|
||||
}, Dep.prototype.data.call(this));
|
||||
|
||||
if (this.mode === 'detail' || this.mode === 'list') {
|
||||
if (this.model.get(this.name)) {
|
||||
data.isErased = this.model.get(this.name).indexOf(this.erasedPlaceholder) === 0
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
events: {
|
||||
@@ -220,6 +235,8 @@ Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) {
|
||||
this.reRender();
|
||||
}, this);
|
||||
}
|
||||
|
||||
this.erasedPlaceholder = 'ERASED:';
|
||||
},
|
||||
|
||||
fetchPhoneNumberData: function () {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/personal-data/modals/personal-data', ['views/modal'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
className: 'dialog dialog-record',
|
||||
|
||||
template: 'personal-data/modals/personal-data',
|
||||
|
||||
backdrop: true,
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.buttonList = [
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Close'
|
||||
}
|
||||
];
|
||||
|
||||
this.header = this.getLanguage().translate('Personal Data');
|
||||
this.header += ': ' + Handlebars.Utils.escapeExpression(this.model.get('name'));
|
||||
|
||||
if (this.getAcl().check(this.model, 'edit')) {
|
||||
this.buttonList.unshift({
|
||||
name: 'erase',
|
||||
label: 'Erase',
|
||||
style: 'danger',
|
||||
disabled: true
|
||||
});
|
||||
}
|
||||
|
||||
this.fieldList = [];
|
||||
|
||||
this.scope = this.model.name;
|
||||
|
||||
this.createView('record', 'views/personal-data/record/record', {
|
||||
el: this.getSelector() + ' .record',
|
||||
model: this.model
|
||||
}, function (view) {
|
||||
this.listenTo(view, 'check', function (fieldList) {
|
||||
this.fieldList = fieldList;
|
||||
if (fieldList.length) {
|
||||
this.enableButton('erase');
|
||||
} else {
|
||||
this.disableButton('erase');
|
||||
}
|
||||
});
|
||||
|
||||
if (!view.fieldList.length) {
|
||||
this.disableButton('export');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actionErase: function () {
|
||||
this.confirm({
|
||||
message: this.translate('erasePersonalDataConfirmation', 'messages'),
|
||||
confirmText: this.translate('Erase')
|
||||
}, function () {
|
||||
this.disableButton('erase');
|
||||
this.ajaxPostRequest('DataPrivacy/action/erase', {
|
||||
fieldList: this.fieldList,
|
||||
entityType: this.scope,
|
||||
id: this.model.id
|
||||
}).then(function () {
|
||||
Espo.Ui.success(this.translate('Done'));
|
||||
|
||||
this.trigger('erase');
|
||||
}.bind(this)).fail(function () {
|
||||
this.enableButton('erase');
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/personal-data/record/record', 'views/record/base', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
template: 'personal-data/record/record',
|
||||
|
||||
events: _.extend({
|
||||
'click .checkbox': function (e) {
|
||||
var name = $(e.currentTarget).data('name');
|
||||
if (e.currentTarget.checked) {
|
||||
if (!~this.checkedFieldList.indexOf(name)) {
|
||||
this.checkedFieldList.push(name);
|
||||
}
|
||||
|
||||
if (this.checkedFieldList.length == this.fieldList.length) {
|
||||
this.$el.find('.checkbox-all').prop('checked', true);
|
||||
} else {
|
||||
this.$el.find('.checkbox-all').prop('checked', false);
|
||||
}
|
||||
} else {
|
||||
var index = this.checkedFieldList.indexOf(name);
|
||||
if (~index) {
|
||||
this.checkedFieldList.splice(index, 1);
|
||||
}
|
||||
|
||||
this.$el.find('.checkbox-all').prop('checked', false);
|
||||
}
|
||||
|
||||
this.trigger('check', this.checkedFieldList);
|
||||
},
|
||||
|
||||
'click .checkbox-all': function (e) {
|
||||
if (e.currentTarget.checked) {
|
||||
this.checkedFieldList = Espo.Utils.clone(this.fieldList);
|
||||
|
||||
this.$el.find('.checkbox').prop('checked', true);
|
||||
} else {
|
||||
this.checkedFieldList = [];
|
||||
|
||||
this.$el.find('.checkbox').prop('checked', false);
|
||||
}
|
||||
|
||||
this.trigger('check', this.checkedFieldList);
|
||||
},
|
||||
}, Dep.prototype.events),
|
||||
|
||||
data: function () {
|
||||
var data = {};
|
||||
data.fieldDataList = this.getFieldDataList();
|
||||
data.scope = this.scope;
|
||||
data.editAccess = this.editAccess;
|
||||
return data;
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.scope = this.model.name;
|
||||
|
||||
this.fieldList = [];
|
||||
|
||||
this.checkedFieldList = [];
|
||||
|
||||
this.editAccess = this.getAcl().check(this.model, 'edit');
|
||||
|
||||
var fieldDefs = this.getMetadata().get(['entityDefs', this.scope, 'fields']) || {};
|
||||
|
||||
var fieldList = [];
|
||||
|
||||
for (var field in fieldDefs) {
|
||||
var defs = fieldDefs[field];
|
||||
if (defs.isPersonalData) {
|
||||
fieldList.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
fieldList.forEach(function (field) {
|
||||
var type = fieldDefs[field].type;
|
||||
var attributeList = this.getFieldManager().getActualAttributeList(type, field);
|
||||
|
||||
var isNotEmpty = false;
|
||||
attributeList.forEach(function (attribute) {
|
||||
var value = this.model.get(attribute);
|
||||
if (value) {
|
||||
if (Object.prototype.toString.call(value) === '[object Array]') {
|
||||
if (value.length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
isNotEmpty = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
var hasAccess = !~this.getAcl().getScopeForbiddenFieldList(this.scope, 'view').indexOf(field);
|
||||
|
||||
if (isNotEmpty && hasAccess) {
|
||||
this.fieldList.push(field);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.fieldList = this.fieldList.sort(function (v1, v2) {
|
||||
return this.translate(v1, 'fields', this.scope).localeCompare(this.translate(v2, 'fields', this.scope));
|
||||
}.bind(this));
|
||||
|
||||
this.fieldList.forEach(function (field) {
|
||||
this.createField(field, null, null, 'detail', true);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getFieldDataList: function () {
|
||||
var forbiddenList = this.getAcl().getScopeForbiddenFieldList(this.scope, 'edit');
|
||||
|
||||
var list = [];
|
||||
this.fieldList.forEach(function (field) {
|
||||
list.push({
|
||||
name: field,
|
||||
key: field + 'Field',
|
||||
editAccess: this.editAccess && !~forbiddenList.indexOf(field)
|
||||
});
|
||||
}, this);
|
||||
return list;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -245,6 +245,15 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.type === 'detail' && this.getMetadata().get(['scopes', this.scope, 'hasPersonalData'])) {
|
||||
if (this.getAcl().get('dataPrivacyPermission') !== 'no') {
|
||||
this.dropdownItemList.push({
|
||||
'label': 'View Personal Data',
|
||||
'name': 'viewPersonalData'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hideActionItem: function (name) {
|
||||
@@ -874,6 +883,19 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
}
|
||||
},
|
||||
|
||||
actionViewPersonalData: function () {
|
||||
this.createView('viewPersonalData', 'views/personal-data/modals/personal-data', {
|
||||
model: this.model
|
||||
}, function (view) {
|
||||
view.render();
|
||||
|
||||
this.listenToOnce(view, 'erase', function () {
|
||||
this.clearView('viewPersonalData');
|
||||
this.model.fetch();
|
||||
}, this);
|
||||
});
|
||||
},
|
||||
|
||||
actionPrintPdf: function () {
|
||||
this.createView('pdfTemplate', 'views/modals/select-template', {
|
||||
entityType: this.model.name
|
||||
|
||||
Reference in New Issue
Block a user