N°3649 - Activity panel: Add notifications

This commit is contained in:
Molkobain
2021-03-11 22:12:44 +01:00
parent 3380b8896a
commit 40d002d9e8
18 changed files with 411 additions and 66 deletions

View File

@@ -755,34 +755,23 @@ HTML
}
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
$oExtensionInstance->OnDisplayRelations($this, $oPage, $bEditMode);
}
$oPage->SetCurrentTab('');
// Look for any trigger that considers this object as "In Scope"
// If any trigger has been found then display a tab with notifications
//
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch('Trigger'));
$aTriggers = array();
while ($oTrigger = $oTriggerSet->Fetch())
{
if ($oTrigger->IsInScope($this))
{
$aTriggers[] = $oTrigger->GetKey();
}
}
if (count($aTriggers) > 0)
{
$aTriggers = $this->GetRelatedTriggersIDs();
if (count($aTriggers) > 0) {
$iId = $this->GetKey();
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
$aNotifSearches = array();
$iNotifsCount = 0;
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
foreach($aNotificationClasses as $sNotifClass)
{
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
@@ -792,18 +781,37 @@ HTML
$sCount = ($iNotifsCount > 0) ? ' ('.$iNotifsCount.')' : '';
$oPage->SetCurrentTab('UI:NotificationsTab', Dict::S('UI:NotificationsTab').$sCount);
foreach($aNotificationClasses as $sNotifClass)
{
foreach($aNotificationClasses as $sNotifClass) {
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-blocklist--medallion');
$oPage->AddUiBlock($oClassIcon);
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
}
}
}
/**
* @return string[] IDs of the triggers that consider this object as "In Scope"
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @since 3.0.0
*/
public function GetRelatedTriggersIDs(): array
{
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch('Trigger'));
$aTriggers = [];
while ($oTrigger = $oTriggerSet->Fetch()) {
if ($oTrigger->IsInScope($this)) {
$aTriggers[] = $oTrigger->GetKey();
}
}
return $aTriggers;
}
/**
* @param \WebPage $oPage
* @param bool $bEditMode

View File

@@ -39,7 +39,7 @@
interface iCMDBChangeOp
{
/**
* Describe (as a text string) the modifications corresponding to this change
* Describe (as an HTML string) the modifications corresponding to this change
*
* @return string
*/

View File

@@ -98,16 +98,16 @@ class Event extends DBObject implements iDisplay
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
if ($bEditMode) return array(); // Not editable
$aDetails = array();
$sClass = get_class($this);
$aZList = MetaModel::FlattenZlist(MetaModel::GetZListItems($sClass, 'details'));
foreach( $aZList as $sAttCode)
{
$sDisplayValue = $this->GetAsHTML($sAttCode);
foreach ($aZList as $sAttCode) {
$sDisplayValue = $this->GetAsHTML($sAttCode);
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
}
$oPage->Details($aDetails);
return array();
}
}
@@ -134,8 +134,8 @@ class EventNotification extends Event
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> "", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => "", "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values" => null, "sql" => "object_id", "default_value" => 0, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
@@ -144,7 +144,6 @@ class EventNotification extends Event
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventNotificationEmail extends EventNotification
@@ -181,7 +180,6 @@ class EventNotificationEmail extends EventNotification
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventIssue extends Event

View File

@@ -26,10 +26,5 @@
@import "dashboard/all";
@import "wizard-container/wizard-container";
@import "object/object-details";
@import "activity-panel/activity-panel";
@import "activity-panel/activity-entry";
@import "activity-panel/caselog-entry";
@import "activity-panel/edits-entry";
@import "activity-panel/transition-entry";
@import "activity-panel/caselog-entry-form";
@import "activity-panel/all";
@import "blocks-integrations/all";

View File

@@ -0,0 +1,12 @@
/*!
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "activity-panel";
@import "caselog-entry-form";
@import "activity-entry";
@import "caselog-entry";
@import "transition-entry";
@import "edits-entry";
@import "notification-entry";

View File

@@ -0,0 +1,35 @@
/*!
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* SCSS variables */
$ibo-notification-entry--short-description--text-color: inherit !default;
$ibo-notification-entry--long-description-toggler-icon--margin-left: 12px !default;
$ibo-notification-entry--long-description--margin-top: 8px !default;
/* CSS rules */
/* - Long description */
a.ibo-notification-entry--short-description {
color: $ibo-notification-entry--short-description--text-color;
}
.ibo-notification-entry--long-description-toggler-icon{
margin-left: $ibo-notification-entry--long-description-toggler-icon--margin-left;
transition: all 0.2s ease-in-out;
}
.ibo-notification-entry--long-description{
display: none;
margin-top: $ibo-notification-entry--long-description--margin-top;
list-style: inside;
}
/* - Long desc. opened */
.ibo-notification-entry{
&.ibo-is-opened{
.ibo-notification-entry--long-description-toggler-icon{
transform: rotateX(180deg);
}
.ibo-notification-entry--long-description{
display: block;
}
}
}

View File

@@ -44,6 +44,9 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Layout:ActivityPanel:MultipleEntriesSaveConfirmation:Title' => 'Multiple logs save',
'UI:Layout:ActivityPanel:MultipleEntriesSaveConfirmation:Explanation' => 'By pressing the "save" button, you will submit entries for all the edited logs at once.',
// Notification entry
'UI:Layout:ActivityPanel:NotificationEntry:MessageLink:Tooltip' => 'Click to open the notifications tab and get more information',
// Placeholder
'UI:Layout:ActivityPanel:NoEntry:Placeholder:Hint' => 'It\'s calm up here, no activity yet',

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="240px" height="240px"><path fill="#3d98a8" d="M45,27.9h-6.632c0-7.885-6.446-14.3-14.368-14.3S9.632,20.015,9.632,27.9H3 C3,16.376,12.421,7,24,7S45,16.376,45,27.9z"/><path fill="#266165" d="M43,33h-2.5V22H43c1.657,0,3,1.343,3,3v5C46,31.657,44.657,33,43,33z"/><path fill="#266165" d="M5,33h2.5V22H5c-1.657,0-3,1.343-3,3v5C2,31.657,3.343,33,5,33z"/><path fill="#fff176" d="M24,10c-9.941,0-18,8.059-18,18h36C42,18.059,33.941,10,24,10z"/><path fill="#263238" d="M31.1,38.9H16.9C11.432,38.9,7,34.468,7,29v0c0-5.468,4.432-9.9,9.9-9.9h14.2 c5.468,0,9.9,4.432,9.9,9.9v0C41,34.468,36.568,38.9,31.1,38.9z"/><path fill="#ffd54f" d="M31.05,40h-14.1C10.912,40,6,35.088,6,29.05V28.95C6,22.912,10.912,18,16.95,18h14.1 C37.088,18,42,22.912,42,28.95v0.101C42,35.088,37.088,40,31.05,40z M16.95,20C12.015,20,8,24.015,8,28.95v0.101 C8,33.985,12.015,38,16.95,38h14.1c4.936,0,8.95-4.015,8.95-8.95V28.95c0-4.935-4.015-8.95-8.95-8.95H16.95z"/><path fill="#b2ebf2" d="M22,29.778C22,30,20,26.972,17,27s-5,3-5,2.778C12,27.139,14.239,25,17,25S22,27.139,22,29.778z"/><path fill="#b2ebf2" d="M36,29.778C36,30,34,26.972,31,27s-5,3-5,2.778C26,27.139,28.239,25,31,25S36,27.139,36,29.778z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -82,6 +82,8 @@ $(function()
entry_datetime: '[data-role="ibo-activity-entry--datetime"]',
edits_entry_long_description: '[data-role="ibo-edits-entry--long-description"]',
edits_entry_long_description_toggler: '[data-role="ibo-edits-entry--long-description-toggler"]',
notification_entry_long_description: '[data-role="ibo-notification-entry--long-description"]',
notification_entry_long_description_toggler: '[data-role="ibo-notification-entry--long-description-toggler"]',
},
enums: {
tab_types: {
@@ -206,7 +208,11 @@ $(function()
});
// - Click on an edits entry's long description toggler
this.element.find(this.js_selectors.edits_entry_long_description_toggler).on('click', function (oEvent) {
me._onEditsLongDescriptionTogglerClick(oEvent, $(this).closest(me.js_selectors.entry));
me._onEntryLongDescriptionTogglerClick(oEvent, $(this).closest(me.js_selectors.entry));
});
// - Click on an notification entry's long description toggler
this.element.find(this.js_selectors.notification_entry_long_description_toggler).on('click', function (oEvent) {
me._onEntryLongDescriptionTogglerClick(oEvent, $(this).closest(me.js_selectors.entry));
});
// Page exit
@@ -390,12 +396,10 @@ $(function()
this._SendEntriesToServer();
}
},
_onCaseLogClosedMessageClick: function(oEntryElem)
{
_onCaseLogClosedMessageClick: function (oEntryElem) {
this._OpenMessage(oEntryElem);
},
_onEditsLongDescriptionTogglerClick: function(oEvent, oEntryElem)
{
_onEntryLongDescriptionTogglerClick: function (oEvent, oEntryElem) {
// Avoid anchor glitch
oEvent.preventDefault();

View File

@@ -233,6 +233,9 @@ return array(
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeScalarFactory' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CaseLogEntry' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/CaseLogEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EditsEntry' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EditsEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EventNotification\\EventNotificationEmailFactory' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EventNotification/EventNotificationEmailFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EventNotification\\EventNotificationFactory' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EventNotification/EventNotificationFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\NotificationEntry' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/NotificationEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\TransitionEntry' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/TransitionEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityPanel' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityPanelFactory' => $baseDir . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityPanelFactory.php',

View File

@@ -463,6 +463,9 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeScalarFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\CaseLogEntry' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/CaseLogEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EditsEntry' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EditsEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EventNotification\\EventNotificationEmailFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EventNotification/EventNotificationEmailFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\EventNotification\\EventNotificationFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/EventNotification/EventNotificationFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\NotificationEntry' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/NotificationEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityEntry\\TransitionEntry' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityEntry/TransitionEntry.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityPanel' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Base\\Layout\\ActivityPanel\\ActivityPanelFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Layout/ActivityPanel/ActivityPanelFactory.php',

View File

@@ -24,6 +24,7 @@ use AttributeDateTime;
use Combodo\iTop\Application\UI\Base\UIBlock;
use DateTime;
use UserRights;
use utils;
/**
* Class ActivityEntry
@@ -46,6 +47,8 @@ class ActivityEntry extends UIBlock
public const DEFAULT_TYPE = 'generic';
/** @var string DEFAULT_DECORATION_CLASSES */
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-mortar-pestle';
/** @var string Relative URL (from the app. root) to the default author picture URL */
public const DEFAULT_AUTHOR_PICTURE_REL_URL = 'images/icons/icons8-music-robot.svg';
/** @var string $sType Type of entry, used for filtering (eg. case log, edits, transition, ...) */
protected $sType;
@@ -209,17 +212,20 @@ class ActivityEntry extends UIBlock
// Set friendlyname to whatever we have in case $sAuthorLogin is not a valid login (deleted user, cron, ...)
$iAuthorId = UserRights::GetUserId($this->sAuthorLogin);
if(empty($iAuthorId) === true)
{
// - Friendlyname
if (true === empty($iAuthorId)) {
$this->sAuthorFriendlyname = $this->sAuthorLogin;
}
else
{
// TODO 3.0.0: Check that this does not return '' when author is the CRON or an extension.
} else {
$this->sAuthorFriendlyname = UserRights::GetUserFriendlyName($this->sAuthorLogin);
}
// - Initials
$this->sAuthorInitials = UserRights::GetUserInitials($this->sAuthorLogin);
// - Picture
$this->sAuthorPictureAbsUrl = UserRights::GetContactPictureAbsUrl($this->sAuthorLogin, false);
if ((null === $this->sAuthorPictureAbsUrl) && (ITOP_APPLICATION_SHORT === $this->sAuthorLogin)) {
$this->sAuthorPictureAbsUrl = utils::GetAbsoluteUrlAppRoot().static::DEFAULT_AUTHOR_PICTURE_REL_URL;
}
$this->bIsFromCurrentUser = UserRights::GetUserId($this->sAuthorLogin) === UserRights::GetUserId();
return $this;

View File

@@ -23,6 +23,8 @@ namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry;
use AttributeDateTime;
use CMDBChangeOp;
use DateTime;
use DBObject;
use EventNotification;
use Exception;
use MetaModel;
use ReflectionClass;
@@ -47,11 +49,10 @@ class ActivityEntryFactory
*/
public static function MakeFromCmdbChangeOp(CMDBChangeOp $oChangeOp)
{
$sFactoryFqcn = static::GetCmdbChangeOpFactoryClass($oChangeOp);
$sFactoryFqcn = static::GetFactoryClass($oChangeOp, 'CMDBChangeOp');
// If no factory found, throw an exception as the developer most likely forgot to create it
if(empty($sFactoryFqcn))
{
if (empty($sFactoryFqcn)) {
throw new Exception('No factory found for '.get_class($oChangeOp).', did you forgot to create one?');
}
@@ -89,36 +90,57 @@ class ActivityEntryFactory
}
/**
* Return the FQCN of the best fitted factory for the $oChangeOp. If none found, null will be returned.
* Make an ActivityEntry entry (for ActivityPanel) based on the $oEvent
*
* @param \CMDBChangeOp $oChangeOp
* @param \EventNotification $oEvent
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntry
* @throws \ReflectionException
*/
public static function MakeFromEventNotification(EventNotification $oEvent)
{
$sFactoryFqcn = static::GetFactoryClass($oEvent, 'EventNotification');
// If no factory found, throw an exception as the developer most likely forgot to create it
if (empty($sFactoryFqcn)) {
throw new Exception('No factory found for '.get_class($oEvent).', did you forgot to create one?');
}
/** @var \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\NotificationEntry $oEntry */
/** @noinspection PhpUndefinedMethodInspection Call static method from the $sFactoryFqcn class */
$oEntry = $sFactoryFqcn::MakeFromEventNotification($oEvent);
return $oEntry;
}
/**
* Return the FQCN of the best fitted factory for the $oObject / $sObjectType tuple. If none found, null will be returned.
*
* @param \DBObject $oObject
*
* @return string|null
* @throws \ReflectionException
*/
protected static function GetCmdbChangeOpFactoryClass(CMDBChangeOp $oChangeOp)
protected static function GetFactoryClass(DBObject $oObject, string $sObjectType)
{
// Classes to search a factory for
$aClassesTree = [get_class($oChangeOp)];
$aClassesTree = [get_class($oObject)];
// Add parent classes to tree if not a root class
$aParentClasses = class_parents($oChangeOp);
if(is_array($aParentClasses))
{
$aParentClasses = class_parents($oObject);
if (is_array($aParentClasses)) {
$aClassesTree = array_merge($aClassesTree, array_values($aParentClasses));
}
$sFactoryFqcn = null;
foreach($aClassesTree as $sClass)
{
// Warning: This will replace all occurrences of 'CMDBChangeOp' which can be an issue on classes using this
foreach ($aClassesTree as $sClass) {
// Warning: This will replace all occurrences of $sObjectType (eg. 'CMDBChangeOp', 'EventNotification', ...) which can be an issue on classes using this
// We used the case sensitive search to limit this issue.
$sSimplifiedClass = (new ReflectionClass($sClass))->getShortName();
$sFactoryFqcnToTry = __NAMESPACE__ . '\\CMDBChangeOp\\' . $sSimplifiedClass . 'Factory';
$sFactoryFqcnToTry = __NAMESPACE__.'\\'.$sObjectType.'\\'.$sSimplifiedClass.'Factory';
// Stop at the first factory found
if(class_exists($sFactoryFqcnToTry))
{
if (class_exists($sFactoryFqcnToTry)) {
$sFactoryFqcn = $sFactoryFqcnToTry;
break;
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EventNotification;
/**
* Class EventNotificationEmailFactory
*
* Factory for EventNotificationEmail events
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EventNotification
* @since 3.0.0
*/
class EventNotificationEmailFactory extends EventNotificationFactory
{
/** @inheritDoc */
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-envelope';
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EventNotification;
use AttributeDateTime;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\NotificationEntry;
use DateTime;
use EventNotification;
/**
* Class EventNotificationFactory
*
* Default factory for EventNotification events
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EventNotification
* @since 3.0.0
*/
class EventNotificationFactory
{
/** @var string Used to overload the type from the NotificationEntry */
public const DEFAULT_TYPE = NotificationEntry::DEFAULT_TYPE;
/** @var string Used to overload the decoration classes from the NotificationEntry */
public const DEFAULT_DECORATION_CLASSES = NotificationEntry::DEFAULT_DECORATION_CLASSES;
/**
* Make an ActivityEntry from the iEventNotification $oEvent
*
* @param \EventNotification $oEvent
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\NotificationEntry
* @throws \OQLException
*/
public static function MakeFromEventNotification(EventNotification $oEvent)
{
$oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oEvent->Get('date'));
// Author login is hardcoded:
// - Adding a user_id column to the event tables like for the CMDBChangeOp could be erro prone during migration as those tables are huge.
// - Marking events as created by the app. is good enough as the user triggering it as no power over it, it cannot avoid it.
$sAuthorLogin = ITOP_APPLICATION_SHORT;
$oEntry = new NotificationEntry($oDateTime, $sAuthorLogin, $oEvent->Get('action_id_friendlyname'), $oEvent->Get('message'));
$oEntry->SetType(static::DEFAULT_TYPE)
->SetDecorationClasses(static::DEFAULT_DECORATION_CLASSES);
return $oEntry;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry;
use DateTime;
/**
* Class NotificationEntry
*
* @internal
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @since 3.0.0
*/
class NotificationEntry extends ActivityEntry
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-notification-entry';
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/activity-entry/notification-entry';
public const DEFAULT_TYPE = 'edits';
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-bell';
/** @var string Title of the entry, usually the linked action title */
protected $sTitle;
/** @var string Message of the entry, usually it's status */
protected $sMessage;
/**
* NotificationEntry constructor.
*
* @param \DateTime $oDateTime
* @param string $sAuthorLogin
* @param string $sTitle
* @param string $sMessage
* @param string|null $sId
*
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, string $sAuthorLogin, string $sTitle, string $sMessage, ?string $sId = null)
{
parent::__construct($oDateTime, $sAuthorLogin, null, $sId);
$this->SetTitle($sTitle);
$this->SetMessage($sMessage);
}
/**
* @see static::$sTitle
*
* @param string $sTitle
*
* @return $this
*/
public function SetTitle(string $sTitle)
{
$this->sTitle = $sTitle;
return $this;
}
/**
* @see static::$sTitle
* @return string
*/
public function GetTitle(): string
{
return $this->sTitle;
}
/**
* @see static::$sMessage
*
* @param string $sMessage
*
* @return $this
*/
public function SetMessage(string $sMessage)
{
$this->sMessage = $sMessage;
return $this;
}
/**
* @see static::$sMessage
* @return string
*/
public function GetMessage(): string
{
return $this->sMessage;
}
}

View File

@@ -45,11 +45,11 @@ class ActivityPanelFactory
/**
* Make an activity panel for an object details layout, meaning that it should contain the case logs and the activity.
*
* @param \DBObject $oObject
* @param string $sMode Mode the object is being displayed (view, edit, create, ...), default is view.
*
* @see cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
*
* @param \DBObject $oObject
* @param string $sMode Mode the object is being displayed (view, edit, create, ...), default is view.
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityPanel
* @throws \ArchivedObjectException
* @throws \CoreException
@@ -124,14 +124,41 @@ class ActivityPanelFactory
$oActivityPanel->AddEntry($oEntry);
// Set previous edits entry
if($oEntry instanceof EditsEntry)
{
if ($oEntry instanceof EditsEntry) {
$oPreviousEditsEntry = $oEntry;
}
}
$iPreviousChangeId = $iChangeId;
}
unset($oChangesSet);
// Retrieving notification events for cmdbAbstractObject only
if ($oObject instanceof cmdbAbstractObject) {
$aRelatedTriggersIDs = $oObject->GetRelatedTriggersIDs();
// Protection for classes which have no related trigger
if (false === empty($aRelatedTriggersIDs)) {
// - Prepare query to retrieve events
$oNotifEventsSearch = DBObjectSearch::FromOQL('SELECT EN FROM EventNotification AS EN JOIN Action AS A ON EN.action_id = A.id WHERE EN.trigger_id IN (:triggers_ids) AND EN.object_id = :object_id');
$oNotifEventsSet = new DBObjectSet($oNotifEventsSearch, ['id' => false], ['triggers_ids' => $aRelatedTriggersIDs, 'object_id' => $iObjId]);
$oNotifEventsSet->SetLimit(MetaModel::GetConfig()->Get('max_history_length'));
/** @var \EventNotification $oNotifEvent */
while ($oNotifEvent = $oNotifEventsSet->Fetch()) {
try {
$oEntry = ActivityEntryFactory::MakeFromEventNotification($oNotifEvent);
}
catch (Exception $oException) {
IssueLog::Debug(static::class.': Could not create entry from EventNotification: '.$oException->getMessage());
continue;
}
$oActivityPanel->AddEntry($oEntry);
}
unset($oNotifEventsSet);
}
}
return $oActivityPanel;
}

View File

@@ -0,0 +1,14 @@
{% extends 'base/layouts/activity-panel/activity-entry/layout.html.twig' %}
{% block iboActivityEntryExtraClasses %}ibo-notification-entry{% endblock %}
{% block iboActivityEntryMainInformationContent %}
<a href="#" class="ibo-notification-entry--short-description" data-role="ibo-notification-entry--long-description-toggler">
{{ oUIBlock.GetTitle() }}
<span class="ibo-notification-entry--long-description-toggler-icon fa fa-caret-down"></span>
</a>
<div class="ibo-notification-entry--long-description" data-role="ibo-notification-entry--long-description">
{# Note: The #tab_UINotificationsTab is hardcoded for now but we should find a way to make the connection with how it is prepared in the object's details #}
<a href="#tab_UINotificationsTab" data-tooltip-content="{{ 'UI:Layout:ActivityPanel:NotificationEntry:MessageLink:Tooltip'|dict_s }}">{{ oUIBlock.GetMessage() }}</a>
</div>
{% endblock %}