attributes instead of fields; server side field level security
This commit is contained in:
@@ -108,5 +108,10 @@ class Acl
|
||||
{
|
||||
return $this->getAclManager()->checkUser($this->getUser(), $entity);
|
||||
}
|
||||
|
||||
public function getScopeForbiddenAttributeList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
return $this->getAclManager()->getScopeForbiddenAttributeList($this->getUser(), $scope, $action, $thresholdLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ class Base implements Injectable
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasField('assignedUsersIds') && $entity->hasRelation('assignedUsers')) {
|
||||
if ($entity->hasAttribute('assignedUsersIds') && $entity->hasRelation('assignedUsers')) {
|
||||
if ($entity->hasLinkMultipleId('assignedUsers', $user->id)) {
|
||||
return true;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ class Base implements Injectable
|
||||
{
|
||||
$userTeamIdList = $user->getLinkMultipleIdList('teams');
|
||||
|
||||
if (!$entity->hasRelation('teams') || !$entity->hasField('teamsIds')) {
|
||||
if (!$entity->hasRelation('teams') || !$entity->hasAttribute('teamsIds')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,39 +32,51 @@ namespace Espo\Core\Acl;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Entities\User;
|
||||
|
||||
use \Espo\Core\Utils\Config;
|
||||
use \Espo\Core\Utils\Metadata;
|
||||
use \Espo\Core\Utils\FieldManager;
|
||||
use \Espo\Core\Utils\File\Manager as FileManager;
|
||||
|
||||
class Table
|
||||
{
|
||||
private $data = null; /*array(
|
||||
'table' => new \StdClass(),
|
||||
'fieldLevelTable' => array()
|
||||
);*/
|
||||
private $data = null;
|
||||
|
||||
protected $cacheFile;
|
||||
private $cacheFile;
|
||||
|
||||
protected $actionList = ['read', 'stream', 'edit', 'delete'];
|
||||
|
||||
protected $levelList = ['all', 'team', 'own', 'no'];
|
||||
|
||||
protected $fieldLevelActionList = ['read', 'edit'];
|
||||
protected $fieldActionList = ['read', 'edit'];
|
||||
|
||||
protected $fieldLevelLevelList = ['yes', 'no'];
|
||||
protected $fieldLevelList = ['yes', 'no'];
|
||||
|
||||
protected $fileManager;
|
||||
private $fileManager;
|
||||
|
||||
protected $metadata;
|
||||
private $metadata;
|
||||
|
||||
public function __construct(\Espo\Entities\User $user, $config = null, $fileManager = null, $metadata = null)
|
||||
private $fieldManager;
|
||||
|
||||
protected $forbiddenAttributesCache = array();
|
||||
|
||||
public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManager $fieldManager = null)
|
||||
{
|
||||
$this->data = (object) [
|
||||
'table' => (object) [],
|
||||
'fieldLevelTable' => (object) []
|
||||
'fieldTable' => (object) [],
|
||||
'attributeTable' => (object) [],
|
||||
];
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->metadata = $metadata;
|
||||
|
||||
if ($fieldManager) {
|
||||
$this->fieldManager = $fieldManager;
|
||||
}
|
||||
|
||||
if (!$this->user->isFetched()) {
|
||||
throw new Error('User must be fetched before ACL check.');
|
||||
}
|
||||
@@ -88,6 +100,21 @@ class Table
|
||||
}
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
protected function getFieldManager()
|
||||
{
|
||||
return $this->fieldManager;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getMap()
|
||||
{
|
||||
return $this->data;
|
||||
@@ -131,7 +158,7 @@ class Table
|
||||
private function load()
|
||||
{
|
||||
$aclTableList = [];
|
||||
$fieldLevelTableList = [];
|
||||
$fieldTableList = [];
|
||||
$assignmentPermissionList = [];
|
||||
$userPermissionList = [];
|
||||
|
||||
@@ -140,7 +167,7 @@ class Table
|
||||
|
||||
foreach ($userRoles as $role) {
|
||||
$aclTableList[] = $role->get('data');
|
||||
$fieldLevelTableList[] = $role->get('fieldLevelData');
|
||||
$fieldTableList[] = $role->get('fieldData');
|
||||
$assignmentPermissionList[] = $role->get('assignmentPermission');
|
||||
$userPermissionList[] = $role->get('userPermission');
|
||||
}
|
||||
@@ -150,23 +177,23 @@ class Table
|
||||
$teamRoles = $team->get('roles');
|
||||
foreach ($teamRoles as $role) {
|
||||
$aclTableList[] = $role->get('data');
|
||||
$fieldLevelTableList[] = $role->get('fieldLevelData');
|
||||
$fieldTableList[] = $role->get('fieldData');
|
||||
$assignmentPermissionList[] = $role->get('assignmentPermission');
|
||||
$userPermissionList[] = $role->get('userPermission');
|
||||
}
|
||||
}
|
||||
|
||||
$aclTable = $this->mergeTableList($aclTableList);
|
||||
$fieldLevelTable = $this->mergeFieldLevelTableList($fieldLevelTableList);
|
||||
$fieldTable = $this->mergefieldTableList($fieldTableList);
|
||||
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
if ($this->metadata->get('scopes.' . $scope . '.disabled')) {
|
||||
$aclTable->$scope = false;
|
||||
unset($fieldLevelTable->$scope);
|
||||
unset($fieldTable->$scope);
|
||||
}
|
||||
}
|
||||
|
||||
$this->applySolid($aclTable, $fieldLevelTable);
|
||||
$this->applySolid($aclTable, $fieldTable);
|
||||
} else {
|
||||
$aclTable = (object) [];
|
||||
foreach ($this->getScopeList() as $scope) {
|
||||
@@ -181,7 +208,7 @@ class Table
|
||||
}
|
||||
}
|
||||
}
|
||||
$fieldLevelTable = (object) [];
|
||||
$fieldTable = (object) [];
|
||||
}
|
||||
|
||||
foreach ($aclTable as $scope => $data) {
|
||||
@@ -193,7 +220,9 @@ class Table
|
||||
}
|
||||
|
||||
$this->data->table = $aclTable;
|
||||
$this->data->fieldLevelTable = $fieldLevelTable;
|
||||
$this->data->fieldTable = $fieldTable;
|
||||
|
||||
$this->fillAttributeTable();
|
||||
|
||||
if (!$this->user->isAdmin()) {
|
||||
$this->data->assignmentPermission = $this->mergeValueList($assignmentPermissionList, $this->metadata->get('app.acl.valueDefaults.assignmentPermission', 'all'));
|
||||
@@ -204,7 +233,77 @@ class Table
|
||||
}
|
||||
}
|
||||
|
||||
private function applySolid(&$table, &$fieldLevelTable)
|
||||
public function getScopeForbiddenAttributeList($scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
$key = $scope . '_'. $thresholdLevel;
|
||||
if (isset($this->forbiddenAttributesCache[$key])) {
|
||||
return $this->forbiddenAttributesCache[$key];
|
||||
}
|
||||
|
||||
$attributeTable = $this->data->attributeTable;
|
||||
|
||||
if (!isset($attributeTable->$scope) || !isset($attributeTable->$scope->$action)) {
|
||||
$this->forbiddenAttributesCache[$key] = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
$levelList = [];
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
if (array_search($level, $this->fieldLevelList) >= array_search($thresholdLevel, $this->fieldLevelList)) {
|
||||
$levelList[] = $level;
|
||||
}
|
||||
}
|
||||
|
||||
$attributeList = [];
|
||||
|
||||
foreach ($levelList as $level) {
|
||||
if (!isset($attributeTable->$scope->$action->$level)) continue;
|
||||
foreach ($attributeTable->$scope->$action->$level as $attribute) {
|
||||
$attributeList[] = $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$this->forbiddenAttributesCache[$key] = $attributeList;
|
||||
|
||||
return $attributeList;
|
||||
}
|
||||
|
||||
protected function fillAttributeTable()
|
||||
{
|
||||
$fieldTable = $this->data->fieldTable;
|
||||
|
||||
$attributeTable = (object) [];
|
||||
|
||||
foreach (get_object_vars($fieldTable) as $scope => $scopeData) {
|
||||
$attributeTable->$scope = (object) [];
|
||||
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
$attributeTable->$scope->$action = (object) [];
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
$attributeTable->$scope->$action->$level = [];
|
||||
}
|
||||
}
|
||||
|
||||
foreach (get_object_vars($scopeData) as $field => $fieldData) {
|
||||
$attributeList = $this->getFieldManager()->getAttributeList($scope, $field);
|
||||
|
||||
foreach ($this->fieldActionList as $action) {
|
||||
if (!isset($fieldData->$action)) continue;
|
||||
foreach ($this->fieldLevelList as $level) {
|
||||
if ($fieldData->$action === $level) {
|
||||
foreach ($attributeList as $attribute) {
|
||||
$attributeTable->$scope->$action->{$level}[] = $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->attributeTable = $attributeTable;
|
||||
}
|
||||
|
||||
protected function applySolid(&$table, &$fieldTable)
|
||||
{
|
||||
if (!$this->metadata) {
|
||||
return;
|
||||
@@ -245,7 +344,7 @@ class Table
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getScopeWithAclList()
|
||||
protected function getScopeWithAclList()
|
||||
{
|
||||
$scopeList = [];
|
||||
$scopes = $this->metadata->get('scopes');
|
||||
@@ -257,7 +356,7 @@ class Table
|
||||
return $scopeList;
|
||||
}
|
||||
|
||||
private function getScopeList()
|
||||
protected function getScopeList()
|
||||
{
|
||||
$scopeList = [];
|
||||
$scopes = $this->metadata->get('scopes');
|
||||
@@ -336,7 +435,7 @@ class Table
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function mergeFieldLevelTableList(array $tableList)
|
||||
private function mergefieldTableList(array $tableList)
|
||||
{
|
||||
$data = (object) [];
|
||||
$scopeList = $this->getScopeWithAclList();
|
||||
@@ -358,14 +457,14 @@ class Table
|
||||
$data->$scope->$field = (object) [];
|
||||
}
|
||||
|
||||
foreach ($this->fieldLevelActionList as $i => $action) {
|
||||
foreach ($this->fieldActionList as $i => $action) {
|
||||
if (!isset($row->$action)) continue;
|
||||
|
||||
$level = $row->$action;
|
||||
if (!isset($data->$scope->$field->$action)) {
|
||||
$data->$scope->$field->$action = $level;
|
||||
} else {
|
||||
if (array_search($data->$scope->$field->$action, $this->fieldLevelLevelList) > array_search($level, $this->fieldLevelLevelList)) {
|
||||
if (array_search($data->$scope->$field->$action, $this->fieldLevelList) > array_search($level, $this->fieldLevelList)) {
|
||||
$data->$scope->$field->$action = $level;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,9 @@ class AclManager
|
||||
$config = $this->getContainer()->get('config');
|
||||
$fileManager = $this->getContainer()->get('fileManager');
|
||||
$metadata = $this->getContainer()->get('metadata');
|
||||
$fieldManager = $this->getContainer()->get('fieldManager');
|
||||
|
||||
$this->tableHashMap[$key] = new \Espo\Core\Acl\Table($user, $config, $fileManager, $metadata);
|
||||
$this->tableHashMap[$key] = new \Espo\Core\Acl\Table($user, $config, $fileManager, $metadata, $fieldManager);
|
||||
}
|
||||
|
||||
return $this->tableHashMap[$key];
|
||||
@@ -218,5 +219,11 @@ class AclManager
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getScopeForbiddenAttributeList(User $user, $scope, $action = 'read', $thresholdLevel = 'no')
|
||||
{
|
||||
if ($user->isAdmin()) return [];
|
||||
return $this->getTable($user)->getScopeForbiddenAttributeList($scope, $action, $thresholdLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class Entity extends \Espo\ORM\Entity
|
||||
|
||||
public function loadLinkMultipleField($field, $columns = null)
|
||||
{
|
||||
if ($this->hasRelation($field) && $this->hasField($field . 'Ids')) {
|
||||
if ($this->hasRelation($field) && $this->hasAttribute($field . 'Ids')) {
|
||||
|
||||
$defs = array();
|
||||
if (!empty($columns)) {
|
||||
@@ -127,7 +127,7 @@ class Entity extends \Espo\ORM\Entity
|
||||
{
|
||||
$idsField = $field . 'Ids';
|
||||
|
||||
if (!$this->hasField($idsField)) return null;
|
||||
if (!$this->hasAttribute($idsField)) return null;
|
||||
|
||||
if (!$this->has($idsField)) {
|
||||
if (!$this->isNew()) {
|
||||
@@ -145,7 +145,7 @@ class Entity extends \Espo\ORM\Entity
|
||||
{
|
||||
$idsField = $field . 'Ids';
|
||||
|
||||
if (!$this->hasField($idsField)) return null;
|
||||
if (!$this->hasAttribute($idsField)) return null;
|
||||
|
||||
if (!$this->has($idsField)) {
|
||||
if (!$this->isNew()) {
|
||||
|
||||
@@ -153,10 +153,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$this->getEntityManager()->getHookManager()->process($this->entityName, 'beforeRemove', $entity, $options);
|
||||
|
||||
$nowString = date('Y-m-d H:i:s', time());
|
||||
if ($entity->hasField('modifiedAt')) {
|
||||
if ($entity->hasAttribute('modifiedAt')) {
|
||||
$entity->set('modifiedAt', $nowString);
|
||||
}
|
||||
if ($entity->hasField('modifiedById')) {
|
||||
if ($entity->hasAttribute('modifiedById')) {
|
||||
$entity->set('modifiedById', $this->getEntityManager()->getUser()->id);
|
||||
}
|
||||
}
|
||||
@@ -208,13 +208,13 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$entity->set('id', Util::generateId());
|
||||
}
|
||||
|
||||
if ($entity->hasField('createdAt')) {
|
||||
if ($entity->hasAttribute('createdAt')) {
|
||||
$entity->set('createdAt', $nowString);
|
||||
}
|
||||
if ($entity->hasField('modifiedAt')) {
|
||||
if ($entity->hasAttribute('modifiedAt')) {
|
||||
$entity->set('modifiedAt', $nowString);
|
||||
}
|
||||
if ($entity->hasField('createdById')) {
|
||||
if ($entity->hasAttribute('createdById')) {
|
||||
$entity->set('createdById', $this->entityManager->getUser()->id);
|
||||
}
|
||||
|
||||
@@ -227,10 +227,10 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
$entity->clear('modifiedById');
|
||||
} else {
|
||||
if (empty($options['silent'])) {
|
||||
if ($entity->hasField('modifiedAt')) {
|
||||
if ($entity->hasAttribute('modifiedAt')) {
|
||||
$entity->set('modifiedAt', $nowString);
|
||||
}
|
||||
if ($entity->hasField('modifiedById')) {
|
||||
if ($entity->hasAttribute('modifiedById')) {
|
||||
$entity->set('modifiedById', $this->entityManager->getUser()->id);
|
||||
}
|
||||
}
|
||||
@@ -253,14 +253,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
|
||||
protected function handleEmailAddressSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('emailAddresses') && $entity->hasField('emailAddress')) {
|
||||
if ($entity->hasRelation('emailAddresses') && $entity->hasAttribute('emailAddress')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function handlePhoneNumberSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('phoneNumbers') && $entity->hasField('phoneNumber')) {
|
||||
if ($entity->hasRelation('phoneNumbers') && $entity->hasAttribute('phoneNumber')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +115,9 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTextFilterFields()
|
||||
protected function getTextFilterFieldList()
|
||||
{
|
||||
return $this->metadata->get("entityDefs.{$this->entityType}.collection.textFilterFields", array('name'));
|
||||
return $this->metadata->get("entityDefs.{$this->entityType}.collection.textFilterFields", ['name']);
|
||||
}
|
||||
|
||||
protected function getSeed()
|
||||
@@ -395,28 +395,28 @@ class Base
|
||||
|
||||
protected function hasAssignedUsersField()
|
||||
{
|
||||
if ($this->getSeed()->hasRelation('assignedUsers') && $this->getSeed()->hasField('assignedUsersIds')) {
|
||||
if ($this->getSeed()->hasRelation('assignedUsers') && $this->getSeed()->hasAttribute('assignedUsersIds')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function hasAssignedUserField()
|
||||
{
|
||||
if ($this->getSeed()->hasField('assignedUserId')) {
|
||||
if ($this->getSeed()->hasAttribute('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function hasCreatedByField()
|
||||
{
|
||||
if ($this->getSeed()->hasField('createdById')) {
|
||||
if ($this->getSeed()->hasAttribute('createdById')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function hasTeamsField()
|
||||
{
|
||||
if ($this->getSeed()->hasRelation('teams') && $this->getSeed()->hasField('teamsIds')) {
|
||||
if ($this->getSeed()->hasRelation('teams') && $this->getSeed()->hasAttribute('teamsIds')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -936,8 +936,8 @@ class Base
|
||||
|
||||
protected function textFilter($textFilter, &$result)
|
||||
{
|
||||
$fieldDefs = $this->getSeed()->getFields();
|
||||
$fieldList = $this->getTextFilterFields();
|
||||
$fieldDefs = $this->getSeed()->getAttributes();
|
||||
$fieldList = $this->getTextFilterFieldList();
|
||||
$d = array();
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
|
||||
@@ -285,4 +285,55 @@ class FieldManager
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getAttributeListByType($scope, $name, $type)
|
||||
{
|
||||
$fieldType = $this->getMetadata()->get('entityDefs.' . $scope . '.fields.' . $name . '.type');
|
||||
if (!$fieldType) return [];
|
||||
|
||||
$defs = $this->getMetadata()->get('fields.' . $fieldType);
|
||||
if (!$defs) return [];
|
||||
if (is_object($defs)) {
|
||||
$defs = get_object_vars($defs);
|
||||
}
|
||||
|
||||
$fieldList = [];
|
||||
|
||||
if (isset($defs[$type . 'Fields'])) {
|
||||
$list = $defs[$type . 'Fields'];
|
||||
$naming = 'suffix';
|
||||
if (isset($defs['naming'])) {
|
||||
$naming = $defs['naming'];
|
||||
}
|
||||
if ($naming == 'prefix') {
|
||||
foreach ($list as $f) {
|
||||
$fieldList[] = $f . ucfirst($name);
|
||||
}
|
||||
} else {
|
||||
foreach ($list as $f) {
|
||||
$fieldList[] = $name . ucfirst($f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($type == 'actual') {
|
||||
$fieldList[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $fieldList;
|
||||
}
|
||||
|
||||
public function getActualAttributeList($scope, $name)
|
||||
{
|
||||
return $this->getAttributeListByType($scope, $name, 'actual');
|
||||
}
|
||||
|
||||
public function getNotActualAttributeList($scope, $name)
|
||||
{
|
||||
return $this->getAttributeListByType($scope, $name, 'notActual');
|
||||
}
|
||||
|
||||
public function getAttributeList($scope, $name)
|
||||
{
|
||||
return array_merge($this->getActualAttributeList($scope, $name), $this->getNotActualAttributeList($scope, $name));
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ class Opportunity extends \Espo\Services\Record
|
||||
foreach ($rows as $row) {
|
||||
$result[$row['month']] = floatval($row['amount']);
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\ORM;
|
||||
|
||||
abstract class Entity implements IEntity
|
||||
{
|
||||
public $id = null;
|
||||
@@ -120,7 +121,7 @@ abstract class Entity implements IEntity
|
||||
if ($name == 'id') {
|
||||
$this->id = $value;
|
||||
}
|
||||
if ($this->hasField($name)) {
|
||||
if ($this->hasAttribute($name)) {
|
||||
$method = '_set' . ucfirst($name);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->$method($value);
|
||||
@@ -175,7 +176,7 @@ abstract class Entity implements IEntity
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
foreach ($this->fields as $field => $fieldDefs) {
|
||||
foreach ($this->getAttributes() as $field => $fieldDefs) {
|
||||
if (array_key_exists($field, $arr)) {
|
||||
if ($field == 'id') {
|
||||
$this->id = $arr[$field];
|
||||
@@ -264,6 +265,11 @@ abstract class Entity implements IEntity
|
||||
return isset($this->fields[$fieldName]);
|
||||
}
|
||||
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
return isset($this->fields[$name]);
|
||||
}
|
||||
|
||||
public function hasRelation($relationName)
|
||||
{
|
||||
return isset($this->relations[$relationName]);
|
||||
@@ -292,6 +298,11 @@ abstract class Entity implements IEntity
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getRelations()
|
||||
{
|
||||
return $this->relations;
|
||||
@@ -307,6 +318,11 @@ abstract class Entity implements IEntity
|
||||
return $this->has($fieldName) && ($this->get($fieldName) != $this->getFetched($fieldName));
|
||||
}
|
||||
|
||||
public function isAttributeChanged($fieldName)
|
||||
{
|
||||
return $this->has($fieldName) && ($this->get($fieldName) != $this->getFetched($fieldName));
|
||||
}
|
||||
|
||||
public function setFetched($fieldName, $value)
|
||||
{
|
||||
$this->fetchedValuesContainer[$fieldName] = $value;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"data": {
|
||||
"type": "jsonObject"
|
||||
},
|
||||
"fieldLevelData": {
|
||||
"fieldData": {
|
||||
"type": "jsonObject"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -31,8 +31,7 @@ namespace Espo\Services;
|
||||
|
||||
class Attachment extends Record
|
||||
{
|
||||
|
||||
protected $notFilteringFields = array('contents');
|
||||
protected $notFilteringAttributeList = ['contents'];
|
||||
|
||||
public function createEntity($data)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,6 @@ use \Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class AuthToken extends Record
|
||||
{
|
||||
protected $internalFields = array('hash', 'token');
|
||||
protected $internalAttributeList = ['hash', 'token'];
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ use \Zend\Mail\Storage;
|
||||
|
||||
class EmailAccount extends Record
|
||||
{
|
||||
protected $internalFields = array('password');
|
||||
protected $internalAttributeList = ['password'];
|
||||
|
||||
protected $readOnlyFields = array('fetchData');
|
||||
protected $readOnlyAttributeList= ['fetchData'];
|
||||
|
||||
const PORTION_LIMIT = 10;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ use \Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
class InboundEmail extends \Espo\Services\Record
|
||||
{
|
||||
protected $internalFields = array('password');
|
||||
protected $internalAttributeList = ['password'];
|
||||
|
||||
private $campaignService = null;
|
||||
|
||||
|
||||
@@ -62,19 +62,19 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
private $streamService;
|
||||
|
||||
protected $notFilteringFields = array(); // TODO maybe remove it
|
||||
protected $notFilteringAttributeList =[]; // TODO maybe remove it
|
||||
|
||||
protected $internalFields = array();
|
||||
protected $internalAttributeList = [];
|
||||
|
||||
protected $readOnlyFields = array();
|
||||
protected $readOnlyAttributeList = [];
|
||||
|
||||
protected $linkSelectParams = array();
|
||||
protected $linkSelectParams = [];
|
||||
|
||||
protected $mergeLinkList = array();
|
||||
protected $mergeLinkList = [];
|
||||
|
||||
protected $exportSkipFieldList = array();
|
||||
protected $exportSkipAttributeList = [];
|
||||
|
||||
protected $exportAdditionalFieldList = array();
|
||||
protected $exportAdditionalAttributeList = [];
|
||||
|
||||
const FOLLOWERS_LIMIT = 4;
|
||||
|
||||
@@ -244,7 +244,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
if (!empty($defs['noJoin']) && !empty($defs['entity'])) {
|
||||
$nameField = $link . 'Name';
|
||||
$idField = $link . 'Id';
|
||||
if ($entity->hasField($nameField) && $entity->hasField($idField)) {
|
||||
if ($entity->hasAttribute($nameField) && $entity->hasAttribute($idField)) {
|
||||
$id = $entity->get($idField);
|
||||
}
|
||||
|
||||
@@ -308,8 +308,8 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
protected function isValid($entity)
|
||||
{
|
||||
$fieldDefs = $entity->getFields();
|
||||
if ($entity->hasField('name') && !empty($fieldDefs['name']['required'])) {
|
||||
$fieldDefs = $entity->getAttributes();
|
||||
if ($entity->hasAttribute('name') && !empty($fieldDefs['name']['required'])) {
|
||||
if (!$entity->get('name')) {
|
||||
return false;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
public function isPermittedAssignedUser(Entity $entity)
|
||||
{
|
||||
if (!$entity->hasField('assignedUserId')) {
|
||||
if (!$entity->hasAttribute('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$entity->hasField('teamsIds')) {
|
||||
if (!$entity->hasAttribute('teamsIds')) {
|
||||
return true;
|
||||
}
|
||||
$teamIds = $entity->get('teamsIds');
|
||||
@@ -424,12 +424,12 @@ class Record extends \Espo\Core\Services\Base
|
||||
return strip_tags($string, '<a><img><p><br><span><ol><ul><li><blockquote><pre><h1><h2><h3><h4><h5><table><tr><td><th><thead><tbody><i><b>');
|
||||
}
|
||||
|
||||
protected function filterInputField($field, $value)
|
||||
protected function filterInputAttribute($attribute, $value)
|
||||
{
|
||||
if (in_array($field, $this->notFilteringFields)) {
|
||||
if (in_array($attribute, $this->notFilteringAttributeList)) {
|
||||
return $value;
|
||||
}
|
||||
$methodName = 'filterInputField' . ucfirst($field);
|
||||
$methodName = 'filterInputAttribute' . ucfirst($attribute);
|
||||
if (method_exists($this, $methodName)) {
|
||||
$value = $this->$methodName($value);
|
||||
}
|
||||
@@ -438,24 +438,28 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
protected function filterInput(&$data)
|
||||
{
|
||||
foreach ($this->readOnlyFields as $field) {
|
||||
unset($data[$field]);
|
||||
foreach ($this->readOnlyAttributeList as $attribute) {
|
||||
unset($data[$attribute]);
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($data[$key])) {
|
||||
foreach ($data[$key] as $i => $v) {
|
||||
$data[$key][$i] = $this->filterInputField($i, $data[$key][$i]);
|
||||
$data[$key][$i] = $this->filterInputAttribute($i, $data[$key][$i]);
|
||||
}
|
||||
} else if ($data[$key] instanceof \stdClass) {
|
||||
$propertyList = get_object_vars($data[$key]);
|
||||
foreach ($propertyList as $property => $value) {
|
||||
$data[$key]->$property = $this->filterInputField($property, $data[$key]->$property);
|
||||
$data[$key]->$property = $this->filterInputAttribute($property, $data[$key]->$property);
|
||||
}
|
||||
} else {
|
||||
$data[$key] = $this->filterInputField($key, $data[$key]);
|
||||
$data[$key] = $this->filterInputAttribute($key, $data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getAcl()->getScopeForbiddenAttributeList($this->entityType, 'edit') as $attribute) {
|
||||
unset($data[$attribute]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleInput(&$data)
|
||||
@@ -802,12 +806,15 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
$count = 0;
|
||||
|
||||
$data = get_object_vars($attributes);
|
||||
$this->filterInput($data);
|
||||
|
||||
if (array_key_exists('ids', $params) && is_array($params['ids'])) {
|
||||
$ids = $params['ids'];
|
||||
foreach ($ids as $id) {
|
||||
$entity = $this->getEntity($id);
|
||||
if ($this->getAcl()->check($entity, 'edit')) {
|
||||
$entity->set(get_object_vars($attributes));
|
||||
$entity->set($data);
|
||||
if ($this->checkAssignment($entity)) {
|
||||
if ($repository->save($entity)) {
|
||||
$idsUpdated[] = $id;
|
||||
@@ -828,7 +835,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
if ($this->getAcl()->check($entity, 'edit')) {
|
||||
$entity->set(get_object_vars($attributes));
|
||||
$entity->set($data);
|
||||
if ($this->checkAssignment($entity)) {
|
||||
if ($repository->save($entity)) {
|
||||
$idsUpdated[] = $id;
|
||||
@@ -982,7 +989,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
'deleted',
|
||||
);
|
||||
|
||||
foreach ($this->exportSkipFieldList as $field) {
|
||||
foreach ($this->exportSkipAttributeList as $field) {
|
||||
$fieldListToSkip[] = $field;
|
||||
}
|
||||
|
||||
@@ -990,7 +997,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
foreach ($collection as $entity) {
|
||||
if (empty($fieldList)) {
|
||||
$fieldList = [];
|
||||
foreach ($entity->getFields() as $field => $defs) {
|
||||
foreach ($entity->getAttributes() as $field => $defs) {
|
||||
if (in_array($field, $fieldListToSkip)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1005,7 +1012,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($this->exportAdditionalFieldList as $field) {
|
||||
foreach ($this->exportAdditionalAttributeList as $field) {
|
||||
$fieldList[] = $field;
|
||||
}
|
||||
}
|
||||
@@ -1013,7 +1020,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
$this->loadAdditionalFieldsForExport($entity);
|
||||
$row = array();
|
||||
foreach ($fieldList as $field) {
|
||||
$value = $this->getFieldFromEntityForExport($entity, $field);
|
||||
$value = $this->getAttributeFromEntityForExport($entity, $field);
|
||||
$row[$field] = $value;
|
||||
}
|
||||
$arr[] = $row;
|
||||
@@ -1050,14 +1057,14 @@ class Record extends \Espo\Core\Services\Base
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
protected function getFieldFromEntityForExport(Entity $entity, $field)
|
||||
protected function getAttributeFromEntityForExport(Entity $entity, $field)
|
||||
{
|
||||
$methodName = 'getField' . ucfirst($field). 'FromEntityForExport';
|
||||
$methodName = 'getAttribute' . ucfirst($field). 'FromEntityForExport';
|
||||
if (method_exists($this, $methodName)) {
|
||||
return $this->$methodName($entity);
|
||||
}
|
||||
|
||||
$defs = $entity->getFields();
|
||||
$defs = $entity->getAttributes();
|
||||
if (!empty($defs[$field]) && !empty($defs[$field]['type'])) {
|
||||
$type = $defs[$field]['type'];
|
||||
switch ($type) {
|
||||
@@ -1079,9 +1086,12 @@ class Record extends \Espo\Core\Services\Base
|
||||
|
||||
public function prepareEntityForOutput(Entity $entity)
|
||||
{
|
||||
foreach ($this->internalFields as $field) {
|
||||
foreach ($this->internalAttributeList as $field) {
|
||||
$entity->clear($field);
|
||||
}
|
||||
foreach ($this->getAcl()->getScopeForbiddenAttributeList($entity->getEntityType(), 'read') as $attribute) {
|
||||
$entity->clear($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
public function merge($id, array $sourceIds = array())
|
||||
@@ -1137,7 +1147,6 @@ class Record extends \Espo\Core\Services\Base
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($sourceList as $source) {
|
||||
$this->getEntityManager()->removeEntity($source);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class User extends Record
|
||||
$this->dependencies[] = 'container';
|
||||
}
|
||||
|
||||
protected $internalFields = array('password');
|
||||
protected $internalAttributeList = ['password'];
|
||||
|
||||
protected function getMailSender()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user