mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
* N°2039 - Rework view all notifications page * N°2039 - Replace modals with toasts * N°2039 - Add bulk mode to view all notifications page * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Update css/backoffice/pages/_notifications.scss * Update dictionaries/ui/application/newsroom/fr.dictionary.itop.newsroom.php * Apply suggestions from code review Co-authored-by: Molkobain <lajarige.guillaume@free.fr> * Add since in phpdoc * Change newsroom empty notification illustration * N°2039 - Refactor code to factorize logic --------- Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
803 lines
30 KiB
PHP
803 lines
30 KiB
PHP
<?php
|
|
|
|
namespace Combodo\iTop\Controller\Newsroom;
|
|
|
|
use ArchivedObjectException;
|
|
use Combodo\iTop\Application\Branding;
|
|
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
|
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
|
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
|
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\Input\Toggler;
|
|
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
|
|
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
|
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
|
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary;
|
|
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
|
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
|
use Combodo\iTop\Application\WebPage\JsonPage;
|
|
use Combodo\iTop\Application\WebPage\JsonPPage;
|
|
use Combodo\iTop\Service\Notification\NotificationsRepository;
|
|
use Combodo\iTop\Service\Router\Router;
|
|
use CoreException;
|
|
use DBObjectSearch;
|
|
use DBObjectSet;
|
|
use Dict;
|
|
use JSPopupMenuItem;
|
|
use MetaModel;
|
|
use SecurityException;
|
|
use URLPopupMenuItem;
|
|
use UserRights;
|
|
use utils;
|
|
|
|
|
|
/**
|
|
* Class iTopNewsroomController
|
|
*
|
|
* @author Stephen Abello <stephen.abello@combodo.com>
|
|
* @package Combodo\iTop\Controller\Newsroom
|
|
* @since 3.2.0
|
|
*/
|
|
class iTopNewsroomController extends Controller
|
|
{
|
|
public const ROUTE_NAMESPACE = 'itopnewsroom';
|
|
|
|
/**
|
|
* @return iTopWebPage
|
|
* @throws \ApplicationException
|
|
* @throws \CoreException
|
|
* @throws \OQLException
|
|
*/
|
|
public function OperationViewAll()
|
|
{
|
|
$oPage = new iTopWebPage(Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Title'));
|
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'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'));
|
|
$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(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToMarkAsRead = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--unread');
|
|
let aNotificationIds = [];
|
|
oNotificationToMarkAsRead.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
$.ajax({
|
|
url: '{$sMarkMultipleAsReadUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
let MarkAsReadButton = oNotificationToMarkAsRead.find('.ibo-button-group:not(.ibo-is-hidden)');
|
|
let MarkAsUnreadButton = oNotificationToMarkAsRead.find('.ibo-button-group.ibo-is-hidden');
|
|
MarkAsReadButton.addClass('ibo-is-hidden');
|
|
MarkAsUnreadButton.removeClass('ibo-is-hidden');
|
|
oNotificationToMarkAsRead.removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
// Make button to mark all as unread
|
|
$oMarkAllAsUnreadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label',
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label',
|
|
);
|
|
$oMarkAllAsUnreadButton->SetIconClass('far fa-envelope')
|
|
->AddCSSClass('ibo-notifications--view-all--unread-action')
|
|
->SetOnClickJsCode(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToMarkAsUnread = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--read');
|
|
let aNotificationIds = [];
|
|
oNotificationToMarkAsUnread.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
$.ajax({
|
|
url: '{$sMarkMultipleAsUnreadUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
let MarkAsUnreadButton = oNotificationToMarkAsUnread.find('.ibo-button-group:not(.ibo-is-hidden)');
|
|
let MarkAsReadButton = oNotificationToMarkAsUnread.find('.ibo-button-group.ibo-is-hidden');
|
|
|
|
MarkAsReadButton.removeClass('ibo-is-hidden');
|
|
MarkAsUnreadButton.addClass('ibo-is-hidden');
|
|
oNotificationToMarkAsUnread.removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
// Make button to delete all
|
|
$oDeleteAllButton = ButtonUIBlockFactory::MakeForDestructiveAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label',
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label'
|
|
);
|
|
$oDeleteAllButtonConfirmTitle = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Title');
|
|
$oDeleteAllButtonConfirmMessage = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message');
|
|
$oDeleteAllButton->SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE);
|
|
$oDeleteAllButton->SetIconClass('fas fa-trash-alt')
|
|
->AddCSSClass('ibo-notifications--view-all--delete-action')
|
|
->SetOnClickJsCode(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToDelete = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"]');
|
|
let aNotificationIds = [];
|
|
oNotificationToDelete.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
CombodoModal.OpenConfirmationModal({
|
|
title: '$oDeleteAllButtonConfirmTitle',
|
|
content: '$oDeleteAllButtonConfirmMessage',
|
|
callback_on_confirm: function() {
|
|
$.ajax({
|
|
url: '{$sDeleteMultipleUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
oNotificationToDelete.remove();
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
buttons: {
|
|
confirm: {
|
|
text: '$oDeleteAllButtonConfirmTitle',
|
|
classes: ['ibo-is-danger']
|
|
}
|
|
},
|
|
do_not_show_again_pref_key: 'notifications-center.delete-all-confirmation-modal.do-not-show-again',
|
|
}, []);
|
|
|
|
JS
|
|
);
|
|
// Add "all" buttons to their container
|
|
$oAllModeButtonsContainer->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(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToMarkAsRead = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--unread.ibo-is-selected');
|
|
let aNotificationIds = [];
|
|
oNotificationToMarkAsRead.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
$.ajax({
|
|
url: '{$sMarkMultipleAsReadUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
let MarkAsReadButton = oNotificationToMarkAsRead.find('.ibo-button-group:not(.ibo-is-hidden)');
|
|
let MarkAsUnreadButton = oNotificationToMarkAsRead.find('.ibo-button-group.ibo-is-hidden');
|
|
MarkAsReadButton.addClass('ibo-is-hidden');
|
|
MarkAsUnreadButton.removeClass('ibo-is-hidden');
|
|
oNotificationToMarkAsRead.removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
// Make button mark all selected as unread
|
|
$oMarkSelectedAsUnreadButton = ButtonUIBlockFactory::MakeForSecondaryAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label',
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label'
|
|
);
|
|
$oMarkSelectedAsUnreadButton->SetIconClass('far fa-envelope')
|
|
->AddCSSClass('ibo-notifications--view-all--unread-action')
|
|
->SetOnClickJsCode(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToMarkAsUnread = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-notifications--view-all--item--read.ibo-is-selected');
|
|
let aNotificationIds = [];
|
|
oNotificationToMarkAsUnread.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
$.ajax({
|
|
url: '{$sMarkMultipleAsUnreadUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
let MarkAsUnreadButton = oNotificationToMarkAsUnread.find('.ibo-button-group:not(.ibo-is-hidden)');
|
|
let MarkAsReadButton = oNotificationToMarkAsUnread.find('.ibo-button-group.ibo-is-hidden');
|
|
|
|
MarkAsReadButton.removeClass('ibo-is-hidden');
|
|
MarkAsUnreadButton.addClass('ibo-is-hidden');
|
|
|
|
oNotificationToMarkAsUnread.removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
// Make button delete all selected
|
|
$oDeleteSelectedButton = ButtonUIBlockFactory::MakeForDestructiveAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label',
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label'
|
|
);
|
|
$oDeleteSelectedButtonConfirmTitle = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Title');
|
|
$oDeleteSelectedButtonConfirmMessage = Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message');
|
|
$oDeleteSelectedButton->SetActionType(Button::ENUM_ACTION_TYPE_ALTERNATIVE);
|
|
$oDeleteSelectedButton->SetIconClass('fas fa-trash-alt')
|
|
->AddCSSClass('ibo-notifications--view-all--delete-action')
|
|
->SetOnClickJsCode(
|
|
<<<JS
|
|
let oSelf = this;
|
|
let oNotificationToDelete = $('.ibo-notifications--view-all--container [data-role="ibo-object-summary"].ibo-is-selected');
|
|
let aNotificationIds = [];
|
|
oNotificationToDelete.each(function(){
|
|
aNotificationIds.push($(this).attr('data-object-id'));
|
|
});
|
|
CombodoModal.OpenConfirmationModal({
|
|
title: '$oDeleteSelectedButtonConfirmTitle',
|
|
content: '$oDeleteSelectedButtonConfirmMessage',
|
|
callback_on_confirm: function() {
|
|
$.ajax({
|
|
url: '{$sDeleteMultipleUrl}',
|
|
data: {
|
|
notification_ids: aNotificationIds
|
|
},
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
oNotificationToDelete.remove();
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
buttons: {
|
|
confirm: {
|
|
text: '$oDeleteSelectedButtonConfirmTitle',
|
|
classes: ['ibo-is-danger']
|
|
}
|
|
},
|
|
do_not_show_again_pref_key: 'notifications-center.delete-all-confirmation-modal.do-not-show-again',
|
|
}, []);
|
|
JS
|
|
);
|
|
|
|
// Add "selected" buttons to their container
|
|
$oSelectedModelButtonsContainer->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 EventiTopNotification');
|
|
$oSearch->AddCondition('contact_id', UserRights::GetContactId(), '=');
|
|
$oSet = new DBObjectSet($oSearch, array('read' => true, 'date' => true), array());
|
|
|
|
// 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->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);
|
|
}
|
|
|
|
// Prepare Event actions
|
|
$oMarkAsReadPopoverMenu = new PopoverMenu();
|
|
$oMarkAsUnreadPopoverMenu = new PopoverMenu();
|
|
|
|
// Common actions
|
|
$sDeleteUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.delete_event', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]);
|
|
$oDeleteButton = new JSPopupMenuItem(
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label',
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label'),
|
|
<<<JS
|
|
let oSelf = this;
|
|
|
|
$.ajax({
|
|
url: '{$sDeleteUrl}',
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
$(oSelf).parents('.ibo-object-summary').remove();
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.deleted');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS,
|
|
'_blank'
|
|
);
|
|
$oViewButton = new URLPopupMenuItem(
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label',
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label'),
|
|
Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.view_event', ['event_id' => $oEvent->GetKey()]),
|
|
'_blank'
|
|
);
|
|
|
|
// Mark as read action
|
|
$oMarkAsReadButton = ButtonUIBlockFactory::MakeForAlternativeSecondaryAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label',
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label',
|
|
);
|
|
|
|
// Mark as read action
|
|
$oMarkAsReadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oViewButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
|
$oMarkAsReadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oDeleteButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
|
|
|
// Mark as unread action
|
|
$oMarkAsUnreadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oViewButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
|
$oMarkAsUnreadPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oDeleteButton))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
|
|
|
|
|
|
// Mark as unread action
|
|
$sMarkAsReadUrl = Router::GetInstance()->GenerateUrl(self::ROUTE_NAMESPACE.'.mark_as_read', ['notification_id' => $oEvent->GetKey(), 'token' => $sCSRFToken]);
|
|
$oMarkAsReadButton->SetOnClickJsCode(
|
|
<<<JS
|
|
let oSelf = this;
|
|
$.ajax({
|
|
url: '{$sMarkAsReadUrl}',
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
$(oSelf).parent('.ibo-button-group').addClass('ibo-is-hidden');
|
|
$(oSelf).parent('.ibo-button-group').siblings('.ibo-button-group').removeClass('ibo-is-hidden');
|
|
$(oSelf).parents('.ibo-object-summary').removeClass('ibo-notifications--view-all--item--unread').addClass('ibo-notifications--view-all--item--read');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.read');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
$oMarkAsReadButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oMarkAsReadButton, $oMarkAsReadPopoverMenu);
|
|
|
|
$oMarkAsUnreadButton = ButtonUIBlockFactory::MakeForAlternativeSecondaryAction(
|
|
Dict::S('UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label'),
|
|
'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label',
|
|
'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(
|
|
<<<JS
|
|
let oSelf = this;
|
|
$.ajax({
|
|
url: '{$sMarkAsUnreadUrl}',
|
|
type: 'POST',
|
|
success: function(data) {
|
|
if (data.status === 'success') {
|
|
$(oSelf).parent('.ibo-button-group').addClass('ibo-is-hidden');
|
|
$(oSelf).parent('.ibo-button-group').siblings('.ibo-button-group').removeClass('ibo-is-hidden');
|
|
$(oSelf).parents('.ibo-object-summary').removeClass('ibo-notifications--view-all--item--read').addClass('ibo-notifications--view-all--item--unread');
|
|
CombodoToast.OpenSuccessToast(data.message);
|
|
$('.ibo-notifications--view-all--container').trigger('itop.notification.unread');
|
|
}
|
|
else {
|
|
CombodoToast.OpenErrorToast(data.message);
|
|
}
|
|
}
|
|
});
|
|
JS
|
|
);
|
|
|
|
$oMarkAsUnreadButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oMarkAsUnreadButton, $oMarkAsUnreadPopoverMenu);
|
|
|
|
// Add actions to the object summary block and remove old button
|
|
$oOldButtonId = $oEventBlock->GetActions()->GetId();
|
|
$oEventBlock->RemoveSubBlock($oOldButtonId);
|
|
$oEventBlock->SetToolBlocks([$oMarkAsReadButtonGroup, $oMarkAsUnreadButtonGroup]);
|
|
$oActionsBlock = new UIContentBlock();
|
|
$oActionsBlock->AddSubBlock($oMarkAsReadButtonGroup);
|
|
$oActionsBlock->AddSubBlock($oMarkAsUnreadButtonGroup);
|
|
$oEventBlock->SetActions($oActionsBlock);
|
|
|
|
// Display the right button depending on the read status
|
|
if($oEvent->Get('read') === 'no'){
|
|
$oMarkAsUnreadButtonGroup->SetCSSClasses(['ibo-is-hidden']);
|
|
}
|
|
else{
|
|
$oMarkAsReadButtonGroup->SetCSSClasses(['ibo-is-hidden']);
|
|
}
|
|
|
|
$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 EventiTopNotification WHERE contact_id = :contact_id AND read = "no"');
|
|
$oSet = new DBObjectSet($oSearch, array(), array('contact_id' => $iContactId));
|
|
|
|
while ($oMessage = $oSet->Fetch()) {
|
|
$sTitle = $oMessage->Get('title');
|
|
$sMessage = $oMessage->Get('message');
|
|
$sText = <<<HTML
|
|
**$sTitle**
|
|
|
|
|
|
$sMessage
|
|
HTML;
|
|
|
|
$sIcon = $oMessage->Get('icon') !== null ?
|
|
$oMessage->Get('icon')->GetDisplayURL('EventiTopNotification', $oMessage->GetKey(), 'icon') :
|
|
Branding::GetCompactMainLogoAbsoluteUrl();
|
|
$aMessages[] = array(
|
|
'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 EventiTopNotification WHERE contact_id = :contact_id AND read = "no"');
|
|
$oSet = new DBObjectSet($oSearch, array(), array('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('EventiTopNotification', $sEventId);
|
|
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;
|
|
}
|
|
} |