This commit is contained in:
Yuri Kuznetsov
2022-10-16 13:27:41 +03:00
parent 19227313b3
commit 7be573be67
10 changed files with 424 additions and 175 deletions
@@ -35,7 +35,7 @@ use stdClass;
class PopupNotification
{
private $service;
private Service $service;
public function __construct(Service $service)
{
@@ -44,6 +44,23 @@ class PopupNotification
public function getActionGrouped(): stdClass
{
return $this->service->getGroupedList();
$grouped = $this->service->getGrouped();
$result = (object) [];
foreach ($grouped as $type => $itemList) {
$rawList = array_map(
function ($item) {
return (object) [
'id' => $item->getId(),
'data' => $item->getData(),
];
},
$itemList
);
$result->$type = $rawList;
}
return $result;
}
}
@@ -40,7 +40,7 @@ use Espo\Core\Field\DateTime;
use Espo\Core\Record\SearchParamsFetcher;
use Espo\Modules\Crm\Tools\Calendar\FetchParams;
use Espo\Modules\Crm\Services\Activities as Service;
use Espo\Modules\Crm\Tools\Activities\Service as Service;
use Espo\Modules\Crm\Tools\Calendar\Item as CalendarItem;
use Espo\Modules\Crm\Tools\Calendar\Service as CalendarService;
use Espo\Entities\User;
@@ -108,8 +108,8 @@ class Activities
$fetchParams = FetchParams
::create(
DateTime::fromString($from . ':00'),
DateTime::fromString($to . ':00')
DateTime::fromString($from),
DateTime::fromString($to)
)
->withScopeList($scopeList);
@@ -244,17 +244,6 @@ class Activities
);
}
/**
* @return array<int,array<string,mixed>>
* @throws \Exception
*/
public function getActionPopupNotifications(): array
{
$userId = $this->user->getId();
return $this->service->getPopupNotifications($userId);
}
/**
* @throws BadRequest
*/
@@ -404,8 +393,8 @@ class Activities
$map = $this->calendarService->fetchBusyRangesForUsers(
$userIdList,
DateTime::fromString($from . ':00'),
DateTime::fromString($to . ':00'),
DateTime::fromString($from),
DateTime::fromString($to),
$request->getQueryParam('entityType'),
$request->getQueryParam('entityId')
);
@@ -33,6 +33,9 @@ class Reminder extends \Espo\Core\ORM\Entity
{
public const ENTITY_TYPE = 'Reminder';
public const TYPE_POPUP = 'Popup';
public const TYPE_EMAIL = 'Email';
public function getUserId(): ?string
{
return $this->get('userId');
@@ -1,7 +1,7 @@
{
"event": {
"url": "Activities/action/popupNotifications",
"grouped": true,
"providerClassName": "Espo\\Modules\\Crm\\Tools\\Activities\\PopupNotificationsProvider",
"serviceName": "Activities",
"methodName": "getPopupNotifications",
"interval": 15,
@@ -0,0 +1,157 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://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\Modules\Crm\Tools\Activities;
use DateInterval;
use DateTime;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\Modules\Crm\Entities\Reminder;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\EntityManager;
use Espo\Tools\PopupNotification\Item;
use Espo\Tools\PopupNotification\Provider;
use Exception;
class PopupNotificationsProvider implements Provider
{
private const REMINDER_PAST_HOURS = 24;
private Config $config;
private EntityManager $entityManager;
public function __construct(
Config $config,
EntityManager $entityManager
) {
$this->config = $config;
$this->entityManager = $entityManager;
}
/**
* @return Item[]
* @throws Exception
*/
public function get(User $user): array
{
$userId = $user->getId();
$dt = new DateTime();
$pastHours = $this->config->get('reminderPastHours', self::REMINDER_PAST_HOURS);
$now = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
$nowShifted = $dt
->sub(new DateInterval('PT' . $pastHours . 'H'))
->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
/** @var iterable<Reminder> $reminderCollection */
$reminderCollection = $this->entityManager
->getRDBRepositoryByClass(Reminder::class)
->select([
'id',
'entityType',
'entityId',
])
->where([
'type' => Reminder::TYPE_POPUP,
'userId' => $userId,
'remindAt<=' => $now,
'startAt>' => $nowShifted,
])
->find();
$resultList = [];
foreach ($reminderCollection as $reminder) {
$reminderId = $reminder->getId();
$entityType = $reminder->getTargetEntityType();
$entityId = $reminder->getTargetEntityId();
if (!$entityId || !$entityType) {
continue;
}
$entity = $this->entityManager->getEntityById($entityType, $entityId);
if (!$entity) {
continue;
}
$data = null;
if (
$entity instanceof CoreEntity &&
$entity->hasLinkMultipleField('users')
) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
$status = $entity->getLinkMultipleColumn('users', 'status', $userId);
if ($status === Meeting::ATTENDEE_STATUS_DECLINED) {
$this->removeReminder($reminderId);
continue;
}
}
$dateAttribute = $entityType === Task::ENTITY_TYPE ?
'dateEnd' :
'dateStart';
$data = (object) [
'id' => $entity->getId(),
'entityType' => $entityType,
$dateAttribute => $entity->get($dateAttribute),
'name' => $entity->get('name'),
];
$resultList[] = new Item($reminderId, $data);
}
return $resultList;
}
private function removeReminder(string $id): void
{
$deleteQuery = $this->entityManager
->getQueryBuilder()
->delete()
->from(Reminder::ENTITY_TYPE)
->where(['id' => $id])
->build();
$this->entityManager->getQueryExecutor()->execute($deleteQuery);
}
}
@@ -27,12 +27,28 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Modules\Crm\Services;
namespace Espo\Modules\Crm\Tools\Activities;
use Espo\Core\Acl;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\ServiceFactory;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Espo\Core\Utils\Metadata;
use Espo\Entities\Email;
use Espo\Entities\Preferences;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Contact;
use Espo\Modules\Crm\Entities\Lead;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\Modules\Crm\Entities\Reminder;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\EntityManager;
use Espo\ORM\Query\UnionBuilder;
use Espo\ORM\Query\SelectBuilder;
@@ -48,74 +64,77 @@ use Espo\Core\Select\Where\ConverterFactory as WhereConverterFactory;
use Espo\Core\Select\SelectBuilderFactory;
use Espo\Core\FieldProcessing\ListLoadProcessor;
use Espo\Core\FieldProcessing\Loader\Params as FieldLoaderParams;
use Espo\Core\Di;
use Espo\Core\Record\ServiceContainer as RecordServiceContainer;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Entities\User as UserEntity;
use PDO;
use Exception;
use DateTime;
use DateInterval;
use stdClass;
class Activities implements
Di\ConfigAware,
Di\MetadataAware,
Di\AclAware,
Di\ServiceFactoryAware,
Di\EntityManagerAware,
Di\UserAware
class Service
{
use Di\ConfigSetter;
use Di\MetadataSetter;
use Di\AclSetter;
use Di\ServiceFactorySetter;
use Di\EntityManagerSetter;
use Di\UserSetter;
const UPCOMING_ACTIVITIES_FUTURE_DAYS = 1;
const UPCOMING_ACTIVITIES_TASK_FUTURE_DAYS = 7;
const REMINDER_PAST_HOURS = 24;
private const UPCOMING_ACTIVITIES_FUTURE_DAYS = 1;
private const UPCOMING_ACTIVITIES_TASK_FUTURE_DAYS = 7;
private WhereConverterFactory $whereConverterFactory;
private ListLoadProcessor $listLoadProcessor;
private RecordServiceContainer $recordServiceContainer;
private SelectBuilderFactory $selectBuilderFactory;
private Config $config;
private Metadata $metadata;
private Acl $acl;
private ServiceFactory $serviceFactory;
private EntityManager $entityManager;
private User $user;
public function __construct(
WhereConverterFactory $whereConverterFactory,
ListLoadProcessor $listLoadProcessor,
RecordServiceContainer $recordServiceContainer,
SelectBuilderFactory $selectBuilderFactory
SelectBuilderFactory $selectBuilderFactory,
Config $config,
Metadata $metadata,
Acl $acl,
ServiceFactory $serviceFactory,
EntityManager $entityManager,
User $user
) {
$this->whereConverterFactory = $whereConverterFactory;
$this->listLoadProcessor = $listLoadProcessor;
$this->recordServiceContainer = $recordServiceContainer;
$this->selectBuilderFactory = $selectBuilderFactory;
$this->config = $config;
$this->metadata = $metadata;
$this->acl = $acl;
$this->serviceFactory = $serviceFactory;
$this->entityManager = $entityManager;
$this->user = $user;
}
protected function isPerson(string $scope): bool
{
return in_array($scope, ['Contact', 'Lead', 'User']) ||
return
in_array(
$scope,
[Contact::ENTITY_TYPE, Lead::ENTITY_TYPE, User::ENTITY_TYPE]
) ||
$this->metadata->get(['scopes', $scope, 'type']) === 'Person';
}
protected function isCompany(string $scope): bool
{
return in_array($scope, ['Account']) || $this->metadata->get(['scopes', $scope, 'type']) === 'Company';
return
in_array($scope, [Account::ENTITY_TYPE]) ||
$this->metadata->get(['scopes', $scope, 'type']) === 'Company';
}
/**
* @param string[] $statusList
*/
protected function getActivitiesUserMeetingQuery(UserEntity $entity, array $statusList = []): Select
protected function getActivitiesUserMeetingQuery(User $entity, array $statusList = []): Select
{
$builder = $this->selectBuilderFactory
->create()
->from('Meeting')
->from(Meeting::ENTITY_TYPE)
->withStrictAccessControl()
->buildQueryBuilder()
->select([
@@ -172,13 +191,13 @@ class Activities implements
/**
* @param string[] $statusList
*/
protected function getActivitiesUserCallQuery(UserEntity $entity, array $statusList = []): Select
protected function getActivitiesUserCallQuery(User $entity, array $statusList = []): Select
{
$seed = $this->entityManager->getNewEntity('Call');
$seed = $this->entityManager->getNewEntity(Call::ENTITY_TYPE);
$builder = $this->selectBuilderFactory
->create()
->from('Call')
->from(Call::ENTITY_TYPE)
->withStrictAccessControl()
->buildQueryBuilder()
->select([
@@ -242,10 +261,10 @@ class Activities implements
* @param string[] $statusList
* @return Select|Select[]
*/
protected function getActivitiesUserEmailQuery(UserEntity $entity, array $statusList = [])
protected function getActivitiesUserEmailQuery(User $entity, array $statusList = [])
{
if ($entity->isPortal() && $entity->get('contactId')) {
$contact = $this->entityManager->getEntity('Contact', $entity->get('contactId'));
$contact = $this->entityManager->getEntity(Contact::ENTITY_TYPE, $entity->get('contactId'));
if ($contact) {
return $this->getActivitiesEmailQuery($contact, $statusList);
@@ -254,7 +273,7 @@ class Activities implements
$builder = $this->selectBuilderFactory
->create()
->from('Email')
->from(Email::ENTITY_TYPE)
->withStrictAccessControl()
->buildQueryBuilder()
->select([
@@ -350,12 +369,12 @@ class Activities implements
$builder = clone $baseBuilder;
if ($entityType == 'Account') {
if ($entityType === Account::ENTITY_TYPE) {
$builder->where([
'OR' => [
[
'parentId' => $id,
'parentType' => 'Account',
'parentType' => Account::ENTITY_TYPE,
],
[
'accountId' => $id,
@@ -363,12 +382,12 @@ class Activities implements
],
]);
}
else if ($entityType == 'Lead' && $entity->get('createdAccountId')) {
else if ($entityType === Lead::ENTITY_TYPE && $entity->get('createdAccountId')) {
$builder->where([
'OR' => [
[
'parentId' => $id,
'parentType' => 'Lead',
'parentType' => Lead::ENTITY_TYPE,
],
[
'accountId' => $entity->get('createdAccountId'),
@@ -392,17 +411,17 @@ class Activities implements
$link = null;
switch ($entityType) {
case 'Contact':
case Contact::ENTITY_TYPE:
$link = 'contacts';
break;
case 'Lead':
case Lead::ENTITY_TYPE:
$link = 'leads';
break;
case 'User':
case User::ENTITY_TYPE:
$link = 'users';
break;
@@ -437,7 +456,7 @@ class Activities implements
*/
protected function getActivitiesMeetingQuery(Entity $entity, array $statusList = [])
{
return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, 'Meeting');
return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, Meeting::ENTITY_TYPE);
}
/**
@@ -446,7 +465,7 @@ class Activities implements
*/
protected function getActivitiesCallQuery(Entity $entity, array $statusList = [])
{
return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, 'Call');
return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, Call::ENTITY_TYPE);
}
/**
@@ -494,12 +513,12 @@ class Activities implements
$builder = clone $baseBuilder;
if ($entityType == 'Account') {
if ($entityType === Account::ENTITY_TYPE) {
$builder->where([
'OR' => [
[
'parentId' => $id,
'parentType' => 'Account',
'parentType' => Account::ENTITY_TYPE,
],
[
'accountId' => $id,
@@ -512,7 +531,7 @@ class Activities implements
'OR' => [
[
'parentId' => $id,
'parentType' => 'Lead',
'parentType' => Lead::ENTITY_TYPE,
],
[
'accountId' => $entity->get('createdAccountId'),
@@ -646,7 +665,7 @@ class Activities implements
$offset = $params['offset'] ?? 0;
if (!$onlyScope && $scope === 'User') {
if (!$onlyScope && $scope === User::ENTITY_TYPE) {
// optimizing sub-queries
$newQueryList = [];
@@ -733,7 +752,7 @@ class Activities implements
$list[] = $row;
}
if ($scope === 'User') {
if ($scope === User::ENTITY_TYPE) {
if ($maxSize && count($list) > $maxSize) {
$totalCount = -1;
@@ -752,7 +771,7 @@ class Activities implements
*/
protected function accessCheck(Entity $entity): void
{
if ($entity instanceof UserEntity) {
if ($entity instanceof User) {
if (!$this->acl->checkUserPermission($entity, 'user')) {
throw new Forbidden();
}
@@ -796,13 +815,15 @@ class Activities implements
}
if (!$isHistory) {
$statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList'], ['Planned']);
$statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList']) ??
[Meeting::STATUS_PLANNED];
}
else {
$statusList = $this->metadata->get(['scopes', $entityType, 'historyStatusList'], ['Held', 'Not Held']);
$statusList = $this->metadata->get(['scopes', $entityType, 'historyStatusList']) ??
[Meeting::STATUS_HELD, Meeting::STATUS_NOT_HELD];
}
if ($entityType === 'Email' && $searchParams->getOrderBy() === 'dateStart') {
if ($entityType === Email::ENTITY_TYPE && $searchParams->getOrderBy() === 'dateStart') {
$searchParams = $searchParams->withOrderBy('dateSent');
}
@@ -947,7 +968,8 @@ class Activities implements
$parts = [];
/** @var string[] $entityTypeList */
$entityTypeList = $this->config->get('activitiesEntityList', ['Meeting', 'Call']);
$entityTypeList = $this->config->get('activitiesEntityList') ??
[Meeting::ENTITY_TYPE, Call::ENTITY_TYPE];
foreach ($entityTypeList as $entityType) {
if (!$fetchAll && $targetScope !== $entityType) {
@@ -962,7 +984,8 @@ class Activities implements
continue;
}
$statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList'], ['Planned']);
$statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList']) ??
[Meeting::STATUS_PLANNED];
$parts[$entityType] = $this->getActivitiesQuery($entity, $entityType, $statusList);
}
@@ -1006,7 +1029,12 @@ class Activities implements
$parts = [];
/** @var string[] $entityTypeList */
$entityTypeList = $this->config->get('historyEntityList', ['Meeting', 'Call', 'Email']);
$entityTypeList = $this->config->get('historyEntityList') ??
[
Meeting::ENTITY_TYPE,
Call::ENTITY_TYPE,
Email::ENTITY_TYPE
];
foreach ($entityTypeList as $entityType) {
if (!$fetchAll && $targetScope !== $entityType) {
@@ -1021,9 +1049,8 @@ class Activities implements
continue;
}
$statusList = $this->metadata->get(
['scopes', $entityType, 'historyStatusList'], ['Held', 'Not Held']
);
$statusList = $this->metadata->get(['scopes', $entityType, 'historyStatusList']) ??
[Meeting::STATUS_HELD, Meeting::STATUS_NOT_HELD];
$parts[$entityType] = $this->getActivitiesQuery($entity, $entityType, $statusList);
}
@@ -1031,7 +1058,7 @@ class Activities implements
$result = $this->getResultFromQueryParts($parts, $scope, $params);
foreach ($result['list'] as &$item) {
if ($item['_scope'] == 'Email') {
if ($item['_scope'] === Email::ENTITY_TYPE) {
$item['dateSent'] = $item['dateStart'];
}
}
@@ -1099,7 +1126,7 @@ class Activities implements
['""', 'hasAttachment'],
]);
if ($entity->getEntityType() === 'User') {
if ($entity->getEntityType() === User::ENTITY_TYPE) {
$builder->where([
'assignedUserId' => $entity->getId(),
]);
@@ -1123,7 +1150,7 @@ class Activities implements
$builder = $this->entityManager
->getQueryBuilder()
->delete()
->from('Reminder')
->from(Reminder::ENTITY_TYPE)
->where([
'id' => $id,
]);
@@ -1139,82 +1166,6 @@ class Activities implements
$this->entityManager->getQueryExecutor()->execute($deleteQuery);
}
/**
* @return array<int,array<string,mixed>>
* @throws Exception
*/
public function getPopupNotifications(string $userId): array
{
$dt = new DateTime();
$pastHours = $this->config->get('reminderPastHours', self::REMINDER_PAST_HOURS);
$now = $dt->format('Y-m-d H:i:s');
$nowShifted = $dt->sub(new DateInterval('PT'.strval($pastHours).'H'))->format('Y-m-d H:i:s');
/** @var iterable<\Espo\Modules\Crm\Entities\Reminder> $reminderCollection */
$reminderCollection = $this->entityManager
->getRDBRepository('Reminder')
->select(['id', 'entityType', 'entityId'])
->where([
'type' => 'Popup',
'userId' => $userId,
'remindAt<=' => $now,
'startAt>' => $nowShifted,
])
->find();
$resultList = [];
foreach ($reminderCollection as $reminder) {
$reminderId = $reminder->get('id');
$entityType = $reminder->get('entityType');
$entityId = $reminder->get('entityId');
$entity = $this->entityManager->getEntity($entityType, $entityId);
$data = null;
if (!$entity) {
continue;
}
if (
$entity instanceof CoreEntity &&
$entity->hasLinkMultipleField('users')
) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
$status = $entity->getLinkMultipleColumn('users', 'status', $userId);
if ($status === 'Declined') {
$this->removeReminder($reminderId);
continue;
}
}
$dateAttribute = 'dateStart';
if ($entityType === 'Task') {
$dateAttribute = 'dateEnd';
}
$data = [
'id' => $entity->getId(),
'entityType' => $entityType,
$dateAttribute => $entity->get($dateAttribute),
'name' => $entity->get('name'),
];
$resultList[] = [
'id' => $reminderId,
'data' => $data,
];
}
return $resultList;
}
/**
* @param array{
* offset?: ?int,
@@ -1235,7 +1186,7 @@ class Activities implements
?int $futureDays = null
): array {
$user = $this->entityManager->getEntity('User', $userId);
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $userId);
if (!$user) {
throw new NotFound();
@@ -1259,7 +1210,7 @@ class Activities implements
foreach ($entityTypeList as $entityType) {
if (
!$this->metadata->get(['scopes', $entityType, 'activity']) &&
$entityType !== 'Task'
$entityType !== Task::ENTITY_TYPE
) {
continue;
}
@@ -1351,13 +1302,13 @@ class Activities implements
protected function getUpcomingActivitiesEntityTypeQuery(
string $entityType,
array $params,
UserEntity $user,
User $user,
int $futureDays
): Select {
$beforeString = (new DateTime())
->modify('+' . $futureDays . ' days')
->format('Y-m-d H:i:s');
->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
$builder = $this->selectBuilderFactory
->create()
@@ -1368,7 +1319,7 @@ class Activities implements
$primaryFilter = 'planned';
if ($entityType === 'Task') {
if ($entityType === Task::ENTITY_TYPE) {
$primaryFilter = 'actual';
}
@@ -1384,7 +1335,7 @@ class Activities implements
$timeZone = $this->getUserTimeZone($user);
if ($entityType === 'Task') {
if ($entityType === Task::ENTITY_TYPE) {
$upcomingTaskFutureDays = $this->config->get(
'activitiesUpcomingTaskFutureDays',
self::UPCOMING_ACTIVITIES_TASK_FUTURE_DAYS
@@ -1392,7 +1343,7 @@ class Activities implements
$taskBeforeString = (new DateTime())
->modify('+' . $upcomingTaskFutureDays . ' days')
->format('Y-m-d H:i:s');
->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
$queryBuilder->where([
'OR' => [
@@ -1489,9 +1440,9 @@ class Activities implements
return $queryBuilder->build();
}
protected function getUserTimeZone(UserEntity $user): string
protected function getUserTimeZone(User $user): string
{
$preferences = $this->entityManager->getEntity('Preferences', $user->getId());
$preferences = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $user->getId());
if ($preferences) {
$timeZone = $preferences->get('timeZone');
@@ -57,8 +57,6 @@ use Espo\Core\ServiceFactory;
use Espo\Tools\WorkingTime\Extractor;
use PDO;
use Exception;
use stdClass;
use DateTime;
class Service
{
@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://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\Tools\PopupNotification;
use stdClass;
class Item
{
private ?string $id;
private stdClass $data;
/**
* @param ?string $id An ID.
* @param stdClass $data Data to pass to a front-end handler.
*/
public function __construct(
?string $id,
stdClass $data
) {
$this->id = $id;
$this->data = $data;
}
public function getId(): ?string
{
return $this->id;
}
public function getData(): stdClass
{
return $this->data;
}
}
@@ -0,0 +1,43 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://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\Tools\PopupNotification;
use Espo\Entities\User;
/**
* Provides a list of popup notification items for a user.
*/
interface Provider
{
/**
* @return Item[]
*/
public function get(User $user): array;
}
@@ -29,6 +29,7 @@
namespace Espo\Tools\PopupNotification;
use Espo\Core\InjectableFactory;
use Espo\Core\ServiceFactory;
use Espo\Core\Utils\Log;
use Espo\Core\Utils\Metadata;
@@ -44,20 +45,26 @@ class Service
private ServiceFactory $serviceFactory;
private User $user;
private Log $log;
private InjectableFactory $injectableFactory;
public function __construct(
Metadata $metadata,
ServiceFactory $serviceFactory,
User $user,
Log $log
Log $log,
InjectableFactory $injectableFactory
) {
$this->metadata = $metadata;
$this->serviceFactory = $serviceFactory;
$this->user = $user;
$this->log = $log;
$this->injectableFactory = $injectableFactory;
}
public function getGroupedList(): stdClass
/**
* @return array<string, Item[]> Items grouped by type.
*/
public function getGrouped(): array
{
$data = $this->metadata->get(['app', 'popupNotifications']) ?? [];
@@ -83,16 +90,40 @@ class Service
return true;
});
$result = (object) [];
$result = [];
foreach ($data as $type => $item) {
$serviceName = $item['serviceName'];
$methodName = $item['methodName'];
/** @var ?class-string<Provider> $className */
$className = $item['providerClassName'] ?? null;
try {
if ($className) {
$provider = $this->injectableFactory->create($className);
$result[$type] = $provider->get($this->user);
continue;
}
// For bc.
$serviceName = $item['serviceName'];
$methodName = $item['methodName'];
$service = $this->serviceFactory->create($serviceName);
$result->$type = $service->$methodName($this->user->id);
$itemList = array_map(
function ($raw) {
if ($raw instanceof stdClass) {
return new Item($raw->id ?? null, $raw->data);
}
return new Item($raw['id'] ?? null, $raw['data']);
},
$service->$methodName($this->user->getId())
);
$result[$type] = $itemList;
}
catch (Throwable $e) {
$this->log->error("Popup notifications: " . $e->getMessage());