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

@@ -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;
}