diff options
Diffstat (limited to 'apps/user_status/lib')
22 files changed, 200 insertions, 197 deletions
diff --git a/apps/user_status/lib/AppInfo/Application.php b/apps/user_status/lib/AppInfo/Application.php index 31b7342e40b..5199c3fdbf0 100644 --- a/apps/user_status/lib/AppInfo/Application.php +++ b/apps/user_status/lib/AppInfo/Application.php @@ -23,7 +23,9 @@ use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\IConfig; use OCP\User\Events\OutOfOfficeChangedEvent; use OCP\User\Events\OutOfOfficeClearedEvent; +use OCP\User\Events\OutOfOfficeEndedEvent; use OCP\User\Events\OutOfOfficeScheduledEvent; +use OCP\User\Events\OutOfOfficeStartedEvent; use OCP\User\Events\UserDeletedEvent; use OCP\User\Events\UserLiveStatusEvent; use OCP\UserStatus\IManager; @@ -61,6 +63,8 @@ class Application extends App implements IBootstrap { $context->registerEventListener(OutOfOfficeChangedEvent::class, OutOfOfficeStatusListener::class); $context->registerEventListener(OutOfOfficeScheduledEvent::class, OutOfOfficeStatusListener::class); $context->registerEventListener(OutOfOfficeClearedEvent::class, OutOfOfficeStatusListener::class); + $context->registerEventListener(OutOfOfficeStartedEvent::class, OutOfOfficeStatusListener::class); + $context->registerEventListener(OutOfOfficeEndedEvent::class, OutOfOfficeStatusListener::class); $config = $this->getContainer()->query(IConfig::class); $shareeEnumeration = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; diff --git a/apps/user_status/lib/BackgroundJob/ClearOldStatusesBackgroundJob.php b/apps/user_status/lib/BackgroundJob/ClearOldStatusesBackgroundJob.php index 1810f7aa118..51a9c623a03 100644 --- a/apps/user_status/lib/BackgroundJob/ClearOldStatusesBackgroundJob.php +++ b/apps/user_status/lib/BackgroundJob/ClearOldStatusesBackgroundJob.php @@ -20,21 +20,18 @@ use OCP\BackgroundJob\TimedJob; */ class ClearOldStatusesBackgroundJob extends TimedJob { - /** @var UserStatusMapper */ - private $mapper; - /** * ClearOldStatusesBackgroundJob constructor. * * @param ITimeFactory $time * @param UserStatusMapper $mapper */ - public function __construct(ITimeFactory $time, - UserStatusMapper $mapper) { + public function __construct( + ITimeFactory $time, + private UserStatusMapper $mapper, + ) { parent::__construct($time); - $this->mapper = $mapper; - // Run every time the cron is run $this->setInterval(60); } diff --git a/apps/user_status/lib/Capabilities.php b/apps/user_status/lib/Capabilities.php index 9a1ac4b47ab..c3edbc032d6 100644 --- a/apps/user_status/lib/Capabilities.php +++ b/apps/user_status/lib/Capabilities.php @@ -17,14 +17,13 @@ use OCP\IEmojiHelper; * @package OCA\UserStatus */ class Capabilities implements ICapability { - private IEmojiHelper $emojiHelper; - - public function __construct(IEmojiHelper $emojiHelper) { - $this->emojiHelper = $emojiHelper; + public function __construct( + private IEmojiHelper $emojiHelper, + ) { } /** - * @return array{user_status: array{enabled: bool, restore: bool, supports_emoji: bool}} + * @return array{user_status: array{enabled: bool, restore: bool, supports_emoji: bool, supports_busy: bool}} */ public function getCapabilities() { return [ @@ -32,6 +31,7 @@ class Capabilities implements ICapability { 'enabled' => true, 'restore' => true, 'supports_emoji' => $this->emojiHelper->doesPlatformSupportEmoji(), + 'supports_busy' => true, ], ]; } diff --git a/apps/user_status/lib/Connector/UserStatus.php b/apps/user_status/lib/Connector/UserStatus.php index 7fbfd62f223..04467a99e5e 100644 --- a/apps/user_status/lib/Connector/UserStatus.php +++ b/apps/user_status/lib/Connector/UserStatus.php @@ -29,21 +29,19 @@ class UserStatus implements IUserStatus { /** @var DateTimeImmutable|null */ private $clearAt; - /** @var Db\UserStatus */ - private $internalStatus; - - public function __construct(Db\UserStatus $status) { - $this->internalStatus = $status; - $this->userId = $status->getUserId(); - $this->status = $status->getStatus(); - $this->message = $status->getCustomMessage(); - $this->icon = $status->getCustomIcon(); - - if ($status->getStatus() === IUserStatus::INVISIBLE) { + public function __construct( + private Db\UserStatus $internalStatus, + ) { + $this->userId = $this->internalStatus->getUserId(); + $this->status = $this->internalStatus->getStatus(); + $this->message = $this->internalStatus->getCustomMessage(); + $this->icon = $this->internalStatus->getCustomIcon(); + + if ($this->internalStatus->getStatus() === IUserStatus::INVISIBLE) { $this->status = IUserStatus::OFFLINE; } - if ($status->getClearAt() !== null) { - $this->clearAt = DateTimeImmutable::createFromFormat('U', (string)$status->getClearAt()); + if ($this->internalStatus->getClearAt() !== null) { + $this->clearAt = DateTimeImmutable::createFromFormat('U', (string)$this->internalStatus->getClearAt()); } } diff --git a/apps/user_status/lib/Connector/UserStatusProvider.php b/apps/user_status/lib/Connector/UserStatusProvider.php index a85223dcf2a..e84d69d1eb2 100644 --- a/apps/user_status/lib/Connector/UserStatusProvider.php +++ b/apps/user_status/lib/Connector/UserStatusProvider.php @@ -14,16 +14,14 @@ use OCP\UserStatus\IProvider; class UserStatusProvider implements IProvider, ISettableProvider { - /** @var StatusService */ - private $service; - /** * UserStatusProvider constructor. * * @param StatusService $service */ - public function __construct(StatusService $service) { - $this->service = $service; + public function __construct( + private StatusService $service, + ) { } /** diff --git a/apps/user_status/lib/ContactsMenu/StatusProvider.php b/apps/user_status/lib/ContactsMenu/StatusProvider.php index 023b4402fc2..6a6949b46ba 100644 --- a/apps/user_status/lib/ContactsMenu/StatusProvider.php +++ b/apps/user_status/lib/ContactsMenu/StatusProvider.php @@ -19,7 +19,9 @@ use function array_map; class StatusProvider implements IBulkProvider { - public function __construct(private StatusService $statusService) { + public function __construct( + private StatusService $statusService, + ) { } public function process(array $entries): void { diff --git a/apps/user_status/lib/Controller/HeartbeatController.php b/apps/user_status/lib/Controller/HeartbeatController.php index e8325617557..30f4af6572a 100644 --- a/apps/user_status/lib/Controller/HeartbeatController.php +++ b/apps/user_status/lib/Controller/HeartbeatController.php @@ -13,6 +13,7 @@ use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\StatusService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\AppFramework\Utility\ITimeFactory; @@ -27,43 +28,29 @@ use OCP\UserStatus\IUserStatus; */ class HeartbeatController extends OCSController { - /** @var IEventDispatcher */ - private $eventDispatcher; - - /** @var IUserSession */ - private $userSession; - - /** @var ITimeFactory */ - private $timeFactory; - - /** @var StatusService */ - private $service; - - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - IEventDispatcher $eventDispatcher, - IUserSession $userSession, - ITimeFactory $timeFactory, - StatusService $service) { + private IEventDispatcher $eventDispatcher, + private IUserSession $userSession, + private ITimeFactory $timeFactory, + private StatusService $service, + ) { parent::__construct($appName, $request); - $this->eventDispatcher = $eventDispatcher; - $this->userSession = $userSession; - $this->timeFactory = $timeFactory; - $this->service = $service; } /** * Keep the status alive * - * @NoAdminRequired - * * @param string $status Only online, away * - * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NO_CONTENT, array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NO_CONTENT, list<empty>, array{}> + * * 200: Status successfully updated * 204: User has no status to keep alive * 400: Invalid status to update */ + #[NoAdminRequired] #[ApiRoute(verb: 'PUT', url: '/api/v1/heartbeat')] public function heartbeat(string $status): DataResponse { if (!\in_array($status, [IUserStatus::ONLINE, IUserStatus::AWAY], true)) { diff --git a/apps/user_status/lib/Controller/PredefinedStatusController.php b/apps/user_status/lib/Controller/PredefinedStatusController.php index 884bc1d2baa..70262d1108a 100644 --- a/apps/user_status/lib/Controller/PredefinedStatusController.php +++ b/apps/user_status/lib/Controller/PredefinedStatusController.php @@ -12,6 +12,7 @@ use OCA\UserStatus\ResponseDefinitions; use OCA\UserStatus\Service\PredefinedStatusService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -23,9 +24,6 @@ use OCP\IRequest; */ class PredefinedStatusController extends OCSController { - /** @var PredefinedStatusService */ - private $predefinedStatusService; - /** * AStatusController constructor. * @@ -33,27 +31,27 @@ class PredefinedStatusController extends OCSController { * @param IRequest $request * @param PredefinedStatusService $predefinedStatusService */ - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - PredefinedStatusService $predefinedStatusService) { + private PredefinedStatusService $predefinedStatusService, + ) { parent::__construct($appName, $request); - $this->predefinedStatusService = $predefinedStatusService; } /** * Get all predefined messages * - * @NoAdminRequired - * - * @return DataResponse<Http::STATUS_OK, UserStatusPredefined[], array{}> + * @return DataResponse<Http::STATUS_OK, list<UserStatusPredefined>, array{}> * * 200: Predefined statuses returned */ + #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/v1/predefined_statuses/')] public function findAll():DataResponse { // Filtering out the invisible one, that should only be set by API - return new DataResponse(array_filter($this->predefinedStatusService->getDefaultStatuses(), function (array $status) { + return new DataResponse(array_values(array_filter($this->predefinedStatusService->getDefaultStatuses(), function (array $status) { return !array_key_exists('visible', $status) || $status['visible'] === true; - })); + }))); } } diff --git a/apps/user_status/lib/Controller/StatusesController.php b/apps/user_status/lib/Controller/StatusesController.php index da942ed7d7c..44688c39023 100644 --- a/apps/user_status/lib/Controller/StatusesController.php +++ b/apps/user_status/lib/Controller/StatusesController.php @@ -14,6 +14,7 @@ use OCA\UserStatus\Service\StatusService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; @@ -26,9 +27,6 @@ use OCP\UserStatus\IUserStatus; */ class StatusesController extends OCSController { - /** @var StatusService */ - private $service; - /** * StatusesController constructor. * @@ -36,44 +34,43 @@ class StatusesController extends OCSController { * @param IRequest $request * @param StatusService $service */ - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - StatusService $service) { + private StatusService $service, + ) { parent::__construct($appName, $request); - $this->service = $service; } /** * Find statuses of users * - * @NoAdminRequired - * * @param int|null $limit Maximum number of statuses to find - * @param int|null $offset Offset for finding statuses - * @return DataResponse<Http::STATUS_OK, UserStatusPublic[], array{}> + * @param non-negative-int|null $offset Offset for finding statuses + * @return DataResponse<Http::STATUS_OK, list<UserStatusPublic>, array{}> * * 200: Statuses returned */ + #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/v1/statuses')] public function findAll(?int $limit = null, ?int $offset = null): DataResponse { $allStatuses = $this->service->findAll($limit, $offset); - return new DataResponse(array_map(function ($userStatus) { + return new DataResponse(array_values(array_map(function ($userStatus) { return $this->formatStatus($userStatus); - }, $allStatuses)); + }, $allStatuses))); } /** * Find the status of a user * - * @NoAdminRequired - * * @param string $userId ID of the user * @return DataResponse<Http::STATUS_OK, UserStatusPublic, array{}> * @throws OCSNotFoundException The user was not found * * 200: Status returned */ + #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/v1/statuses/{userId}')] public function find(string $userId): DataResponse { try { diff --git a/apps/user_status/lib/Controller/UserStatusController.php b/apps/user_status/lib/Controller/UserStatusController.php index 70bf619253b..9b3807ce86e 100644 --- a/apps/user_status/lib/Controller/UserStatusController.php +++ b/apps/user_status/lib/Controller/UserStatusController.php @@ -20,6 +20,7 @@ use OCA\UserStatus\Service\StatusService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSNotFoundException; @@ -35,7 +36,7 @@ class UserStatusController extends OCSController { public function __construct( string $appName, IRequest $request, - private string $userId, + private ?string $userId, private LoggerInterface $logger, private StatusService $service, private CalendarStatusService $calendarStatusService, @@ -46,13 +47,12 @@ class UserStatusController extends OCSController { /** * Get the status of the current user * - * @NoAdminRequired - * * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}> * @throws OCSNotFoundException The user was not found * * 200: The status was found successfully */ + #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/v1/user_status')] public function getStatus(): DataResponse { try { @@ -68,14 +68,13 @@ class UserStatusController extends OCSController { /** * Update the status type of the current user * - * @NoAdminRequired - * * @param string $statusType The new status type * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}> * @throws OCSBadRequestException The status type is invalid * * 200: The status was updated successfully */ + #[NoAdminRequired] #[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/status')] public function setStatus(string $statusType): DataResponse { try { @@ -92,8 +91,6 @@ class UserStatusController extends OCSController { /** * Set the message to a predefined message for the current user * - * @NoAdminRequired - * * @param string $messageId ID of the predefined message * @param int|null $clearAt When the message should be cleared * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}> @@ -101,6 +98,7 @@ class UserStatusController extends OCSController { * * 200: The message was updated successfully */ + #[NoAdminRequired] #[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/message/predefined')] public function setPredefinedMessage(string $messageId, ?int $clearAt): DataResponse { @@ -120,16 +118,16 @@ class UserStatusController extends OCSController { /** * Set the message to a custom message for the current user * - * @NoAdminRequired - * * @param string|null $statusIcon Icon of the status * @param string|null $message Message of the status * @param int|null $clearAt When the message should be cleared * @return DataResponse<Http::STATUS_OK, UserStatusPrivate, array{}> * @throws OCSBadRequestException The clearAt or icon is invalid or the message is too long + * @throws OCSNotFoundException No status for the current user * * 200: The message was updated successfully */ + #[NoAdminRequired] #[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/message/custom')] public function setCustomMessage(?string $statusIcon, ?string $message, @@ -152,18 +150,19 @@ class UserStatusController extends OCSController { } catch (StatusMessageTooLongException $ex) { $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to a too long status message.'); throw new OCSBadRequestException($ex->getMessage(), $ex); + } catch (DoesNotExistException $ex) { + throw new OCSNotFoundException('No status for the current user'); } } /** * Clear the message of the current user * - * @NoAdminRequired - * - * @return DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK, list<empty>, array{}> * * 200: Message cleared successfully */ + #[NoAdminRequired] #[ApiRoute(verb: 'DELETE', url: '/api/v1/user_status/message')] public function clearMessage(): DataResponse { $this->service->clearMessage($this->userId); @@ -173,14 +172,13 @@ class UserStatusController extends OCSController { /** * Revert the status to the previous status * - * @NoAdminRequired - * * @param string $messageId ID of the message to delete * - * @return DataResponse<Http::STATUS_OK, UserStatusPrivate|array<empty>, array{}> + * @return DataResponse<Http::STATUS_OK, UserStatusPrivate|list<empty>, array{}> * * 200: Status reverted */ + #[NoAdminRequired] #[ApiRoute(verb: 'DELETE', url: '/api/v1/user_status/revert/{messageId}')] public function revertStatus(string $messageId): DataResponse { $backupStatus = $this->service->revertUserStatus($this->userId, $messageId, true); diff --git a/apps/user_status/lib/Dashboard/UserStatusWidget.php b/apps/user_status/lib/Dashboard/UserStatusWidget.php index 71f5145e283..2870a2c1907 100644 --- a/apps/user_status/lib/Dashboard/UserStatusWidget.php +++ b/apps/user_status/lib/Dashboard/UserStatusWidget.php @@ -32,14 +32,6 @@ use OCP\UserStatus\IUserStatus; * @package OCA\UserStatus */ class UserStatusWidget implements IAPIWidget, IAPIWidgetV2, IIconWidget, IOptionWidget { - private IL10N $l10n; - private IDateTimeFormatter $dateTimeFormatter; - private IURLGenerator $urlGenerator; - private IInitialState $initialStateService; - private IUserManager $userManager; - private IUserSession $userSession; - private StatusService $service; - /** * UserStatusWidget constructor * @@ -51,20 +43,15 @@ class UserStatusWidget implements IAPIWidget, IAPIWidgetV2, IIconWidget, IOption * @param IUserSession $userSession * @param StatusService $service */ - public function __construct(IL10N $l10n, - IDateTimeFormatter $dateTimeFormatter, - IURLGenerator $urlGenerator, - IInitialState $initialStateService, - IUserManager $userManager, - IUserSession $userSession, - StatusService $service) { - $this->l10n = $l10n; - $this->dateTimeFormatter = $dateTimeFormatter; - $this->urlGenerator = $urlGenerator; - $this->initialStateService = $initialStateService; - $this->userManager = $userManager; - $this->userSession = $userSession; - $this->service = $service; + public function __construct( + private IL10N $l10n, + private IDateTimeFormatter $dateTimeFormatter, + private IURLGenerator $urlGenerator, + private IInitialState $initialStateService, + private IUserManager $userManager, + private IUserSession $userSession, + private StatusService $service, + ) { } /** @@ -124,7 +111,7 @@ class UserStatusWidget implements IAPIWidget, IAPIWidgetV2, IIconWidget, IOption $this->service->findAllRecentStatusChanges($limit + 1, 0), static function (UserStatus $status) use ($userId, $since): bool { return $status->getUserId() !== $userId - && ($since === null || $status->getStatusTimestamp() > (int) $since); + && ($since === null || $status->getStatusTimestamp() > (int)$since); } ), 0, @@ -156,21 +143,21 @@ class UserStatusWidget implements IAPIWidget, IAPIWidgetV2, IIconWidget, IOption public function getItems(string $userId, ?string $since = null, int $limit = 7): array { $widgetItemsData = $this->getWidgetData($userId, $since, $limit); - return array_map(function (array $widgetData) { + return array_values(array_map(function (array $widgetData) { $formattedDate = $this->dateTimeFormatter->formatTimeSpan($widgetData['timestamp']); return new WidgetItem( $widgetData['displayName'], $widgetData['icon'] . ($widgetData['icon'] ? ' ' : '') . $widgetData['message'] . ', ' . $formattedDate, // https://nextcloud.local/index.php/u/julien $this->urlGenerator->getAbsoluteURL( - $this->urlGenerator->linkToRoute('core.ProfilePage.index', ['targetUserId' => $widgetData['userId']]) + $this->urlGenerator->linkToRoute('profile.ProfilePage.index', ['targetUserId' => $widgetData['userId']]) ), $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkToRoute('core.avatar.getAvatar', ['userId' => $widgetData['userId'], 'size' => 44]) ), - (string) $widgetData['timestamp'] + (string)$widgetData['timestamp'] ); - }, $widgetItemsData); + }, $widgetItemsData)); } /** diff --git a/apps/user_status/lib/Db/UserStatus.php b/apps/user_status/lib/Db/UserStatus.php index 1be35830853..b2da4a9e07a 100644 --- a/apps/user_status/lib/Db/UserStatus.php +++ b/apps/user_status/lib/Db/UserStatus.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace OCA\UserStatus\Db; use OCP\AppFramework\Db\Entity; +use OCP\DB\Types; /** * Class UserStatus @@ -73,13 +74,13 @@ class UserStatus extends Entity { public function __construct() { $this->addType('userId', 'string'); $this->addType('status', 'string'); - $this->addType('statusTimestamp', 'int'); + $this->addType('statusTimestamp', Types::INTEGER); $this->addType('isUserDefined', 'boolean'); $this->addType('messageId', 'string'); $this->addType('customIcon', 'string'); $this->addType('customMessage', 'string'); - $this->addType('clearAt', 'int'); + $this->addType('clearAt', Types::INTEGER); $this->addType('isBackup', 'boolean'); - $this->addType('statusMessageTimestamp', 'int'); + $this->addType('statusMessageTimestamp', Types::INTEGER); } } diff --git a/apps/user_status/lib/Db/UserStatusMapper.php b/apps/user_status/lib/Db/UserStatusMapper.php index c98f0bf817f..15982d44fd8 100644 --- a/apps/user_status/lib/Db/UserStatusMapper.php +++ b/apps/user_status/lib/Db/UserStatusMapper.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace OCA\UserStatus\Db; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -83,7 +84,7 @@ class UserStatusMapper extends QBMapper { /** * @param string $userId * @return UserStatus - * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws DoesNotExistException */ public function findByUserId(string $userId, bool $isBackup = false): UserStatus { $qb = $this->db->getQueryBuilder(); @@ -126,7 +127,7 @@ class UserStatusMapper extends QBMapper { $qb->expr()->eq('status', $qb->createNamedParameter(IUserStatus::ONLINE)) )); - $qb->execute(); + $qb->executeStatement(); } /** @@ -140,7 +141,7 @@ class UserStatusMapper extends QBMapper { ->where($qb->expr()->isNotNull('clear_at')) ->andWhere($qb->expr()->lte('clear_at', $qb->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } diff --git a/apps/user_status/lib/Listener/BeforeTemplateRenderedListener.php b/apps/user_status/lib/Listener/BeforeTemplateRenderedListener.php index 8b639169b07..ab3a1e62beb 100644 --- a/apps/user_status/lib/Listener/BeforeTemplateRenderedListener.php +++ b/apps/user_status/lib/Listener/BeforeTemplateRenderedListener.php @@ -18,6 +18,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\IInitialStateService; use OCP\IUserSession; +use OCP\Util; /** @template-implements IEventListener<BeforeTemplateRenderedEvent> */ class BeforeTemplateRenderedListener implements IEventListener { @@ -25,15 +26,6 @@ class BeforeTemplateRenderedListener implements IEventListener { /** @var ProfileManager */ private $profileManager; - /** @var IUserSession */ - private $userSession; - - /** @var IInitialStateService */ - private $initialState; - - /** @var JSDataService */ - private $jsDataService; - /** * BeforeTemplateRenderedListener constructor. * @@ -44,14 +36,11 @@ class BeforeTemplateRenderedListener implements IEventListener { */ public function __construct( ProfileManager $profileManager, - IUserSession $userSession, - IInitialStateService $initialState, - JSDataService $jsDataService + private IUserSession $userSession, + private IInitialStateService $initialState, + private JSDataService $jsDataService, ) { $this->profileManager = $profileManager; - $this->userSession = $userSession; - $this->initialState = $initialState; - $this->jsDataService = $jsDataService; } /** @@ -80,7 +69,7 @@ class BeforeTemplateRenderedListener implements IEventListener { return ['profileEnabled' => $this->profileManager->isProfileEnabled($user)]; }); - \OCP\Util::addScript('user_status', 'menu'); - \OCP\Util::addStyle('user_status', 'user-status-menu'); + Util::addScript('user_status', 'menu'); + Util::addStyle('user_status', 'user-status-menu'); } } diff --git a/apps/user_status/lib/Listener/OutOfOfficeStatusListener.php b/apps/user_status/lib/Listener/OutOfOfficeStatusListener.php index 7b1a91ead8c..6337d637896 100644 --- a/apps/user_status/lib/Listener/OutOfOfficeStatusListener.php +++ b/apps/user_status/lib/Listener/OutOfOfficeStatusListener.php @@ -15,34 +15,41 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\OutOfOfficeChangedEvent; use OCP\User\Events\OutOfOfficeClearedEvent; +use OCP\User\Events\OutOfOfficeEndedEvent; use OCP\User\Events\OutOfOfficeScheduledEvent; +use OCP\User\Events\OutOfOfficeStartedEvent; use OCP\UserStatus\IManager; use OCP\UserStatus\IUserStatus; /** * Class UserDeletedListener * - * @template-implements IEventListener<OutOfOfficeScheduledEvent|OutOfOfficeChangedEvent|OutOfOfficeClearedEvent> + * @template-implements IEventListener<OutOfOfficeScheduledEvent|OutOfOfficeChangedEvent|OutOfOfficeClearedEvent|OutOfOfficeStartedEvent|OutOfOfficeEndedEvent> * */ class OutOfOfficeStatusListener implements IEventListener { - public function __construct(private IJobList $jobsList, + public function __construct( + private IJobList $jobsList, private ITimeFactory $time, - private IManager $manager) { + private IManager $manager, + ) { } /** * @inheritDoc */ public function handle(Event $event): void { - if($event instanceof OutOfOfficeClearedEvent) { + if ($event instanceof OutOfOfficeClearedEvent) { $this->manager->revertUserStatus($event->getData()->getUser()->getUID(), IUserStatus::MESSAGE_OUT_OF_OFFICE, IUserStatus::DND); $this->jobsList->scheduleAfter(UserStatusAutomation::class, $this->time->getTime(), ['userId' => $event->getData()->getUser()->getUID()]); return; } if ($event instanceof OutOfOfficeScheduledEvent - || $event instanceof OutOfOfficeChangedEvent) { + || $event instanceof OutOfOfficeChangedEvent + || $event instanceof OutOfOfficeStartedEvent + || $event instanceof OutOfOfficeEndedEvent + ) { // This might be overwritten by the office hours automation, but that is ok. This is just in case no office hours are set $this->jobsList->scheduleAfter(UserStatusAutomation::class, $this->time->getTime(), ['userId' => $event->getData()->getUser()->getUID()]); } diff --git a/apps/user_status/lib/Listener/UserDeletedListener.php b/apps/user_status/lib/Listener/UserDeletedListener.php index 55ec5fe13ff..bf021635156 100644 --- a/apps/user_status/lib/Listener/UserDeletedListener.php +++ b/apps/user_status/lib/Listener/UserDeletedListener.php @@ -21,16 +21,14 @@ use OCP\User\Events\UserDeletedEvent; */ class UserDeletedListener implements IEventListener { - /** @var StatusService */ - private $service; - /** * UserDeletedListener constructor. * * @param StatusService $service */ - public function __construct(StatusService $service) { - $this->service = $service; + public function __construct( + private StatusService $service, + ) { } diff --git a/apps/user_status/lib/Listener/UserLiveStatusListener.php b/apps/user_status/lib/Listener/UserLiveStatusListener.php index fa4ff8e4aba..2db999d3712 100644 --- a/apps/user_status/lib/Listener/UserLiveStatusListener.php +++ b/apps/user_status/lib/Listener/UserLiveStatusListener.php @@ -29,18 +29,13 @@ use Psr\Log\LoggerInterface; * @template-implements IEventListener<UserLiveStatusEvent> */ class UserLiveStatusListener implements IEventListener { - private UserStatusMapper $mapper; - private StatusService $statusService; - private ITimeFactory $timeFactory; - - public function __construct(UserStatusMapper $mapper, - StatusService $statusService, - ITimeFactory $timeFactory, + public function __construct( + private UserStatusMapper $mapper, + private StatusService $statusService, + private ITimeFactory $timeFactory, private CalendarStatusService $calendarStatusService, - private LoggerInterface $logger) { - $this->mapper = $mapper; - $this->statusService = $statusService; - $this->timeFactory = $timeFactory; + private LoggerInterface $logger, + ) { } /** @@ -66,13 +61,13 @@ class UserLiveStatusListener implements IEventListener { // If the status is user-defined and one of the persistent status, we // will not override it. - if ($userStatus->getIsUserDefined() && - \in_array($userStatus->getStatus(), StatusService::PERSISTENT_STATUSES, true)) { + if ($userStatus->getIsUserDefined() + && \in_array($userStatus->getStatus(), StatusService::PERSISTENT_STATUSES, true)) { return; } // Don't overwrite the "away" calendar status if it's set - if($userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY) { + if ($userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY) { $event->setUserStatus(new ConnectorUserStatus($userStatus)); return; } diff --git a/apps/user_status/lib/Migration/Version1008Date20230921144701.php b/apps/user_status/lib/Migration/Version1008Date20230921144701.php index 561dfd343cf..30ebbf37b0e 100644 --- a/apps/user_status/lib/Migration/Version1008Date20230921144701.php +++ b/apps/user_status/lib/Migration/Version1008Date20230921144701.php @@ -18,7 +18,9 @@ use OCP\Migration\SimpleMigrationStep; class Version1008Date20230921144701 extends SimpleMigrationStep { - public function __construct(private IDBConnection $connection) { + public function __construct( + private IDBConnection $connection, + ) { } public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { diff --git a/apps/user_status/lib/ResponseDefinitions.php b/apps/user_status/lib/ResponseDefinitions.php index 6668c40b917..82f606dd301 100644 --- a/apps/user_status/lib/ResponseDefinitions.php +++ b/apps/user_status/lib/ResponseDefinitions.php @@ -22,7 +22,6 @@ namespace OCA\UserStatus; * icon: string, * message: string, * clearAt: ?UserStatusClearAt, - * visible: ?bool, * } * * @psalm-type UserStatusType = "online"|"away"|"dnd"|"busy"|"offline"|"invisible" diff --git a/apps/user_status/lib/Service/JSDataService.php b/apps/user_status/lib/Service/JSDataService.php index a6b9b0b4056..a777e97fe57 100644 --- a/apps/user_status/lib/Service/JSDataService.php +++ b/apps/user_status/lib/Service/JSDataService.php @@ -14,22 +14,16 @@ use OCP\UserStatus\IUserStatus; class JSDataService implements \JsonSerializable { - /** @var IUserSession */ - private $userSession; - - /** @var StatusService */ - private $statusService; - /** * JSDataService constructor. * * @param IUserSession $userSession * @param StatusService $statusService */ - public function __construct(IUserSession $userSession, - StatusService $statusService) { - $this->userSession = $userSession; - $this->statusService = $statusService; + public function __construct( + private IUserSession $userSession, + private StatusService $statusService, + ) { } public function jsonSerialize(): array { diff --git a/apps/user_status/lib/Service/PredefinedStatusService.php b/apps/user_status/lib/Service/PredefinedStatusService.php index b17442a0caa..599d5b8b52f 100644 --- a/apps/user_status/lib/Service/PredefinedStatusService.php +++ b/apps/user_status/lib/Service/PredefinedStatusService.php @@ -20,6 +20,7 @@ use OCP\UserStatus\IUserStatus; * @package OCA\UserStatus\Service */ class PredefinedStatusService { + private const BE_RIGHT_BACK = 'be-right-back'; private const MEETING = 'meeting'; private const COMMUTING = 'commuting'; private const SICK_LEAVE = 'sick-leave'; @@ -31,16 +32,14 @@ class PredefinedStatusService { public const CALL = 'call'; public const OUT_OF_OFFICE = 'out-of-office'; - /** @var IL10N */ - private $l10n; - /** * DefaultStatusService constructor. * * @param IL10N $l10n */ - public function __construct(IL10N $l10n) { - $this->l10n = $l10n; + public function __construct( + private IL10N $l10n, + ) { } /** @@ -67,6 +66,15 @@ class PredefinedStatusService { ], ], [ + 'id' => self::BE_RIGHT_BACK, + 'icon' => '⏳', + 'message' => $this->getTranslatedStatusForId(self::BE_RIGHT_BACK), + 'clearAt' => [ + 'type' => 'period', + 'time' => 900, + ], + ], + [ 'id' => self::REMOTE_WORK, 'icon' => '🏡', 'message' => $this->getTranslatedStatusForId(self::REMOTE_WORK), @@ -145,6 +153,9 @@ class PredefinedStatusService { case self::REMOTE_WORK: return '🏡'; + case self::BE_RIGHT_BACK: + return '⏳'; + case self::CALL: return '💬'; @@ -181,6 +192,9 @@ class PredefinedStatusService { case self::CALL: return $this->l10n->t('In a call'); + case self::BE_RIGHT_BACK: + return $this->l10n->t('Be right back'); + default: return null; } @@ -197,6 +211,7 @@ class PredefinedStatusService { self::SICK_LEAVE, self::VACATIONING, self::OUT_OF_OFFICE, + self::BE_RIGHT_BACK, self::REMOTE_WORK, IUserStatus::MESSAGE_CALL, IUserStatus::MESSAGE_AVAILABILITY, diff --git a/apps/user_status/lib/Service/StatusService.php b/apps/user_status/lib/Service/StatusService.php index 3246777b46b..188eb26d1d7 100644 --- a/apps/user_status/lib/Service/StatusService.php +++ b/apps/user_status/lib/Service/StatusService.php @@ -22,6 +22,7 @@ use OCP\IConfig; use OCP\IEmojiHelper; use OCP\IUserManager; use OCP\UserStatus\IUserStatus; +use Psr\Log\LoggerInterface; use function in_array; /** @@ -63,12 +64,15 @@ class StatusService { /** @var int */ public const MAXIMUM_MESSAGE_LENGTH = 80; - public function __construct(private UserStatusMapper $mapper, + public function __construct( + private UserStatusMapper $mapper, private ITimeFactory $timeFactory, private PredefinedStatusService $predefinedStatusService, private IEmojiHelper $emojiHelper, private IConfig $config, - private IUserManager $userManager) { + private IUserManager $userManager, + private LoggerInterface $logger, + ) { $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; @@ -163,7 +167,7 @@ class StatusService { $userStatus->setIsBackup(false); if ($userStatus->getId() === null) { - return $this->mapper->insert($userStatus); + return $this->insertWithoutThrowingUniqueConstrain($userStatus); } return $this->mapper->update($userStatus); @@ -207,7 +211,7 @@ class StatusService { $userStatus->setStatusMessageTimestamp($this->timeFactory->now()->getTimestamp()); if ($userStatus->getId() === null) { - return $this->mapper->insert($userStatus); + return $this->insertWithoutThrowingUniqueConstrain($userStatus); } return $this->mapper->update($userStatus); @@ -244,8 +248,28 @@ class StatusService { $userStatus->setUserId($userId); } - // CALL trumps CALENDAR status, but we don't need to do anything but overwrite the message - if ($userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY && $messageId === IUserStatus::MESSAGE_CALL) { + $updateStatus = false; + if ($messageId === IUserStatus::MESSAGE_OUT_OF_OFFICE) { + // OUT_OF_OFFICE trumps AVAILABILITY, CALL and CALENDAR status + $updateStatus = $userStatus->getMessageId() === IUserStatus::MESSAGE_AVAILABILITY || $userStatus->getMessageId() === IUserStatus::MESSAGE_CALL || $userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY; + } elseif ($messageId === IUserStatus::MESSAGE_AVAILABILITY) { + // AVAILABILITY trumps CALL and CALENDAR status + $updateStatus = $userStatus->getMessageId() === IUserStatus::MESSAGE_CALL || $userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY; + } elseif ($messageId === IUserStatus::MESSAGE_CALL) { + // CALL trumps CALENDAR status + $updateStatus = $userStatus->getMessageId() === IUserStatus::MESSAGE_CALENDAR_BUSY; + } + + if ($messageId === IUserStatus::MESSAGE_OUT_OF_OFFICE || $messageId === IUserStatus::MESSAGE_AVAILABILITY || $messageId === IUserStatus::MESSAGE_CALL || $messageId === IUserStatus::MESSAGE_CALENDAR_BUSY) { + if ($updateStatus) { + $this->logger->debug('User ' . $userId . ' is currently NOT available, overwriting status [status: ' . $userStatus->getStatus() . ', messageId: ' . json_encode($userStatus->getMessageId()) . ']', ['app' => 'dav']); + } else { + $this->logger->debug('User ' . $userId . ' is currently NOT available, but we are NOT overwriting status [status: ' . $userStatus->getStatus() . ', messageId: ' . json_encode($userStatus->getMessageId()) . ']', ['app' => 'dav']); + } + } + + // There should be a backup already or none is needed. So we take a shortcut. + if ($updateStatus) { $userStatus->setStatus($status); $userStatus->setStatusTimestamp($this->timeFactory->getTime()); $userStatus->setIsUserDefined(true); @@ -265,7 +289,7 @@ class StatusService { // If we just created the backup // we need to create a new status to insert - // Unfortunatley there's no way to unset the DB ID on an Entity + // Unfortunately there's no way to unset the DB ID on an Entity $userStatus = new UserStatus(); $userStatus->setUserId($userId); } @@ -289,7 +313,7 @@ class StatusService { if ($userStatus->getId() !== null) { return $this->mapper->update($userStatus); } - return $this->mapper->insert($userStatus); + return $this->insertWithoutThrowingUniqueConstrain($userStatus); } /** @@ -336,7 +360,7 @@ class StatusService { $userStatus->setStatusMessageTimestamp($this->timeFactory->now()->getTimestamp()); if ($userStatus->getId() === null) { - return $this->mapper->insert($userStatus); + return $this->insertWithoutThrowingUniqueConstrain($userStatus); } return $this->mapper->update($userStatus); @@ -475,10 +499,10 @@ class StatusService { return; } // If there is a custom message, don't overwrite it - if(empty($status->getCustomMessage())) { + if (empty($status->getCustomMessage())) { $status->setCustomMessage($predefinedMessage['message']); } - if(empty($status->getCustomIcon())) { + if (empty($status->getCustomIcon())) { $status->setCustomIcon($predefinedMessage['icon']); } } @@ -560,4 +584,16 @@ class StatusService { // For users that matched restore the previous status $this->mapper->restoreBackupStatuses($restoreIds); } + + protected function insertWithoutThrowingUniqueConstrain(UserStatus $userStatus): UserStatus { + try { + return $this->mapper->insert($userStatus); + } catch (Exception $e) { + // Ignore if a parallel request already set the status + if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; + } + } + return $userStatus; + } } |