* @package Combodo\iTop\Controller\Newsroom * @since 3.2.0 */ class iTopNewsroomController extends Controller { public const ROUTE_NAMESPACE = 'itopnewsroom'; public const DEFAULT_NEWSROOM_DISPLAY_SIZE = 7; public const DEFAULT_NEWSROOM_MIN_DISPLAY_SIZE = 1; public const DEFAULT_NEWSROOM_MAX_DISPLAY_SIZE = 20; /** * @return iTopWebPage * @throws \ApplicationException * @throws \CoreException * @throws \OQLException */ public function OperationViewAll() { $oPage = new iTopWebPage(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Title')); $oPage->LinkScriptFromAppRoot('js/pages/backoffice/itop-newsroom.view-all.js'); // Add title block // Make bulk actions block $oBulkActionsBlock = PanelUIBlockFactory::MakeForInformation(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Title')); $oBulkActionsBlock->AddSubTitleBlock(new Html(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:SubTitle'))); $sPictureUrl = UserRights::GetUserPictureAbsUrl(); $oBulkActionsBlock->SetIcon($sPictureUrl, Panel::ENUM_ICON_COVER_METHOD_CONTAIN, true); $oNotificationsCenterButton = ButtonUIBlockFactory::MakeIconLink( 'fas fa-cogs', Dict::S('UI:NotificationsCenter:Panel:SubTitle'), Router::GetInstance()->GenerateUrl(NotificationsCenterController::ROUTE_NAMESPACE.'.display_page'), ); $oBulkActionsBlock->SetToolBlocks([$oNotificationsCenterButton]); $oToolbar = ToolbarUIBlockFactory::MakeStandard(); $oToolbar->AddCSSClass('ibo-notifications--view-all--toolbar'); $oAllModeButtonsContainer = new UIContentBlock('ibo-notifications--view-all--all-mode-buttons', ['ibo-notifications--view-all--bulk-buttons', 'ibo-notifications--view-all--all-mode-buttons']); // Create CSRF token we'll use in this page $sCSRFToken = utils::GetNewTransactionId(); // Make button to mark all as read $sMarkMultipleAsReadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_multiple_as_read', ['token' => $sCSRFToken]); $sMarkMultipleAsUnreadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_multiple_as_unread', ['token' => $sCSRFToken]); $sDeleteMultipleUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.delete_multiple', ['token' => $sCSRFToken]); $oMarkAllAsReadButton = ButtonUIBlockFactory::MakeForSecondaryAction( Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label'), 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label' ); $oMarkAllAsReadButton->SetIconClass('far fa-envelope-open') ->AddCSSClass('ibo-notifications--view-all--read-action') ->SetOnClickJsCode( <<SetIconClass('far fa-envelope') ->AddCSSClass('ibo-notifications--view-all--unread-action') ->SetOnClickJsCode( <<SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE); $oDeleteAllButton->SetIconClass('fas fa-trash-alt') ->AddCSSClass('ibo-notifications--view-all--delete-action') ->SetOnClickJsCode( <<AddSubBlock($oMarkAllAsReadButton); $oAllModeButtonsContainer->AddSubBlock($oMarkAllAsUnreadButton); $oAllModeButtonsContainer->AddSubBlock($oDeleteAllButton); $oToolbar->AddSubBlock($oAllModeButtonsContainer); $oSelectedModelButtonsContainer = new UIContentBlock('ibo-notifications--view-all--selected-mode-buttons', ['ibo-is-hidden', 'ibo-notifications--view-all--bulk-buttons', 'ibo-notifications--view-all--selected-mode-buttons']); // Make button mark all selected as read $oMarkSelectedAsReadButton = ButtonUIBlockFactory::MakeForSecondaryAction( Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label'), 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsRead:Label' ); $oMarkSelectedAsReadButton->SetIconClass('far fa-envelope-open') ->AddCSSClass('ibo-notifications--view-all--read-action') ->SetOnClickJsCode( <<SetIconClass('far fa-envelope') ->AddCSSClass('ibo-notifications--view-all--unread-action') ->SetOnClickJsCode( <<SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE); $oDeleteSelectedButton->SetIconClass('fas fa-trash-alt') ->AddCSSClass('ibo-notifications--view-all--delete-action') ->SetOnClickJsCode( <<AddSubBlock($oMarkSelectedAsReadButton); $oSelectedModelButtonsContainer->AddSubBlock($oMarkSelectedAsUnreadButton); $oSelectedModelButtonsContainer->AddSubBlock($oDeleteSelectedButton); $oToolbar->AddSubBlock($oSelectedModelButtonsContainer); // Make toggler to switch between "all" and "selected" mode $oTogglerContentBlock = new UIContentBlock('ibo-notifications--view-all--toggler', ['ibo-notifications--view-all--toggler']); $oToggler = new Toggler(); $oInputWithLabel = InputUIBlockFactory::MakeInputWithLabel('slider', Dict::S('UI:Newsroom:iTopNotification:SelectMode:Label'), $oToggler); $oTogglerContentBlock->AddSubBlock($oInputWithLabel); $oToolbar->AddSubBlock($oTogglerContentBlock); $oBulkActionsBlock->AddSubBlock($oToolbar); $oPage->AddUiBlock($oBulkActionsBlock); // Search for all notifications for the current user $oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom'); $oSearch->AddCondition('contact_id', UserRights::GetContactId(), '='); $oSearch->AllowAllData(); $oSet = new DBObjectSet($oSearch, ['read' => true, 'date' => false], []); // Add main content block $oMainContentBlock = new UIContentBlock(null, ['ibo-notifications--view-all--container']); $oPage->AddUiBlock($oMainContentBlock); while ($oEvent = $oSet->Fetch()) { $iEventId = $oEvent->GetKey(); // Prepare object summary block $sReadColor = $oEvent->Get('read') === 'no' ? 'ibo-notifications--view-all--item--unread' : 'ibo-notifications--view-all--item--read'; $sReadLabel = $oEvent->Get('read') === 'no' ? Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Unread:Label') : Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Read:Label'); $oEventBlock = new ObjectSummary($oEvent); $oEventBlock->SetHasLazyLoadIcon(true); $oEventBlock->SetCSSColorClass($sReadColor); $oEventBlock->SetSubTitle($sReadLabel); $oEventBlock->SetClassLabel(''); $oImage = $oEvent->Get('icon'); if (!$oImage->IsEmpty()) { $sIconUrl = $oImage->GetDisplayURL(get_class($oEvent), $iEventId, 'icon'); $oEventBlock->SetIcon($sIconUrl, Panel::ENUM_ICON_COVER_METHOD_COVER, true); } // Common actions $sDeleteUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.delete_event', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]); $oDeleteButton = ButtonUIBlockFactory::MakeForAlternativeDestructiveAction( '', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label' ); $oDeleteButton->SetOnClickJsCode( <<SetIconClass('fas fa-trash-alt') ->SetTooltip(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label')); $oViewButton = ButtonUIBlockFactory::MakeIconLink( 'fas fa-external-link-alt', Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label'), Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.view_event', ['event_id' => $iEventId]), '_blank' ); // Mark as read action $oMarkAsReadButton = ButtonUIBlockFactory::MakeForAlternativeSecondaryAction( '', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label' ); $oMarkAsReadButton->AddCSSClass('ibo-notifications--view-all--read-action') ->SetIconClass('far fa-envelope-open') ->SetTooltip(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label')); // Mark as unread action $sMarkAsReadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_as_read', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]); $oMarkAsReadButton->SetOnClickJsCode( <<AddCSSClass('ibo-notifications--view-all--unread-action') ->SetIconClass('far fa-envelope') ->SetTooltip(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label')); $sMarkAsUnreadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_as_unread', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]); $oMarkAsUnreadButton->SetOnClickJsCode( <<GetActions()->GetId(); $oEventBlock->RemoveSubBlock($oOldButtonId); $oEventBlock->SetToolBlocks([$oMarkAsUnreadButton, $oMarkAsReadButton, $oViewButton, $oDeleteButton]); $oActionsBlock = new UIContentBlock(); $oActionsBlock->AddSubBlock($oMarkAsUnreadButton); $oActionsBlock->AddSubBlock($oMarkAsReadButton); $oActionsBlock->AddSubBlock($oViewButton); $oActionsBlock->AddSubBlock($oDeleteButton); $oEventBlock->SetActions($oActionsBlock); $oMainContentBlock->AddSubBlock($oEventBlock); } // Add empty content block $oEmptyContentBlock = new UIContentBlock('ibo-notifications--view-all--empty', ['ibo-notifications--view-all--empty', 'ibo-svg-illustration--container']); $oEmptyContentBlock->AddSubBlock(new Html(file_get_contents(APPROOT.'/images/illustrations/undraw_social_serenity.svg'))); $oEmptyContentBlock->AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Empty:Title'))); $oPage->AddUiBlock($oEmptyContentBlock); // Hide empty content block if there are notifications if ($oSet->Count() === 0) { $oMainContentBlock->AddCSSClass('ibo-is-hidden'); } else { $oEmptyContentBlock->AddCSSClass('ibo-is-hidden'); } return $oPage; } /** * @return JsonPPage * @throws \ArchivedObjectException * @throws \CoreException * @throws \CoreUnexpectedValue * @throws \MySQLException * @throws \OQLException */ public function OperationFetchUnreadMessages() { $sCallback = utils::ReadParam('callback', ''); $oPage = new JsonPPage($sCallback); $aMessages = []; $iContactId = UserRights::GetContactId(); if (utils::IsNotNullOrEmptyString($iContactId)) { $oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom WHERE contact_id = :contact_id AND read = "no"'); $oSearch->AllowAllData(); $oSet = new DBObjectSet($oSearch, [], ['contact_id' => $iContactId]); while ($oMessage = $oSet->Fetch()) { $sTitle = $oMessage->Get('title'); $sMessage = $oMessage->Get('message'); $sText = <<Get('icon') !== null ? $oMessage->Get('icon')->GetDisplayURL(EventNotificationNewsroom::class, $oMessage->GetKey(), 'icon') : Branding::GetCompactMainLogoAbsoluteUrl(); $aMessages[] = [ 'id' => $oMessage->GetKey(), 'text' => $sText, 'url' => Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.view_event', ['event_id' => $oMessage->GetKey()]), 'target' => '_self', 'start_date' => $oMessage->Get('date'), 'priority' => $oMessage->Get('priority'), 'image' => $sIcon, ]; } } $oPage->SetData($aMessages); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return int * @throws \ArchivedObjectException * @throws \CoreCannotSaveObjectException * @throws \CoreException * @throws \CoreUnexpectedValue * @throws \CoreWarning * @throws \MySQLException * @throws \OQLException */ public function OperationMarkAllAsReadMessages() { $iCount = 0; $iContactId = UserRights::GetContactId(); if (utils::IsNotNullOrEmptyString($iContactId)) { $oSearch = DBObjectSearch::FromOQL('SELECT EventNotificationNewsroom WHERE contact_id = :contact_id AND read = "no"'); $oSearch->AllowAllData(); $oSet = new DBObjectSet($oSearch, [], ['contact_id' => $iContactId]); while ($oEvent = $oSet->Fetch()) { $oEvent->Set('read', 'yes'); $oEvent->SetCurrentDate('read_date'); $oEvent->DBWrite(); $iCount++; } } return $iCount; } /** * @return void * @throws \ArchivedObjectException * @throws \CoreCannotSaveObjectException * @throws \CoreException * @throws \CoreUnexpectedValue * @throws \CoreWarning * @throws \MySQLException * @throws \OQLException * @throws \Exception */ public function OperationViewEvent() { $sEventId = utils::ReadParam('event_id', 0); if ($sEventId > 0) { try { $oEvent = MetaModel::GetObject(EventNotificationNewsroom::class, $sEventId, true, true); if ($oEvent !== null && $oEvent->Get('contact_id') === UserRights::GetContactId()) { $oEvent->Set('read', 'yes'); $oEvent->SetCurrentDate('read_date'); $oEvent->DBWrite(); $sUrl = $oEvent->Get('url'); header("Location: $sUrl"); } } catch (ArchivedObjectException|CoreException $e) { $this->DisplayPageNotFound(); } } } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationMarkAsUnread(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnSingleNotification('mark_as_unread')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationMarkAsRead(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnSingleNotification('mark_as_read')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationDeleteEvent(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnSingleNotification('delete')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationMarkMultipleAsRead(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnMultipleNotifications('mark_as_read')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationMarkMultipleAsUnread(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnMultipleNotifications('mark_as_unread')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @return \Combodo\iTop\Application\WebPage\JsonPage */ public function OperationDeleteMultiple(): JsonPage { $oPage = new JsonPage(); $oPage->SetData($this->PerformActionOnMultipleNotifications('delete')); $oPage->SetOutputDataOnly(true); return $oPage; } /** * @param string $sAction * * @return string[] * @throws \SecurityException */ protected function PerformActionOnSingleNotification(string $sAction): array { $iNotificationId = utils::ReadParam('notification_id', 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER); return $this->PerformAction($sAction, [$iNotificationId]); } /** * @param string $sAction * * @return string[] * @throws \SecurityException */ protected function PerformActionOnMultipleNotifications(string $sAction): array { $aNotificationIds = utils::ReadParam('notification_ids', []); return $this->PerformAction($sAction, $aNotificationIds); } /** * @param string $sAction * @param array $aNotificationIds * * @return string[] * @throws \SecurityException */ protected function PerformAction(string $sAction, array $aNotificationIds): array { $sCSRFToken = utils::ReadParam('token', '', false, 'raw_data'); if (utils::IsTransactionValid($sCSRFToken, false) === false) { throw new SecurityException('Invalid CSRF token'); } $sActionAsCamelCase = utils::ToCamelCase($sAction); $aReturnData = [ 'status' => 'error', 'message' => 'Invalid notification(s)', ]; // Check action type if (false === in_array($sAction, ['mark_as_read', 'mark_as_unread', 'delete'])) { $aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:InvalidAction:Message"); return $aReturnData; } // No ID passed to the API if (count($aNotificationIds) === 0) { $aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:$sActionAsCamelCase:NoEvent:Message"); return $aReturnData; } try { $sRepositoryMethodName = "SearchNotificationsTo{$sActionAsCamelCase}ByContact"; $oSet = NotificationsRepository::GetInstance()->$sRepositoryMethodName(UserRights::GetContactId(), $aNotificationIds); // No notification found $iCount = $oSet->Count(); if ($iCount === 0) { $aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:$sActionAsCamelCase:NoEvent:Message"); return $aReturnData; } while ($oEvent = $oSet->Fetch()) { if ($sAction === 'mark_as_read') { $oEvent->Set('read', 'yes'); $oEvent->SetCurrentDate('read_date'); $oEvent->DBWrite(); } elseif ($sAction === 'mark_as_unread') { $oEvent->Set('read', 'no'); $oEvent->DBWrite(); } elseif ($sAction === 'delete') { $oEvent->DBDelete(); } } $aReturnData['status'] = 'success'; if ($iCount === 1) { $aReturnData['message'] = Dict::S("UI:Newsroom:iTopNotification:ViewAllPage:Action:{$sActionAsCamelCase}:Success:Message"); } else { $aReturnData['message'] = Dict::Format("UI:Newsroom:iTopNotification:ViewAllPage:Action:{$sActionAsCamelCase}Multiple:Success:Message", $iCount); } } catch (Exception $oException) { $aReturnData['message'] = $oException->getMessage(); } return $aReturnData; } }