diff --git a/application/Espo/Acl/Email.php b/application/Espo/Acl/Email.php index 1bcc04b1c9..853316ef8d 100644 --- a/application/Espo/Acl/Email.php +++ b/application/Espo/Acl/Email.php @@ -34,6 +34,7 @@ use \Espo\ORM\Entity; class Email extends \Espo\Core\Acl\Base { + protected $ownerUserIdAttribute = 'usersIds'; public function checkEntityRead(EntityUser $user, Entity $entity, $data) { @@ -118,4 +119,3 @@ class Email extends \Espo\Core\Acl\Base return false; } } - diff --git a/application/Espo/AclPortal/Email.php b/application/Espo/AclPortal/Email.php index 3ad2d602a9..7e0cea31d3 100644 --- a/application/Espo/AclPortal/Email.php +++ b/application/Espo/AclPortal/Email.php @@ -34,6 +34,7 @@ use \Espo\ORM\Entity; class Email extends \Espo\Core\AclPortal\Base { + protected $ownerUserIdAttribute = 'usersIds'; public function checkEntityRead(EntityUser $user, Entity $entity, $data) { diff --git a/application/Espo/Core/Acl/Base.php b/application/Espo/Core/Acl/Base.php index 18c4561dd9..bc41864bae 100644 --- a/application/Espo/Core/Acl/Base.php +++ b/application/Espo/Core/Acl/Base.php @@ -46,6 +46,8 @@ class Base implements Injectable protected $injections = array(); + protected $ownerUserIdAttribute = null; + public function inject($name, $object) { $this->injections[$name] = $object; @@ -281,5 +283,23 @@ class Base implements Injectable return false; } -} + public function getOwnerUserIdAttribute(Entity $entity) + { + if ($this->ownerUserIdAttribute) { + return $this->ownerUserIdAttribute; + } + + if ($entity->hasLinkMultipleField('assignedUsers')) { + return 'assignedUsersIds'; + } + + if ($entity->hasAttribute('assignedUserId')) { + return 'assignedUserId'; + } + + if ($entity->hasAttribute('createdById')) { + return 'createdById'; + } + } +} diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index 4d3c7f67e3..6ece88af05 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -247,6 +247,14 @@ class Container ); } + protected function loadInternalAclManager() + { + $className = $this->getServiceClassName('acl', '\\Espo\\Core\\AclManager'); + return new $className( + $this->get('container') + ); + } + protected function loadAcl() { $className = $this->getServiceClassName('acl', '\\Espo\\Core\\Acl'); diff --git a/application/Espo/Hooks/Common/Stream.php b/application/Espo/Hooks/Common/Stream.php index cb9cec8a28..20403526b7 100644 --- a/application/Espo/Hooks/Common/Stream.php +++ b/application/Espo/Hooks/Common/Stream.php @@ -178,7 +178,7 @@ class Stream extends \Espo\Core\Hooks\Base return $userIdList; } - public function afterSave(Entity $entity, array $options = array()) + public function afterSave(Entity $entity, array $options = []) { $entityType = $entity->getEntityType(); diff --git a/application/Espo/Hooks/Common/StreamNotesAcl.php b/application/Espo/Hooks/Common/StreamNotesAcl.php new file mode 100644 index 0000000000..fd1cf88bd1 --- /dev/null +++ b/application/Espo/Hooks/Common/StreamNotesAcl.php @@ -0,0 +1,124 @@ +addDependency('serviceFactory'); + $this->addDependency('aclManager'); + } + + protected function getServiceFactory() + { + return $this->getInjection('serviceFactory'); + } + + protected function getAclManager() + { + return $this->getInjection('aclManager'); + } + + public function afterSave(Entity $entity, array $options = []) + { + if (!empty($options['noStream'])) return; + if (!empty($options['silent'])) return; + + if ($entity->isNew()) return; + + $entityType = $entity->getEntityType(); + + if (in_array($entityType, ['Note', 'User', 'Team', 'Role', 'Portal', 'PortalRole'])) return; + + if (!$this->getMetadata()->get(['scopes', $entityType, 'acl'])) return; + if (!$this->getMetadata()->get(['scopes', $entityType, 'object'])) return; + + $ownerUserIdAttribute = $this->getAclManager()->getImplementation($entityType)->getOwnerUserIdAttribute($entity); + + $usersAttributeIsChanged = false; + $teamsAttributeIsChanged = false; + + if ($ownerUserIdAttribute) { + if ($entity->isAttributeChanged($ownerUserIdAttribute)) { + $usersAttributeIsChanged = true; + if ($entity->getAttributeParam($ownerUserIdAttribute, 'isLinkMultipleIdList')) { + $userIdList = $entity->get($ownerUserIdAttribute); + } else { + $userId = $entity->get($ownerUserIdAttribute); + if ($userId) { + $userIdList = [$userId]; + } else { + $userIdList = []; + } + } + } + } + + if ($entity->hasLinkMultipleField('teams') && $entity->isAttributeChanged('teamsIds')) { + $teamsAttributeIsChanged = true; + $teamIdList = $entity->get('teamsIds'); + } + + if ($usersAttributeIsChanged || $teamsAttributeIsChanged) { + $noteList = $this->getEntityManager()->getRepository('Note')->where([ + 'OR' => [ + [ + 'relatedId' => $entity->id, + 'relatedType' => $entityType + ], + [ + 'parentId' => $entity->id, + 'parentType' => $entityType, + 'superParentId!=' => null, + 'relatedId' => null + ] + ] + ])->find(); + + foreach ($noteList as $note) { + if ($teamsAttributeIsChanged) { + $note->set('teamsIds', $teamIdList); + } + if ($usersAttributeIsChanged) { + $note->set('usersIds', $userIdList); + } + $this->getEntityManager()->saveEntity($note); + } + } + } +} diff --git a/application/Espo/Hooks/Note/Notifications.php b/application/Espo/Hooks/Note/Notifications.php index 82f16b3d0f..245128281f 100644 --- a/application/Espo/Hooks/Note/Notifications.php +++ b/application/Espo/Hooks/Note/Notifications.php @@ -40,6 +40,7 @@ class Notifications extends \Espo\Core\Hooks\Base protected function init() { $this->addDependency('serviceFactory'); + $this->addDependency('container'); } protected function getServiceFactory() @@ -47,6 +48,16 @@ class Notifications extends \Espo\Core\Hooks\Base return $this->getInjection('serviceFactory'); } + protected function getInternalAclManager() + { + return $this->getInjection('container')->get('internalAclManager'); + } + + protected function getPortalAclManager() + { + return $this->getInjection('container')->get('portalAclManager'); + } + protected function getMentionedUserIdList($entity) { $mentionedUserList = array(); @@ -60,7 +71,7 @@ class Notifications extends \Espo\Core\Hooks\Base return $mentionedUserList; } - protected function getSubscriberIdList($parentType, $parentId, $isInternal = false) + protected function getSubscriberList($parentType, $parentId, $isInternal = false) { $pdo = $this->getEntityManager()->getPDO(); @@ -80,15 +91,14 @@ class Notifications extends \Espo\Core\Hooks\Base user.is_portal_user = 0 "; } - $sth = $pdo->prepare($sql); - $sth->execute(); - $userIdList = []; - while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) { - if ($this->getUser()->id != $row['userId']) { - $userIdList[] = $row['userId']; - } - } - return $userIdList; + + $userList = $this->getEntityManager()->getRepository('User')->where([ + 'isActive' => true + ])->select(['id', 'isPortalUser', 'isAdmin'])->find([ + 'customWhere' => "AND user.id IN (".$sql.")" + ]); + + return $userList; } public function afterSave(Entity $entity) @@ -99,13 +109,77 @@ class Notifications extends \Espo\Core\Hooks\Base $superParentType = $entity->get('superParentType'); $superParentId = $entity->get('superParentId'); - $userIdList = []; + $notifyUserIdList = []; if ($parentType && $parentId) { - $userIdList = array_merge($userIdList, $this->getSubscriberIdList($parentType, $parentId, $entity->get('isInternal'))); - if ($superParentType && $superParentId) { - $userIdList = array_merge($userIdList, $this->getSubscriberIdList($superParentType, $superParentId, $entity->get('isInternal'))); + $userList = $this->getSubscriberList($parentType, $parentId, $entity->get('isInternal')); + $userIdMetList = []; + foreach ($userList as $user) { + $userIdMetList[] = $user->id; } + if ($superParentType && $superParentId) { + $additionalUserList = $this->getSubscriberList($superParentType, $superParentId, $entity->get('isInternal')); + foreach ($additionalUserList as $user) { + if ($user->isPortal()) continue; + if (in_array($user->id, $userIdMetList)) continue; + $userIdMetList[] = $user->id; + $userList[] = $user; + } + } + + if ($entity->get('relatedType')) { + $targetType = $entity->get('relatedType'); + } else { + $targetType = $parentType; + } + + $teamIdList = $entity->getLinkMultipleIdList('teams'); + $userIdList = $entity->getLinkMultipleIdList('users'); + + foreach ($userList as $user) { + if ($user->isAdmin()) { + $notifyUserIdList[] = $user->id; + continue; + } + + if ($user->isPortal()) { + if ($entity->get('relatedType')) { + continue; + } else { + $notifyUserIdList[] = $user->id; + } + continue; + } + + $level = $this->getInternalAclManager()->getLevel($user, $targetType, 'read'); + + if ($level === 'all') { + $notifyUserIdList[] = $user->id; + continue; + } else if ($level === 'team') { + if (in_array($user->id, $userIdList)) { + $notifyUserIdList[] = $user->id; + continue; + } + + if (!empty($teamIdList)) { + $userTeamIdList = $user->getLinkMultipleIdList('teams'); + foreach ($teamIdList as $teamId) { + if (in_array($teamId, $userTeamIdList)) { + $notifyUserIdList[] = $user->id; + break; + } + } + } + continue; + } else if ($level === 'own') { + if (in_array($user->id, $userIdList)) { + $notifyUserIdList[] = $user->id; + continue; + } + } + } + } else { $targetType = $entity->get('targetType'); if ($targetType === 'users') { @@ -113,8 +187,8 @@ class Notifications extends \Espo\Core\Hooks\Base if (is_array($targetUserIdList)) { foreach ($targetUserIdList as $userId) { if ($userId === $this->getUser()->id) continue; - if (in_array($userId, $userIdList)) continue; - $userIdList[] = $userId; + if (in_array($userId, $notifyUserIdList)) continue; + $notifyUserIdList[] = $userId; } } } else if ($targetType === 'teams') { @@ -130,8 +204,8 @@ class Notifications extends \Espo\Core\Hooks\Base )); foreach ($targetUserList as $user) { if ($user->id === $this->getUser()->id) continue; - if (in_array($user->id, $userIdList)) continue; - $userIdList[] = $user->id; + if (in_array($user->id, $notifyUserIdList)) continue; + $notifyUserIdList[] = $user->id; } } } @@ -148,8 +222,8 @@ class Notifications extends \Espo\Core\Hooks\Base )); foreach ($targetUserList as $user) { if ($user->id === $this->getUser()->id) continue; - if (in_array($user->id, $userIdList)) continue; - $userIdList[] = $user->id; + if (in_array($user->id, $notifyUserIdList)) continue; + $notifyUserIdList[] = $user->id; } } } @@ -162,22 +236,22 @@ class Notifications extends \Espo\Core\Hooks\Base )); foreach ($targetUserList as $user) { if ($user->id === $this->getUser()->id) continue; - $userIdList[] = $user->id; + $notifyUserIdList[] = $user->id; } } } - $userIdList = array_unique($userIdList); + $notifyUserIdList = array_unique($notifyUserIdList); - foreach ($userIdList as $i => $userId) { + foreach ($notifyUserIdList as $i => $userId) { if ($entity->isUserIdNotified($userId)) { - unset($userIdList[$i]); + unset($notifyUserIdList[$i]); } } - $userIdList = array_values($userIdList); + $notifyUserIdList = array_values($notifyUserIdList); - if (!empty($userIdList)) { - $this->getNotificationService()->notifyAboutNote($userIdList, $entity); + if (!empty($notifyUserIdList)) { + $this->getNotificationService()->notifyAboutNote($notifyUserIdList, $entity); } } } @@ -190,4 +264,3 @@ class Notifications extends \Espo\Core\Hooks\Base return $this->notificationService; } } - diff --git a/application/Espo/Services/Stream.php b/application/Espo/Services/Stream.php index f4327a28eb..f5510bf4ac 100644 --- a/application/Espo/Services/Stream.php +++ b/application/Espo/Services/Stream.php @@ -314,6 +314,8 @@ class Stream extends \Espo\Core\Services\Base } } + $teamIdList = $user->getTeamIdList(); + $pdo = $this->getEntityManager()->getPDO(); $select = [ @@ -321,9 +323,12 @@ class Stream extends \Espo\Core\Services\Base 'targetType', 'createdAt', 'createdById', 'createdByName', 'isGlobal', 'isInternal', 'createdByGender' ]; + $onlyTeamEntityTypeList = $this->getOnlyTeamEntityTypeList($user); + $onlyOwnEntityTypeList = $this->getOnlyOwnEntityTypeList($user); + $selectParamsList = []; - $selectParamsSubscription = array( + $selectParamsSubscription = [ 'select' => $select, 'leftJoins' => ['createdBy'], 'customJoin' => " @@ -334,22 +339,78 @@ class Stream extends \Espo\Core\Services\Base note.parent_id = subscription.entity_id ) ) AND - subscription.user_id = ".$pdo->quote($user->id) . " + subscription.user_id = ". $pdo->quote($user->id) ." ", - 'whereClause' => array(), + 'whereClause' => [], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; - if ($user->get('isPortalUser')) { - $selectParamsSubscription['whereClause'][] = array( + if ($user->isPortal()) { + $selectParamsSubscription['whereClause'][] = [ 'isInternal' => false - ); + ]; + + $notAllEntityTypeList = $this->getNotAllEntityTypeList($user); + + $selectParamsSubscription['whereClause'][] = [ + 'OR' => [ + [ + 'relatedId' => null + ], + [ + 'relatedId!=' => null, + 'relatedType!=' => $notAllEntityTypeList + ] + ] + ]; + + $selectParamsList[] = $selectParamsSubscription; + } else { + $selectParamsSubscriptionRest = $selectParamsSubscription; + $selectParamsSubscriptionRest['whereClause'][] = [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType!=' => array_merge($onlyTeamEntityTypeList, $onlyOwnEntityTypeList) + ], + [ + 'relatedId=' => null + ] + ] + ]; + $selectParamsList[] = $selectParamsSubscriptionRest; + + if (count($onlyTeamEntityTypeList)) { + $selectParamsSubscriptionTeam = $selectParamsSubscription; + $selectParamsSubscriptionTeam['distinct'] = true; + $selectParamsSubscriptionTeam['leftJoins'][] = 'teams'; + $selectParamsSubscriptionTeam['whereClause'][] = [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyTeamEntityTypeList + ], + 'teamsMiddle.teamId' => $teamIdList + ]; + $selectParamsList[] = $selectParamsSubscriptionTeam; + } + + if (count($onlyOwnEntityTypeList)) { + $selectParamsSubscriptionOwn = $selectParamsSubscription; + $selectParamsSubscriptionOwn['distinct'] = true; + $selectParamsSubscriptionOwn['leftJoins'][] = 'users'; + $selectParamsSubscriptionOwn['whereClause'][] = [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyOwnEntityTypeList + ], + 'usersMiddle.userId' => $user->id + ]; + $selectParamsList[] = $selectParamsSubscriptionOwn; + } } - $selectParamsList[] = $selectParamsSubscription; - - $selectParamsSubscriptionSuper = array( + $selectParamsSubscriptionSuper = [ 'select' => $select, 'leftJoins' => ['createdBy'], 'customJoin' => " @@ -368,58 +429,109 @@ class Stream extends \Espo\Core\Services\Base note.parent_type <> note.super_parent_type ) ', - 'whereClause' => array(), + 'whereClause' => [], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; - if ($user->get('isPortalUser')) { - $selectParamsSubscriptionSuper['whereClause'][] = array( - 'isInternal' => false - ); + if ($user->isPortal()) { + + } else { + $selectParamsSubscriptionRest = $selectParamsSubscriptionSuper; + $selectParamsSubscriptionRest['whereClause'][] = [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType!=' => array_merge($onlyTeamEntityTypeList, $onlyOwnEntityTypeList) + ], + [ + 'relatedId=' => null, + 'parentType!=' => array_merge($onlyTeamEntityTypeList, $onlyOwnEntityTypeList) + ] + ] + ]; + $selectParamsList[] = $selectParamsSubscriptionRest; + + if (count($onlyTeamEntityTypeList)) { + $selectParamsSubscriptionTeam = $selectParamsSubscriptionSuper; + $selectParamsSubscriptionTeam['distinct'] = true; + $selectParamsSubscriptionTeam['leftJoins'][] = 'teams'; + $selectParamsSubscriptionTeam['whereClause'][] = [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyTeamEntityTypeList + ], + [ + 'relatedId=' => null, + 'parentType=' => $onlyTeamEntityTypeList + ] + ], + 'teamsMiddle.teamId' => $teamIdList + ]; + $selectParamsList[] = $selectParamsSubscriptionTeam; + } + + if (count($onlyOwnEntityTypeList)) { + $selectParamsSubscriptionOwn = $selectParamsSubscriptionSuper; + $selectParamsSubscriptionOwn['distinct'] = true; + $selectParamsSubscriptionOwn['leftJoins'][] = 'users'; + $selectParamsSubscriptionOwn['whereClause'][] = [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyOwnEntityTypeList + ], + [ + 'relatedId=' => null, + 'parentType=' => $onlyOwnEntityTypeList + ] + ], + 'usersMiddle.userId' => $user->id + ]; + $selectParamsList[] = $selectParamsSubscriptionOwn; + } } - $selectParamsList[] = $selectParamsSubscriptionSuper; - - $selectParamsList[] = array( + $selectParamsList[] = [ 'select' => $select, 'leftJoins' => ['createdBy'], - 'whereClause' => array( + 'whereClause' => [ 'createdById' => $user->id, 'parentId' => null, 'type' => 'Post', 'isGlobal' => false - ), + ], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; - $selectParamsList[] = array( + $selectParamsList[] = [ 'select' => $select, 'leftJoins' => ['users', 'createdBy'], - 'whereClause' => array( + 'whereClause' => [ 'createdById!=' => $user->id, 'usersMiddle.userId' => $user->id, 'parentId' => null, 'type' => 'Post', 'isGlobal' => false - ), + ], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; if (!$user->get('isPortalUser') || $user->get('isAdmin')) { - $selectParamsList[] = array( + $selectParamsList[] = [ 'select' => $select, 'leftJoins' => ['createdBy'], - 'whereClause' => array( + 'whereClause' => [ 'parentId' => null, 'type' => 'Post', 'isGlobal' => true - ), + ], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; } if ($user->get('isPortalUser')) { @@ -429,42 +541,37 @@ class Stream extends \Espo\Core\Services\Base $portalIdQuotedList[] = $pdo->quote($portalId); } if (!empty($portalIdQuotedList)) { - $selectParamsList[] = array( + $selectParamsList[] = [ 'select' => $select, 'leftJoins' => ['portals', 'createdBy'], - 'whereClause' => array( + 'whereClause' => [ 'parentId' => null, 'portalsMiddle.portalId' => $portalIdList, 'type' => 'Post', 'isGlobal' => false - ), + ], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; } } - $teamIdList = $user->getTeamIdList(); - $teamIdQuotedList = []; - foreach ($teamIdList as $teamId) { - $teamIdQuotedList[] = $pdo->quote($teamId); - } if (!empty($teamIdList)) { - $selectParamsList[] = array( + $selectParamsList[] = [ 'select' => $select, 'leftJoins' => ['teams', 'createdBy'], - 'whereClause' => array( + 'whereClause' => [ 'parentId' => null, 'teamsMiddle.teamId' => $teamIdList, 'type' => 'Post', 'isGlobal' => false - ), + ], 'orderBy' => 'number', 'order' => 'DESC' - ); + ]; } - $whereClause = array(); + $whereClause = []; if (!empty($params['after'])) { $whereClause[]['createdAt>'] = $params['after']; } @@ -480,32 +587,32 @@ class Stream extends \Espo\Core\Services\Base } } - $ignoreScopeList = $this->getIgnoreScopeList(); + $ignoreScopeList = $this->getIgnoreScopeList($user); if (!empty($ignoreScopeList)) { - $whereClause[] = array( - 'OR' => array( + $whereClause[] = [ + 'OR' => [ 'relatedType' => null, 'relatedType!=' => $ignoreScopeList - ) - ); - $whereClause[] = array( - 'OR' => array( + ] + ]; + $whereClause[] = [ + 'OR' => [ 'parentType' => null, 'parentType!=' => $ignoreScopeList - ) - ); + ] + ]; if (in_array('Email', $ignoreScopeList)) { - $whereClause[] = array( + $whereClause[] = [ 'type!=' => ['EmailReceived', 'EmailSent'] - ); + ]; } } $sqlPartList = []; foreach ($selectParamsList as $i => $selectParams) { if (empty($selectParams['whereClause'])) { - $selectParams['whereClause'] = array(); + $selectParams['whereClause'] = []; } $selectParams['whereClause'][] = $whereClause; $sqlPartList[] = "(\n" . $this->getEntityManager()->getQuery()->createSelectQuery('Note', $selectParams) . "\n)"; @@ -560,7 +667,7 @@ class Stream extends \Espo\Core\Services\Base ); } - public function find($scope, $id, $params = array()) + public function find($scope, $id, $params = []) { if ($scope === 'User') { if (empty($id)) { @@ -570,6 +677,9 @@ class Stream extends \Espo\Core\Services\Base } $entity = $this->getEntityManager()->getEntity($scope, $id); + $onlyTeamEntityTypeList = $this->getOnlyTeamEntityTypeList($this->getUser()); + $onlyOwnEntityTypeList = $this->getOnlyOwnEntityTypeList($this->getUser()); + if (empty($entity)) { throw new NotFound(); } @@ -578,18 +688,101 @@ class Stream extends \Espo\Core\Services\Base throw new Forbidden(); } - $where = array( - 'OR' => array( - array( + $selectParams = [ + 'offset' => $params['offset'], + 'limit' => $params['maxSize'], + 'orderBy' => 'number', + 'order' => 'DESC' + ]; + + $where = [ + 'OR' => [ + [ 'parentType' => $scope, 'parentId' => $id - ), - array( + ], + [ 'superParentType' => $scope, 'superParentId' => $id - ) - ) - ); + ] + ] + ]; + + if ($this->getUser()->isPortal()) { + $where = [ + 'OR' => [ + [ + 'parentType' => $scope, + 'parentId' => $id + ] + ] + ]; + $notAllEntityTypeList = $this->getNotAllEntityTypeList($this->getUser()); + $where[] = [ + 'OR' => [ + [ + 'relatedId' => null + ], + [ + 'relatedId!=' => null, + 'relatedType!=' => $notAllEntityTypeList + ] + ] + ]; + } else { + if (count($onlyTeamEntityTypeList) || count($onlyOwnEntityTypeList)) { + $selectParams['leftJoins'] = ['teams', 'users']; + $selectParams['distinct'] = true; + $where[] = [ + 'OR' => [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType!=' => array_merge($onlyTeamEntityTypeList, $onlyOwnEntityTypeList) + ], + [ + 'relatedId=' => null, + 'superParentId' => $id, + 'superParentType' => $scope, + 'parentId!=' => null, + 'parentType!=' => array_merge($onlyTeamEntityTypeList, $onlyOwnEntityTypeList) + ], + [ + 'relatedId=' => null, + 'parentType=' => $scope, + 'parentId=' => $id + ] + ], + [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyTeamEntityTypeList + ], + [ + 'relatedId=' => null, + 'parentType=' => $onlyTeamEntityTypeList + ] + ], + 'teamsMiddle.teamId' => $this->getUser()->getTeamIdList() + ], + [ + 'OR' => [ + [ + 'relatedId!=' => null, + 'relatedType=' => $onlyOwnEntityTypeList + ], + [ + 'relatedId=' => null, + 'parentType=' => $onlyOwnEntityTypeList + ] + ], + 'usersMiddle.userId' => $this->getUser()->id + ] + ] + ]; + } + } if (!empty($params['after'])) { $where['createdAt>'] = $params['after']; @@ -606,41 +799,36 @@ class Stream extends \Espo\Core\Services\Base } } - $ignoreScopeList = $this->getIgnoreScopeList(); + $ignoreScopeList = $this->getIgnoreScopeList($this->getUser()); if (!empty($ignoreScopeList)) { - $where[] = array( - 'OR' => array( + $where[] = [ + 'OR' => [ 'relatedType' => null, 'relatedType!=' => $ignoreScopeList - ) - ); - $where[] = array( - 'OR' => array( + ] + ]; + $where[] = [ + 'OR' => [ 'parentType' => null, 'parentType!=' => $ignoreScopeList - ) - ); + ] + ]; if (in_array('Email', $ignoreScopeList)) { - $where[] = array( + $where[] = [ 'type!=' => ['EmailReceived', 'EmailSent'] - ); + ]; } } if ($this->getUser()->get('isPortalUser')) { - $where[] = array( + $where[] = [ 'isInternal' => false - ); + ]; } - $collection = $this->getEntityManager()->getRepository('Note')->find(array( - 'whereClause' => $where, - 'offset' => $params['offset'], - 'limit' => $params['maxSize'], - 'orderBy' => 'number', - 'order' => 'DESC' - )); + $selectParams['whereClause'] = $where; + $collection = $this->getEntityManager()->getRepository('Note')->find($selectParams); foreach ($collection as $e) { if ($e->get('type') == 'Post' || $e->get('type') == 'EmailReceived') { @@ -658,13 +846,15 @@ class Stream extends \Espo\Core\Services\Base if ($e->get('relatedId') && $e->get('relatedType')) { $e->loadParentNameField('related'); } - } unset($where['createdAt>']); - $count = $this->getEntityManager()->getRepository('Note')->count(array( - 'whereClause' => $where, - )); + + unset($selectParams['offset']); + unset($selectParams['limit']); + + $selectParams['where'] = $where; + $count = $this->getEntityManager()->getRepository('Note')->count($selectParams); return array( 'total' => $count, @@ -680,6 +870,27 @@ class Stream extends \Espo\Core\Services\Base } } + protected function processNoteTeamsUsers(Entity $note, Entity $entity) + { + if ($entity->hasLinkMultipleField('teams') && $entity->has('teamsIds')) { + $teamIdList = $entity->get('teamsIds'); + if (!empty($teamIdList)) { + $note->set('teamsIds', $teamIdList); + } + } + + $ownerUserIdAttribute = $this->getAclManager()->getImplementation($entity->getEntityType())->getOwnerUserIdAttribute($entity); + if ($ownerUserIdAttribute && $entity->get($ownerUserIdAttribute)) { + if ($entity->getAttributeParam($ownerUserIdAttribute, 'isLinkMultipleIdList')) { + $userIdList = $entity->get($ownerUserIdAttribute); + } else { + $userId = $entity->get($ownerUserIdAttribute); + $userIdList = [$userId]; + } + $note->set('usersIds', $userIdList); + } + } + public function noteEmailReceived(Entity $entity, Entity $email, $isInitial = false) { $entityType = $entity->getEntityType(); @@ -702,6 +913,8 @@ class Stream extends \Espo\Core\Services\Base $note->set('relatedId', $email->id); $note->set('relatedType', 'Email'); + $this->processNoteTeamsUsers($note, $email); + if ($email->get('accountId')) { $note->set('superParentId', $email->get('accountId')); $note->set('superParentType', 'Account'); @@ -751,6 +964,8 @@ class Stream extends \Espo\Core\Services\Base $note->set('relatedId', $email->id); $note->set('relatedType', 'Email'); + $this->processNoteTeamsUsers($note, $email); + if ($email->get('accountId')) { $note->set('superParentId', $email->get('accountId')); $note->set('superParentType', 'Account'); @@ -805,9 +1020,11 @@ class Stream extends \Espo\Core\Services\Base if ($entity->has('accountId') && $entity->get('accountId')) { $note->set('superParentId', $entity->get('accountId')); $note->set('superParentType', 'Account'); + + $this->processNoteTeamsUsers($note, $entity); } - $data = array(); + $data = []; if ($entity->get('assignedUserId')) { if (!$entity->has('assignedUserName')) { @@ -854,10 +1071,12 @@ class Stream extends \Espo\Core\Services\Base $note->set('type', 'CreateRelated'); $note->set('parentId', $parentId); $note->set('parentType', $parentType); - $note->set(array( + $note->set([ 'relatedType' => $entityType, - 'relatedId' => $entity->id, - )); + 'relatedId' => $entity->id + ]); + + $this->processNoteTeamsUsers($note, $entity); if ($entity->has('accountId') && $entity->get('accountId')) { $note->set('superParentId', $entity->get('accountId')); @@ -878,20 +1097,22 @@ class Stream extends \Espo\Core\Services\Base if ($entity->has('accountId') && $entity->get('accountId')) { $note->set('superParentId', $entity->get('accountId')); $note->set('superParentType', 'Account'); + + $this->processNoteTeamsUsers($note, $entity); } if ($entity->get('assignedUserId')) { if (!$entity->has('assignedUserName')) { $this->loadAssignedUserName($entity); } - $note->set('data', array( + $note->set('data', [ 'assignedUserId' => $entity->get('assignedUserId'), 'assignedUserName' => $entity->get('assignedUserName'), - )); + ]); } else { - $note->set('data', array( + $note->set('data', [ 'assignedUserId' => null - )); + ]); } $this->getEntityManager()->saveEntity($note); @@ -908,6 +1129,8 @@ class Stream extends \Espo\Core\Services\Base if ($entity->has('accountId') && $entity->get('accountId')) { $note->set('superParentId', $entity->get('accountId')); $note->set('superParentType', 'Account'); + + $this->processNoteTeamsUsers($note, $entity); } $style = 'default'; @@ -1009,13 +1232,13 @@ class Stream extends \Espo\Core\Services\Base $note->set('parentId', $entity->id); $note->set('parentType', $entity->getEntityType()); - $note->set('data', array( + $note->set('data', [ 'fields' => $updatedFieldList, - 'attributes' => array( + 'attributes' => [ 'was' => $was, 'became' => $became - ) - )); + ] + ]); $this->getEntityManager()->saveEntity($note); } @@ -1095,14 +1318,76 @@ class Stream extends \Espo\Core\Services\Base } - protected function getIgnoreScopeList() + protected function getOnlyTeamEntityTypeList(\Espo\Entities\User $user) + { + if ($user->isPortal()) return []; + + $list = []; + $scopes = $this->getMetadata()->get('scopes', []); + foreach ($scopes as $scope => $item) { + if ($scope === 'User') continue; + if (empty($item['entity'])) continue; + if (empty($item['object'])) continue; + if ( + $this->getAclManager()->getLevel($user, $scope, 'read') === 'team' + ) { + $list[] = $scope; + } + } + + return $list; + } + + protected function getOnlyOwnEntityTypeList(\Espo\Entities\User $user) + { + if ($user->isPortal()) return []; + + $list = []; + $scopes = $this->getMetadata()->get('scopes', []); + foreach ($scopes as $scope => $item) { + if ($scope === 'User') continue; + if (empty($item['entity'])) continue; + if (empty($item['object'])) continue; + if ( + $this->getAclManager()->getLevel($user, $scope, 'read') === 'own' + ) { + $list[] = $scope; + } + } + return $list; + } + + protected function getNotAllEntityTypeList(\Espo\Entities\User $user) + { + if (!$user->isPortal()) return []; + + $list = []; + $scopes = $this->getMetadata()->get('scopes', []); + foreach ($scopes as $scope => $item) { + if ($scope === 'User') continue; + if (empty($item['entity'])) continue; + if (empty($item['object'])) continue; + if ( + $this->getAclManager()->getLevel($user, $scope, 'read') !== 'all' + ) { + $list[] = $scope; + } + } + return $list; + } + + protected function getIgnoreScopeList(\Espo\Entities\User $user) { $ignoreScopeList = []; - $scopes = $this->getMetadata()->get('scopes', array()); - foreach ($scopes as $scope => $d) { - if (empty($d['entity']) || !$d['entity']) continue; - if (empty($d['object']) || !$d['object']) continue; - if (!$this->getAcl()->checkScope($scope, 'read') || !$this->getAcl()->checkScope($scope, 'stream')) { + $scopes = $this->getMetadata()->get('scopes', []); + foreach ($scopes as $scope => $item) { + if (empty($item['entity'])) continue; + if (empty($item['object'])) continue; + if ( + !$this->getAclManager()->checkScope($user, $scope, 'read') + || + !$this->getAclManager()->checkScope($user, $scope, 'stream') + ) { $ignoreScopeList[] = $scope; } }