follow as collaborator

This commit is contained in:
Yuri Kuznetsov
2025-11-27 20:53:00 +02:00
parent 3ac3f04e3d
commit 5efcd6bd8a
6 changed files with 110 additions and 5 deletions
@@ -35,6 +35,7 @@
"followEntityOnStreamPost": "Auto-follow record after posting in Stream",
"followCreatedEntities": "Auto-follow created records",
"followCreatedEntityTypeList": "Auto-follow created records of specific entity types",
"followAsCollaborator": "Auto-follow when added as a collaborator",
"emailUseExternalClient": "Use an external email client",
"textSearchStoringDisabled": "Disable text filter storing",
"calendarSlotDuration": "Calendar Slot Duration",
@@ -67,6 +67,10 @@
[
{"name": "followCreatedEntities"},
{"name": "followCreatedEntityTypeList"}
],
[
{"name": "followAsCollaborator"},
false
]
]
},
@@ -72,7 +72,8 @@
"assignmentNotificationsIgnoreEntityTypeList": false,
"assignmentEmailNotificationsIgnoreEntityTypeList": false,
"dashletsOptions": false,
"dashboardLayout": false
"dashboardLayout": false,
"followAsCollaborator": false
},
"Call": {
"reminders": false
@@ -211,6 +211,10 @@
"default": [],
"tooltip": true
},
"followAsCollaborator": {
"type": "bool",
"default": true
},
"emailUseExternalClient": {
"type": "bool",
"default": false
@@ -29,6 +29,7 @@
namespace Espo\Tools\Stream;
use Espo\Core\Acl\AssignmentChecker\Helper;
use Espo\Core\Field\DateTime;
use Espo\Core\Name\Field;
use Espo\Core\ORM\Repository\Option\SaveContext;
@@ -52,6 +53,7 @@ use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Tools\Stream\Service as Service;
use Espo\Tools\Stream\Jobs\AutoFollow as AutoFollowJob;
use Espo\Tools\Stream\Jobs\ControlFollowers as ControlFollowersJob;
use Espo\Tools\User\PreferencesProvider;
/**
* Handles operations with entities.
@@ -71,7 +73,9 @@ class HookProcessor
private Service $service,
private User $user,
private Preferences $preferences,
private JobSchedulerFactory $jobSchedulerFactory
private JobSchedulerFactory $jobSchedulerFactory,
private Helper $helper,
private PreferencesProvider $preferencesProvider,
) {}
public function beforeSave(Entity $entity, SaveOptions $options): void
@@ -346,11 +350,11 @@ class HookProcessor
if ($entity->isNew()) {
$this->afterSaveStreamNew($entity, $options);
return;
} else {
$this->afterSaveStreamNotNew($entity, $options);
}
$this->afterSaveStreamNotNew($entity, $options);
$this->followAddedCollaborators($entity);
}
/**
@@ -680,4 +684,31 @@ class HookProcessor
$entity->set(Field::STREAM_UPDATED_AT, DateTime::createNow()->toString());
}
private function followAddedCollaborators(CoreEntity $entity): void
{
if (
!$this->helper->hasCollaboratorsField($entity->getEntityType()) ||
!$entity->isAttributeChanged(Field::COLLABORATORS . 'Ids')
) {
return;
}
$currentUserIds = $entity->getLinkMultipleIdList(Field::COLLABORATORS);
$previousUserIds = $entity->getFetchedLinkMultipleIdList(Field::COLLABORATORS);
$addedUserIds = array_diff($currentUserIds, $previousUserIds);
$userIds = array_filter($addedUserIds, fn ($userId) => $this->toFollowUserAsCollaborator($userId));
$userIds = array_values($userIds);
$this->service->followEntityMass($entity, $userIds);
}
private function toFollowUserAsCollaborator(string $userId): bool
{
$preferences = $this->preferencesProvider->tryGet($userId);
return $preferences?->get('followAsCollaborator') === true;
}
}
@@ -0,0 +1,64 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://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 Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Tools\User;
use Espo\Entities\Preferences;
use Espo\ORM\EntityManager;
use RuntimeException;
/**
* @since 9.3.0
*/
class PreferencesProvider
{
/** @var array<string, ?Preferences> */
private array $cache = [];
public function __construct(
private EntityManager $entityManager,
) {}
public function tryGet(string $userId): ?Preferences
{
if (!isset($this->cache[$userId])) {
$this->cache[$userId] = $this->entityManager
->getRepositoryByClass(Preferences::class)
->getById($userId);
}
return $this->cache[$userId];
}
public function get(string $userId): Preferences
{
return $this->tryGet($userId) ??
throw new RuntimeException("Could not get preferences for $userId");
}
}