N°2847 - Work on the ActivityPanel and PopoverMenu features

- Deprecate cmdbAbstractObject::DisplayBareHistory() as history will be replace by ActivityPanel
- Rename illustrations to original filenames to find source more easily
- Remove unused "max_history_case_log_entry_length" config. parameter
- Activity panel: Introduce iCMDBChangeOp and iCMDBChangeOpSetAttribute interface for better dependency injection
- Activity panel: Add placeholder when no entry
- Activity panel: Fix tab toolbar icons color
- Activity panel: Add history entries (entries after the first 50 are not loaded yet)
- Popover menu: Fix no border-radius on first/last entries hover
This commit is contained in:
Molkobain
2020-08-17 11:53:39 +02:00
parent e39947f72c
commit 2ce1c2efec
40 changed files with 1145 additions and 45 deletions

View File

@@ -408,6 +408,8 @@ EOF
* @param int $iLimitStart
*
* @throws \CoreException
*
* @deprecated
*/
public function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
{

View File

@@ -31,7 +31,22 @@
* @package iTopORM
*/
class CMDBChangeOp extends DBObject
/**
* Interface iCMDBChangeOp
*
* @since 2.8.0
*/
interface iCMDBChangeOp
{
/**
* Describe (as a text string) the modifications corresponding to this change
*
* @return string
*/
public function GetDescription();
}
class CMDBChangeOp extends DBObject implements iCMDBChangeOp
{
public static function Init()
{
@@ -156,13 +171,22 @@ class CMDBChangeOpDelete extends CMDBChangeOp
}
}
/**
* Interface iCMDBChangeOpSetAttribute
*
* @since 2.8.0
*/
interface iCMDBChangeOpSetAttribute
{
}
/**
* Record the modification of an attribute (abstract)
*
* @package iTopORM
*/
class CMDBChangeOpSetAttribute extends CMDBChangeOp
class CMDBChangeOpSetAttribute extends CMDBChangeOp implements iCMDBChangeOpSetAttribute
{
public static function Init()
{
@@ -799,7 +823,6 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
}
$oObj = $oMonoObjectSet->Fetch();
$oCaseLog = $oObj->Get($this->Get('attcode'));
$iMaxVisibleLength = MetaModel::getConfig()->Get('max_history_case_log_entry_length', 0);
$sTextEntry = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$oCaseLog->GetEntryAt($this->Get('lastentry')).'</div></div>';
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName, $sTextEntry);

View File

@@ -967,15 +967,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'max_history_case_log_entry_length' => array(
'type' => 'integer',
'description' => 'The length (in number of characters) at which to truncate the (expandable) display (in the history) of a case log entry. If zero, the display in the history is not truncated.',
// examples... not used
'default' => 60,
'value' => 60,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'full_text_chunk_duration' => array(
'type' => 'integer',
'description' => 'Delay after which the results are displayed.',

View File

@@ -46,6 +46,7 @@ $ibo-popover-menu--section-border-radius: $ibo-popover-menu--border-radius !defa
margin: 0px 0px;
width: 100%;
white-space: nowrap;
overflow: hidden; /* To avoid first/last entries of the menu to have no border-radius on hover */
&:first-child{
border-radius: $ibo-popover-menu--section-border-radius $ibo-popover-menu--section-border-radius 0 0;

View File

@@ -22,3 +22,5 @@
@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";

View File

@@ -33,17 +33,25 @@ $ibo-activity-entry--medallion--has-no-image--border: 1px solid $ibo-color-grey-
$ibo-activity-entry--information--margin-to-other-side: $ibo-activity-entry--medallion--diameter + $ibo-activity-entry--medallion--margin-with-information !default;
$ibo-activity-entry--main-information--padding-x: 12px !default;
$ibo-activity-entry--main-information--padding-y: $ibo-activity-entry--main-information--padding-x !default;
$ibo-activity-entry--main-information--padding-x: 16px !default;
$ibo-activity-entry--main-information--padding-y: 12px !default;
$ibo-activity-entry--main-information--text-color: $ibo-color-grey-800 !default;
$ibo-activity-entry--main-information--background-color: $ibo-color-grey-200 !default;
$ibo-activity-entry--main-information--border-radius: $ibo-border-radius-500 !default;
$ibo-activity-entry--main-information--border-radius--for-tip: 0 !default;
$ibo-activity-entry--main-information--elements-spacing: $ibo-activity-entry--main-information--padding-x !default;
$ibo-activity-entry--main-information-accent-strip--width: 2px !default;
$ibo-activity-entry--main-information-hyperlink--text-color: $ibo-color-blue-700 !default;
$ibo-activity-entry--main-information-hyperlink--on-hover--text-color: $ibo-color-blue-900 !default;
$ibo-activity-entry--main-information-hyperlink--on-active--text-color: $ibo-activity-entry--main-information-hyperlink--on-hover--text-color !default;
$ibo-activity-entry--main-information--is-current-user--background-color: $ibo-color-blue-100 !default;
$ibo-activity-entry--main-information--is-closed--max-height: 48px !default;
$ibo-activity-entry--main-information--is-closed--placeholder-top: 30px !default;
$ibo-activity-entry--main-information--is-closed--placeholder-padding-left: $ibo-activity-entry--main-information--padding-x !default;
$ibo-activity-entry--main-information-icon--text-color: $ibo-color-grey-700 !default;
$ibo-activity-entry--main-information-icon--font-size: 16px !default;
$ibo-activity-entry--sub-information--margin-top: 4px !default;
$ibo-activity-entry--sub-information--margin-bottom: $ibo-activity-entry--sub-information--margin-top !default;
$ibo-activity-entry--sub-information--text-color: $ibo-color-grey-700 !default;
@@ -162,7 +170,12 @@ $ibo-activity-entry--sub-information--text-color: $ibo-color-grey-700 !default;
}
.ibo-activity-entry--main-information{
position: relative;
display: flex;
flex-direction: row;
align-items: baseline;
padding: $ibo-activity-entry--main-information--padding-y $ibo-activity-entry--main-information--padding-x;
color: $ibo-activity-entry--main-information--text-color;
background-color: $ibo-activity-entry--main-information--background-color;
border-radius: $ibo-activity-entry--main-information--border-radius;
@@ -170,6 +183,27 @@ $ibo-activity-entry--sub-information--text-color: $ibo-color-grey-700 !default;
pre{
white-space: pre-wrap;
}
/* Specific hyperlink color */
a{
color: $ibo-activity-entry--main-information-hyperlink--text-color;
&:hover{
color: $ibo-activity-entry--main-information-hyperlink--on-hover--text-color;
}
&:active,
&:focus{
color: $ibo-activity-entry--main-information-hyperlink--on-active--text-color;
}
}
}
.ibo-activity-entry--main-information-icon{
margin-right: $ibo-activity-entry--main-information--elements-spacing;
color: $ibo-activity-entry--main-information-icon--text-color;
font-size: $ibo-activity-entry--main-information-icon--font-size;
}
.ibo-activity-entry--main-information-content{
}
.ibo-activity-entry--sub-information{
margin-top: $ibo-activity-entry--sub-information--margin-top;

View File

@@ -50,6 +50,7 @@ $ibo-activity-panel--tab-text--max-width: 100px !default;
/* - Tab toolbar */
$ibo-activity-panel--tab-toolbar--padding-x: $ibo-activity-panel--padding-x !default;
$ibo-activity-panel--tab-toolbar--height: 32px !default;
$ibo-activity-panel--tab-toolbar--text-color: $ibo-color-grey-800 !default;
$ibo-activity-panel--tab-toolbar--background-color: $ibo-activity-panel--tab--is-active--background-color !default;
$ibo-activity-panel--tab-for-caselog--elements-spacing: 16px !default;
@@ -64,6 +65,11 @@ $ibo-activity-panel--tab-for-activity---checkbox-margin-right: 8px !default;
$ibo-activity-panel--body--padding-top: $ibo-activity-panel--tab-toolbar--height + 16px !default;
$ibo-activity-panel--body--padding-x: $ibo-activity-panel--padding-x !default;
$ibo-activity-panel--body--placeholder--margin-top: 16px !default;
$ibo-activity-panel--body--placeholder-image--width: 250px !default;
$ibo-activity-panel--body--placeholder-hint--margin-top: 16px !default;
$ibo-activity-panel--body--placeholder-hint--color: $ibo-color-grey-800 !default;
/* Whole layout */
.ibo-activity-panel{
width: $ibo-activity-panel--width;
@@ -85,7 +91,7 @@ $ibo-activity-panel--body--padding-x: $ibo-activity-panel--padding-x !default;
/* Remove hyperlinks default color */
a{
color: inherit;
color: $ibo-activity-panel--tab-toolbar--text-color;
}
}
.ibo-activity-panel--tabs{
@@ -220,3 +226,21 @@ $ibo-activity-panel--body--padding-x: $ibo-activity-panel--padding-x !default;
padding-right: $ibo-activity-panel--body--padding-x;
}
.ibo-activity-panel--body--placeholder{
margin-top: $ibo-activity-panel--body--placeholder--margin-top;
}
.ibo-activity-panel--body--placeholder-image{
@extend %ibo-fully-centered-content;
> svg {
width: $ibo-activity-panel--body--placeholder-image--width;
height: inherit;
}
}
.ibo-activity-panel--body--placeholder-hint{
margin-top: $ibo-activity-panel--body--placeholder-hint--margin-top;
color: $ibo-activity-panel--body--placeholder-hint--color;
@extend %ibo-font-ral-ita-100;
@extend %ibo-fully-centered-content;
}

View File

@@ -18,10 +18,19 @@
/* SCSS variables */
$ibo-caselog-entry--highlight-colors: $ibo-caselog-highlight-colors !default;
$ibo-caselog-entry--main-information--padding-y: 12px !default;
$ibo-caselog-entry--main-information--decoration--width: 3px !default;
/* Main information */
.ibo-caselog-entry{
.ibo-activity-entry--main-information{
padding-top: $ibo-caselog-entry--main-information--padding-y;
padding-bottom: $ibo-caselog-entry--main-information--padding-y;
}
.ibo-activity-entry--main-information-icon{
display: none;
}
/* Highlight color */
.ibo-activity-entry--main-information::before{
content: "";

View File

@@ -0,0 +1,48 @@
/*!
* 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
*/
/* SCSS variables */
$ibo-edits-entry--short-description--text-color: inherit !default;
$ibo-edits-entry--long-description-toggler-icon--margin-left: 12px !default;
$ibo-edits-entry--long-description--margin-top: 8px !default;
/* CSS rules */
/* - Long description */
a.ibo-edits-entry--short-description {
color: $ibo-edits-entry--short-description--text-color;
}
.ibo-edits-entry--long-description-toggler-icon{
margin-left: $ibo-edits-entry--long-description-toggler-icon--margin-left;
transition: all 0.2s ease-in-out;
}
.ibo-edits-entry--long-description{
display: none;
margin-top: $ibo-edits-entry--long-description--margin-top;
list-style: inside;
}
/* - Long desc. opened */
.ibo-edits-entry{
&.ibo-is-opened{
.ibo-edits-entry--long-description-toggler-icon{
transform: rotateX(180deg);
}
.ibo-edits-entry--long-description{
display: block;
}
}
}

View File

@@ -0,0 +1,27 @@
/*!
* 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
*/
/* SCSS variables */
$ibo-transition-entry--original-state-label--text-color: $ibo-color-grey-800 !default;
$ibo-transition-entry--original-state-label--text-decoration: line-through !default;
/* Main information */
.ibo-transition-entry--original-state-label{
color: $ibo-transition-entry--original-state-label--text-color;
text-decoration: $ibo-transition-entry--original-state-label--text-decoration;
}

View File

@@ -62,6 +62,7 @@ $ibo-color-green-700: hsla(92, 47.9%, 42.2%, 1) !default;
$ibo-color-green-800: hsla(95, 49.5%, 36.5%, 1) !default;
$ibo-color-green-900: hsla(103, 55.6%, 26.5%, 1) !default;
$ibo-color-blue-grey-50: hsla(210, 36%, 96%, 1) !default;
$ibo-color-blue-grey-100: hsla(198, 15.7%, 83.7%, 1) !default;
$ibo-color-blue-grey-200: hsla(200, 15.3%, 73.1%, 1) !default;
$ibo-color-blue-grey-300: hsla(200, 15.6%, 62.4%, 1) !default;
@@ -148,6 +149,7 @@ $ibo-color-pink-900: hsla(318, 51%, 29%, 1) !default;
--ibo-color-green-800: #{$ibo-color-green-800};
--ibo-color-green-900: #{$ibo-color-green-900};
--ibo-color-blue-grey-50: #{$ibo-color-blue-grey-50};
--ibo-color-blue-grey-100: #{$ibo-color-blue-grey-100};
--ibo-color-blue-grey-200: #{$ibo-color-blue-grey-200};
--ibo-color-blue-grey-300: #{$ibo-color-blue-grey-300};

View File

@@ -298,12 +298,16 @@ Dict::Add('EN US', 'English', 'English', array(
'Change:ObjectCreated' => 'Object created',
'Change:ObjectDeleted' => 'Object deleted',
'Change:ObjectModified' => 'Object modified',
'Change:TwoAttributesChanged' => 'Edited %1$s and %2$s',
'Change:ThreeAttributesChanged' => 'Edited %1$s, %2$s and 1 other',
'Change:FourOrMoreAttributesChanged' => 'Edited %1$s, %2$s and %3$s others',
'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s set to %2$s (previous value: %3$s)',
'Change:AttName_SetTo' => '%1$s set to %2$s',
'Change:Text_AppendedTo_AttName' => '%1$s appended to %2$s',
'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modified, previous value: %2$s',
'Change:AttName_Changed' => '%1$s modified',
'Change:AttName_EntryAdded' => '%1$s modified, new entry added: %2$s',
'Change:State_Changed_NewValue_OldValue' => 'Changed from %2$s to %1$s',
'Change:LinkSet:Added' => 'added %1$s',
'Change:LinkSet:Removed' => 'removed %1$s',
'Change:LinkSet:Modified' => 'modified %1$s',

View File

@@ -36,4 +36,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Layout:ActivityPanel:Tab:Caselog:Toolbar:CloseAll:Tooltip' => 'Close all messages',
'UI:Layout:ActivityPanel:Tab:Caselog:Toolbar:AuthorsCount:Tooltip' => 'Number of persons interacting in this log',
'UI:Layout:ActivityPanel:Tab:Caselog:Toolbar:MessagesCount:Tooltip' => 'Number of messages in this log',
// Placeholder
'UI:Layout:ActivityPanel:NoEntry:Placeholder:Hint' => 'It\'s calm up here, no activity yet',
));

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -30,6 +30,7 @@ $(function()
css_classes:
{
is_expanded: 'ibo-is-expanded',
is_opened: 'ibo-is-opened',
is_closed: 'ibo-is-closed',
is_active: 'ibo-is-active',
is_hidden: 'ibo-is-hidden',
@@ -45,7 +46,9 @@ $(function()
entry_group: '[data-role="ibo-activity-panel--entry-group"]',
entry: '[data-role="ibo-activity-entry"]',
entry_main_information: '[data-role="ibo-activity-entry--main-information"]',
entry_datetime: '[data-role="ibo-activity-entry--datetime"]'
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"]',
},
// the constructor
@@ -90,6 +93,10 @@ $(function()
this.element.find(this.js_selectors.entry_group).on('click', '.'+this.css_classes.is_closed + ' ' + this.js_selectors.entry_main_information, function(oEvent){
me._onCaseLogClosedMessageClick($(this).closest(me.js_selectors.entry));
});
// Click on an edits entry long description toggler
this.element.find(this.js_selectors.edits_entry_long_description_toggler).on('click', function(oEvent){
me._onEditsTogglerClick(oEvent, $(this).closest(me.js_selectors.entry));
});
// Mostly for outside clicks that should close elements
oBodyElem.on('click', function(oEvent){
me._onBodyClick(oEvent);
@@ -146,6 +153,13 @@ $(function()
{
this._OpenMessage(oEntryElem);
},
_onEditsTogglerClick: function(oEvent, oEntryElem)
{
// Avoid anchor glitch
oEvent.preventDefault();
oEntryElem.toggleClass(this.css_classes.is_opened);
},
_onBodyClick: function(oEvent)
{

View File

@@ -169,7 +169,16 @@ return array(
'Combodo\\iTop\\Application\\UI\\Component\\QuickCreate\\QuickCreateHelper' => $baseDir . '/sources/application/UI/Component/QuickCreate/QuickCreateHelper.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntry' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/ActivityEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntryFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/ActivityEntryFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentAddedFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentAddedFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentRemovedFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentRemovedFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpCreateFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpCreateFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpDeleteFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpDeleteFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeScalarFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CaseLogEntry' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CaseLogEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\EditsEntry' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/EditsEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\TransitionEntry' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/TransitionEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanel' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanelFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityPanelFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\NavigationMenu\\NavigationMenu' => $baseDir . '/sources/application/UI/Layout/NavigationMenu/NavigationMenu.php',
@@ -2208,6 +2217,8 @@ return array(
'iApplicationObjectExtension' => $baseDir . '/application/applicationextension.inc.php',
'iApplicationUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackgroundProcess' => $baseDir . '/core/backgroundprocess.inc.php',
'iCMDBChangeOp' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'iCMDBChangeOpSetAttribute' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
'iDisplay' => $baseDir . '/core/dbobject.class.php',

View File

@@ -399,7 +399,16 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Application\\UI\\Component\\QuickCreate\\QuickCreateHelper' => __DIR__ . '/../..' . '/sources/application/UI/Component/QuickCreate/QuickCreateHelper.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntry' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/ActivityEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\ActivityEntryFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/ActivityEntryFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentAddedFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentAddedFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpAttachmentRemovedFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpAttachmentRemovedFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpCreateFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpCreateFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpDeleteFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpDeleteFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CMDBChangeOp\\CMDBChangeOpSetAttributeScalarFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\CaseLogEntry' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CaseLogEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\EditsEntry' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/EditsEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityEntry\\TransitionEntry' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityEntry/TransitionEntry.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanel' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanelFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityPanelFactory.php',
'Combodo\\iTop\\Application\\UI\\Layout\\NavigationMenu\\NavigationMenu' => __DIR__ . '/../..' . '/sources/application/UI/Layout/NavigationMenu/NavigationMenu.php',
@@ -2438,6 +2447,8 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'iApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackgroundProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
'iCMDBChangeOp' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'iCMDBChangeOpSetAttribute' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php',

View File

@@ -41,8 +41,13 @@ class ActivityEntry extends UIBlock
const HTML_TEMPLATE_REL_PATH = 'layouts/activity-panel/activity-entry/layout';
// Specific constants
/** @var string DEFAULT_ORIGIN */
const DEFAULT_ORIGIN = 'unknown';
/** @var string DEFAULT_DECORATION_CLASSES */
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-mortar-pestle';
/** @var string $sDecorationClasses CSS classes to use to decorate the entry */
protected $sDecorationClasses;
/** @var string $sContent Raw content of the entry itself (should not have been processed / escaped) */
protected $sContent;
/** @var \DateTime $oDateTime Date / time the entry occurred */
@@ -63,23 +68,48 @@ class ActivityEntry extends UIBlock
/**
* ActivityEntry constructor.
*
* @param string $sContent
* @param \DateTime $oDateTime
* @param \User $sAuthorLogin
* @param string $sId
* @param string $sContent
* @param string $sIdCode
*
* @throws \OQLException
*/
public function __construct($sContent, DateTime $oDateTime, $sAuthorLogin, $sId = null)
public function __construct(DateTime $oDateTime, $sAuthorLogin, $sContent = null, $sIdCode = null)
{
parent::__construct($sId);
parent::__construct($sIdCode);
$this->SetDecorationClasses(static::DEFAULT_DECORATION_CLASSES);
$this->SetContent($sContent);
$this->SetDateTime($oDateTime);
$this->SetAuthor($sAuthorLogin);
$this->SetOrigin(static::DEFAULT_ORIGIN);
}
/**
* Set the CSS decoration classes
*
* @param string $sDecorationClasses Must be a space-separated list of CSS classes
*
* @return $this
*/
public function SetDecorationClasses($sDecorationClasses)
{
$this->sDecorationClasses = $sDecorationClasses;
return $this;
}
/**
* Return a string of the space separated CSS decoration classes
*
* @return string
*/
public function GetDecorationClasses()
{
return $this->sDecorationClasses;
}
/**
* Set the content without any filtering / escaping
*

View File

@@ -21,8 +21,11 @@ namespace Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry;
use AttributeDateTime;
use CMDBChangeOp;
use DateTime;
use Exception;
use MetaModel;
use ReflectionClass;
/**
* Class ActivityEntryFactory
@@ -34,6 +37,31 @@ use MetaModel;
*/
class ActivityEntryFactory
{
/**
* Make an ActivityEntry entry (for ActivityPanel) based on the $oChangeOp.
*
* @param \CMDBChangeOp $oChangeOp
*
* @return \Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntry
* @throws \Exception
*/
public static function MakeFromCmdbChangeOp(CMDBChangeOp $oChangeOp)
{
$sFactoryFqcn = static::GetCmdbChangeOpFactoryClass($oChangeOp);
// 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($oChangeOp).', did you forgot to create one?');
}
/** @var \Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntry $oEntry */
/** @noinspection PhpUndefinedMethodInspection Call static method from the $sFactoryFqcn class */
$oEntry = $sFactoryFqcn::MakeFromCmdbChangeOp($oChangeOp);
return $oEntry;
}
/**
* Make a CaseLogEntry entry (for ActivityPanel) from an ormCaseLog array entry.
*
@@ -51,12 +79,51 @@ class ActivityEntryFactory
$sUserLogin = ($oUser === null) ? '' : $oUser->Get('login');
$oEntry = new CaseLogEntry(
$aOrmEntry['message_html'],
DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $aOrmEntry['date']),
$sUserLogin,
$sAttCode
$sAttCode,
$aOrmEntry['message_html']
);
return $oEntry;
}
/**
* Return the FQCN of the best fitted factory for the $oChangeOp. If none found, null will be returned.
*
* @param \CMDBChangeOp $oChangeOp
*
* @return string|null
* @throws \ReflectionException
*/
protected static function GetCmdbChangeOpFactoryClass(CMDBChangeOp $oChangeOp)
{
// Classes to search a factory for
$aClassesTree = [get_class($oChangeOp)];
// Add parent classes to tree if not a root class
$aParentClasses = class_parents($oChangeOp);
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
// We used the case sensitive search to limit this issue.
$sSimplifiedClass = (new ReflectionClass($sClass))->getShortName();
$sFactoryFqcnToTry = __NAMESPACE__ . '\\CMDBChangeOp\\' . $sSimplifiedClass . 'Factory';
// Stop at the first factory found
if(class_exists($sFactoryFqcnToTry))
{
$sFactoryFqcn = $sFactoryFqcnToTry;
break;
}
}
return $sFactoryFqcn;
}
}

View File

@@ -0,0 +1,32 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
/**
* Class CMDBChangeOpAttachmentAddedFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpAttachmentAddedFactory extends CMDBChangeOpFactory
{
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-paperclip';
}

View File

@@ -0,0 +1,34 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use iCMDBChangeOp;
/**
* Class CMDBChangeOpAttachmentRemovedFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpAttachmentRemovedFactory extends CMDBChangeOpFactory
{
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-unlink';
}

View File

@@ -0,0 +1,44 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use iCMDBChangeOp;
/**
* Class CMDBChangeOpCreateFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpCreateFactory extends CMDBChangeOpFactory
{
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-unlink';
/**
* @inheritDoc
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOp $oChangeOp)
{
$oEntry = parent::MakeFromCmdbChangeOp($oChangeOp);
$oEntry->SetDecorationClasses('fas fa-fw fa-seedling');
return $oEntry;
}
}

View File

@@ -0,0 +1,44 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use iCMDBChangeOp;
/**
* Class CMDBChangeOpDeleteFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpDeleteFactory extends CMDBChangeOpFactory
{
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-unlink';
/**
* @inheritDoc
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOp $oChangeOp)
{
$oEntry = parent::MakeFromCmdbChangeOp($oChangeOp);
$oEntry->SetDecorationClasses('fas fa-fw fa-trash');
return $oEntry;
}
}

View File

@@ -0,0 +1,60 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use AttributeDateTime;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntry;
use DateTime;
use iCMDBChangeOp;
/**
* Class CMDBChangeOpFactory
*
* Default factory for CMDBChangeOp change ops
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpFactory
{
/** @var string DEFAULT_DECORATION_CLASSES Use to overload the decoration classes from the ActivityEntry */
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-mortar-pestle';
/**
* Make an ActivityEntry from the iCMDBChangeOp $oChangeOp
*
* @param \iCMDBChangeOp $oChangeOp
*
* @return \Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntry
* @throws \OQLException
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOp $oChangeOp)
{
$oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oChangeOp->Get('date'));
$sAuthorFriendlyname = $oChangeOp->Get('userinfo');
$sContent = $oChangeOp->GetDescription();
$oEntry = new ActivityEntry($oDateTime, $sAuthorFriendlyname, $sContent);
$oEntry->SetDecorationClasses(static::DEFAULT_DECORATION_CLASSES);
return $oEntry;
}
}

View File

@@ -0,0 +1,58 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use AttributeDateTime;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\EditsEntry;
use DateTime;
use iCMDBChangeOpSetAttribute;
/**
* Class CMDBChangeOpSetAttributeFactory
*
* Default factory for CMDBChangeOpSetAttribute change ops
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpSetAttributeFactory
{
/**
* Make an EditsEntry from the iCMDBChangeOpSetAttribute $oChangeOp
*
* @param \iCMDBChangeOpSetAttribute $oChangeOp
*
* @return \Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\EditsEntry
* @throws \OQLException
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOpSetAttribute $oChangeOp)
{
$sHostObjectClass = $oChangeOp->Get('objclass');
$sAttCode = $oChangeOp->Get('attcode');
$oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oChangeOp->Get('date'));
$sAuthorFriendlyname = $oChangeOp->Get('userinfo');
$oEntry = new EditsEntry($oDateTime, $sAuthorFriendlyname, $sHostObjectClass);
$oEntry->AddAttribute($sAttCode, $oChangeOp->GetDescription());
return $oEntry;
}
}

View File

@@ -0,0 +1,65 @@
<?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\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
use AttributeDateTime;
use iCMDBChangeOpSetAttribute;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\TransitionEntry;
use DateTime;
use MetaModel;
/**
* Class CMDBChangeOpSetAttributeScalarFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp\Factory
* @since 2.8.0
*/
class CMDBChangeOpSetAttributeScalarFactory extends CMDBChangeOpSetAttributeFactory
{
/**
* @inheritDoc
* @throws \CoreException
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOpSetAttribute $oChangeOp)
{
$sHostObjectClass = $oChangeOp->Get('objclass');
$sAttCode = $oChangeOp->Get('attcode');
// Specific ActivityEntry for transition, otherwise just a regular EditsEntry
if($sAttCode === MetaModel::GetStateAttributeCode($sHostObjectClass))
{
$oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oChangeOp->Get('date'));
$sAuthorFriendlyname = $oChangeOp->Get('userinfo');
$sOriginStateLabel = MetaModel::GetStateLabel($sHostObjectClass, $oChangeOp->Get('oldvalue'));
$sTargetStateLabel = MetaModel::GetStateLabel($sHostObjectClass, $oChangeOp->Get('newvalue'));
$oEntry = new TransitionEntry($oDateTime, $sAuthorFriendlyname, $sHostObjectClass, $sOriginStateLabel, $sTargetStateLabel);
}
else
{
$oEntry = parent::MakeFromCmdbChangeOp($oChangeOp);
}
return $oEntry;
}
}

View File

@@ -20,11 +20,7 @@
namespace Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry;
use AttributeDateTime;
use Combodo\iTop\Application\UI\UIBlock;
use DateTime;
use User;
use UserRights;
/**
* Class CaseLogEntry
@@ -51,17 +47,17 @@ class CaseLogEntry extends ActivityEntry
/**
* CaseLogEntry constructor.
*
* @param string $sContent
* @param \DateTime $oDateTime
* @param \User $sAuthorLogin
* @param string $sAttCode
* @param string $sContentCode
* @param string $sId
*
* @throws \OQLException
*/
public function __construct($sContent, DateTime $oDateTime, $sAuthorLogin, $sAttCode, $sId = null)
public function __construct(DateTime $oDateTime, $sAuthorLogin, $sAttCode, $sContentCode, $sId = null)
{
parent::__construct($sContent, $oDateTime, $sAuthorLogin, $sId);
parent::__construct($oDateTime, $sAuthorLogin, $sContentCode, $sId);
$this->sAttCode = $sAttCode;
$this->SetCaseLogRank(static::DEFAULT_CASELOG_RANK);

View File

@@ -0,0 +1,205 @@
<?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\Layout\ActivityPanel\ActivityEntry;
use DateTime;
use Dict;
use Exception;
use MetaModel;
/**
* Class EditsEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 2.8.0
*/
class EditsEntry extends ActivityEntry
{
// Overloaded constants
const BLOCK_CODE = 'ibo-edits-entry';
const HTML_TEMPLATE_REL_PATH = 'layouts/activity-panel/activity-entry/edits-entry';
// Specific constants
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-pen';
/** @var string $sObjectClass */
protected $sObjectClass;
/** @var array $aAttributes Array of edited attributes with their code, label and description */
protected $aAttributes;
/**
* EditsEntry constructor.
*
* @param \DateTime $oDateTime
* @param \User $sAuthorLogin
* @param string $sObjectClass Class of the object concerned by the edits
* @param string $sId
*
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, $sAuthorLogin, $sObjectClass, $sId = null)
{
parent::__construct($oDateTime, $sAuthorLogin, null, $sId);
$this->sObjectClass = $sObjectClass;
$this->SetAttributes([]);
}
/**
* Return the class of the object concerned by the edits
*
* @return string
*/
public function GetObjectClass()
{
return $this->sObjectClass;
}
/**
* Set all attributes at once, replacing all existing.
*
* @param array $aAttributes
*
* @return $this
*/
public function SetAttributes($aAttributes)
{
$this->aAttributes = $aAttributes;
return $this;
}
/**
* Return an array of edited attributes with their code, label and description
*
* @return array
*/
public function GetAttributes()
{
return $this->aAttributes;
}
/**
* Add the attribute identified by $sAttCode to the edited attribute.
* Note that if an attribute with the same $sAttCode already exists, it will be replaced.
*
* @param string $sAttCode
* @param string $sEditDescriptionAsHtml The description of the edit already in HTML, it MUSt have been sanitized first (Already in HTML because most of the time it comes from CMDBChangeOp::GetDescription())
*
* @throws \Exception
*/
public function AddAttribute($sAttCode, $sEditDescriptionAsHtml)
{
$this->aAttributes[$sAttCode] = [
'code' => $sAttCode,
'label' => MetaModel::GetLabel($this->sObjectClass, $sAttCode),
'description' => $sEditDescriptionAsHtml,
];
}
/**
* Remove the attribute of code $sAttCode from the edited attributes.
* Note that if there is no attribute with this code, it will proceed silently.
*
* @param string $sAttCode
*
* @return array
*/
public function RemoveAttribute($sAttCode)
{
if(array_key_exists($sAttCode, $this->aAttributes))
{
unset($this->aAttributes[$sAttCode]);
}
return $this->aAttributes;
}
/**
* Merge $oEntry into the current one ($this).
* Note that edits on any existing attribute codes will be replaced.
*
* @param \Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\EditsEntry $oEntry
*
* @return $this
* @throws \Exception
*/
public function Merge(EditsEntry $oEntry)
{
if($oEntry->GetObjectClass() !== $this->GetObjectClass())
{
throw new Exception("Cannot merge an entry from {$oEntry->GetObjectClass()} into {$this->GetObjectClass()}, they must be for the same class");
}
// Merging attributes
foreach($oEntry->GetAttributes() as $sAttCode => $aAttData)
{
$this->aAttributes[$sAttCode] = $aAttData;
}
return $this;
}
/**
* Return the short description of the edits entry in HTML
*
* @return string
*/
public function GetShortDescriptionAsHtml()
{
// We need the array to be indexed by numbers instead of being associative
$aAttributesData = array_values($this->GetAttributes());
$iAttributesCount = count($aAttributesData);
switch($iAttributesCount)
{
case 0:
$sDescriptionAsHtml = '';
break;
case 1:
$sDescriptionAsHtml = $aAttributesData[0]['description'];
break;
default:
$sFirstAttLabelAsHtml = '<span class="ibo-edits-entry--attribute-label" data-attribute-code="'.$aAttributesData[0]['code'].'">'.$aAttributesData[0]['label'].'</span>';
$sSecondAttLabelAsHtml = '<span class="ibo-edits-entry--attribute-label" data-attribute-code="'.$aAttributesData[1]['code'].'">'.$aAttributesData[1]['label'].'</span>';
switch($iAttributesCount)
{
case 2:
$sDescriptionAsHtml = Dict::Format('Change:TwoAttributesChanged', $sFirstAttLabelAsHtml, $sSecondAttLabelAsHtml);
break;
case 3:
$sDescriptionAsHtml = Dict::Format('Change:ThreeAttributesChanged', $sFirstAttLabelAsHtml, $sSecondAttLabelAsHtml);
break;
default:
$sDescriptionAsHtml = Dict::Format('Change:FourOrMoreAttributesChanged', $sFirstAttLabelAsHtml, $sSecondAttLabelAsHtml, count($aAttributesData) - 2);
break;
}
}
return $sDescriptionAsHtml;
}
}

View File

@@ -0,0 +1,145 @@
<?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\Layout\ActivityPanel\ActivityEntry;
use DateTime;
/**
* Class TransitionEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 2.8.0
*/
class TransitionEntry extends ActivityEntry
{
// Overloaded constants
const BLOCK_CODE = 'ibo-transition-entry';
const HTML_TEMPLATE_REL_PATH = 'layouts/activity-panel/activity-entry/transition-entry';
// Specific constants
const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-map-signs';
/** @var string $sOriginStateCode Code of the state before the transition */
protected $sOriginStateCode;
/** @var string $sOriginStateLabel Label of the $sOriginStateCode state */
protected $sOriginStateLabel;
/** @var string $sTargetStateCode Code of the state after the transition */
protected $sTargetStateCode;
/** @var string $sTargetStateLabel Label of the $sTargetStateCode state */
protected $sTargetStateLabel;
/**
* TransitionEntry constructor.
*
* @param \DateTime $oDateTime
* @param \User $sAuthorLogin
* @param string $sObjectClass Class of the object which made the transition
* @param string $sOriginStateCode
* @param string $sTargetStateCode
* @param string $sId
*
* @throws \CoreException
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, $sAuthorLogin, $sObjectClass, $sOriginStateCode, $sTargetStateCode, $sId = null)
{
parent::__construct($oDateTime, $sAuthorLogin, null, $sId);
$this->SetOriginalState($sObjectClass, $sOriginStateCode);
$this->SetTargetState($sObjectClass, $sTargetStateCode);
}
/**
* Set the code / label of the state before the transition
*
* @param string $sObjectClass Class of the object the state is from
* @param string $sStateCode
*
* @return $this
* @throws \CoreException
*/
public function SetOriginalState($sObjectClass, $sStateCode)
{
$this->sOriginStateCode = $sStateCode;
$this->sOriginStateLabel = \MetaModel::GetStateLabel($sObjectClass, $sStateCode);
return $this;
}
/**
* Return the code of the state before the transition
*
* @return string
*/
public function GetOriginalStateCode()
{
return $this->sOriginStateCode;
}
/**
* Return the label of the state before the transition
*
* @return string
*/
public function GetOriginalStateLabel()
{
return $this->sOriginStateLabel;
}
/**
* Set the code / label of the state after the transition
*
* @param string $sObjectClas
* @param string $sStateCode
*
* @return $this
* @throws \CoreException
*/
public function SetTargetState($sObjectClas, $sStateCode)
{
$this->sTargetStateCode = $sStateCode;
$this->sTargetStateLabel = \MetaModel::GetStateLabel($sObjectClas, $sStateCode);
return $this;
}
/**
* Return the code of the state after the transition
*
* @return string
*/
public function GetTargetStateCode()
{
return $this->sTargetStateCode;
}
/**
* Return the label of the state after the transition
*
* @return string
*/
public function GetTargetStateLabel()
{
return $this->sTargetStateLabel;
}
}

View File

@@ -22,7 +22,6 @@ namespace Combodo\iTop\Application\UI\Layout\ActivityPanel;
use AttributeDateTime;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntry;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\CaseLogEntry;
use Combodo\iTop\Application\UI\UIBlock;
use DBObject;
@@ -184,8 +183,12 @@ class ActivityPanel extends UIBlock
$aCurrentGroup['entries'][] = $oEntry;
$aPreviousEntryData = ['author_login' => $sAuthorLogin, 'origin' => $sOrigin];
}
// Flush last group
if(empty($aCurrentGroup['entries']) === false)
{
$aGroupedEntries[] = $aCurrentGroup;
}
return $aGroupedEntries;
}

View File

@@ -20,9 +20,14 @@
namespace Combodo\iTop\Application\UI\Layout\ActivityPanel;
use CMDBChangeOpSetAttributeCaseLog;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Application\UI\Layout\ActivityPanel\ActivityEntry\EditsEntry;
use DBObject;
use DBObjectSearch;
use DBObjectSet;
use MetaModel;
use UserRights;
/**
* Class ActivityPanelFactory
@@ -45,6 +50,9 @@ class ActivityPanelFactory
*/
public static function MakeForObjectDetails(DBObject $oObject)
{
$sObjClass = get_class($oObject);
$iObjId = $oObject->GetKey();
$oActivityPanel = new ActivityPanel($oObject);
// Retrieve case logs entries
@@ -60,7 +68,48 @@ class ActivityPanelFactory
}
}
// Retrieve history changes
// Retrieve history changes (including case logs entries)
// - Prepare query to retrieve changes
$oChangesSearch = DBObjectSearch::FromOQL('SELECT CMDBChangeOp WHERE objclass = :obj_class AND objkey = :obj_key');
$oChangesSet = new DBObjectSet($oChangesSearch, ['date' => false], ['obj_class' => $sObjClass, 'obj_key' => $iObjId]);
// Note: This limit will include case log changes which will be skipped, but still we count them as they are displayed anyway by the case log attributes themselves
$oChangesSet->SetLimit(MetaModel::GetConfig()->Get('max_history_length'));
// Prepare previous values to group edits within a same CMDBChange
$iPreviousChangeId = 0;
$oPreviousEditsEntry = null;
/** @var \CMDBChangeOp $oChangeOp */
while($oChangeOp = $oChangesSet->Fetch())
{
// Skip case log changes as they are handled directly from the attributes themselves
if($oChangeOp instanceof CMDBChangeOpSetAttributeCaseLog)
{
continue;
}
// Make entry from CMDBChangeOp
$iChangeId = $oChangeOp->Get('change');
$oEntry = ActivityEntryFactory::MakeFromCmdbChangeOp($oChangeOp);
// If same CMDBChange and mergeable edits entry, we merge them
if( ($iChangeId == $iPreviousChangeId) && ($oPreviousEditsEntry instanceof EditsEntry) && ($oEntry instanceof EditsEntry))
{
$oPreviousEditsEntry->Merge($oEntry);
}
else
{
$oActivityPanel->AddEntry($oEntry);
// Set previous edits entry
if($oEntry instanceof EditsEntry)
{
$oPreviousEditsEntry = $oEntry;
}
}
$iPreviousChangeId = $iChangeId;
}
return $oActivityPanel;
}

View File

@@ -21,7 +21,7 @@
{% else %}
<div class="ibo-global-search--compartment--placeholder">
<div class="ibo-global-search--compartment--placeholder-image ibo-svg-illustration--container">
{{ source("illustrations/global-search-empty-history.svg") }}
{{ source("illustrations/undraw_web_search.svg") }}
</div>
<div class="ibo-global-search--compartment--placeholder-hint">{{ 'UI:Component:GlobalSearch:LastQueries:NoQuery:Placeholder'|dict_s }}</div>
</div>

View File

@@ -15,7 +15,7 @@
<span>{{ 'UI:Component:QuickCreate:Recents:Title'|dict_s }}</span>
</div>
<div class="ibo-quick-create--compartment-content" data-role="ibo-quick-create--compartment-content">
{% if oUIBlock.GetLastClasses()|length > 0 %}
{% if oUIBlock.GetLastClasses()|length > 10 %}
{% for aClass in oUIBlock.GetLastClasses() %}
<a href="{{ aClass.target_url }}" class="ibo-quick-create--compartment-element" data-role="ibo-quick-create--compartment-element" data-class-code="{{ aQuery.class }}">
{% if aClass.icon_url is defined %}
@@ -27,7 +27,7 @@
{% else %}
<div class="ibo-quick-create--compartment--placeholder">
<div class="ibo-quick-create--compartment--placeholder-image ibo-svg-illustration--container">
{{ source("illustrations/quick-create-empty-history.svg") }}
{{ source("illustrations/undraw_duplicate.svg") }}
</div>
<div class="ibo-quick-create--compartment--placeholder-hint">{{ 'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder'|dict_s }}</div>

View File

@@ -3,3 +3,7 @@
{% block iboActivityEntryExtraClasses %}ibo-caselog-entry ibo-caselog-entry--entry-for-caselog-{{ oUIBlock.GetCaseLogRank() }}{% endblock %}
{% block iboActivityEntryType %}caselog{% endblock %}
{% block iboActivityEntryExtraDataAttributes %}data-entry-caselog-attribute-code="{{ oUIBlock.GetAttCode() }}"{% endblock %}
{% block iboActivityEntryMainInformationIcon %}
<span class="fas fa-fw fa-quote-left"></span>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'layouts/activity-panel/activity-entry/layout.html.twig' %}
{% block iboActivityEntryExtraClasses %}ibo-edits-entry{% endblock %}
{% block iboActivityEntryType %}edits{% endblock %}
{% block iboActivityEntryMainInformationIcon %}
<span class="fas fa-fw fa-pen"></span>
{% endblock %}
{% block iboActivityEntryMainInformationContent %}
{% if oUIBlock.GetAttributes()|length == 1 %}
<span class="ibo-edits-entry--short-description">{{ oUIBlock.GetShortDescriptionAsHtml()|raw }}</span>
{% else %}
<a href="#" class="ibo-edits-entry--short-description" data-role="ibo-edits-entry--long-description-toggler">
{{ oUIBlock.GetShortDescriptionAsHtml()|raw }}
<span class="ibo-edits-entry--long-description-toggler-icon fa fa-caret-down"></span>
</a>
<ul class="ibo-edits-entry--long-description" data-role="ibo-edits-entry--long-description">
{% for sAttCode, aAttData in oUIBlock.GetAttributes() %}
<li class="ibo-edits-entry--attribute-description" data-attribute-code="{{ sAttCode }}">{{ aAttData.description|raw }}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@@ -16,11 +16,20 @@
<div class="ibo-activity-entry--information" data-role="ibo-activity-entry--information">
{% block iboActivityEntryInformation %}
<div class="ibo-activity-entry--main-information" data-role="ibo-activity-entry--main-information">
{% block iboActivityEntryMainInformation %}
<div class="ibo-activity-entry--main-information-icon">
{% block iboActivityEntryMainInformationIcon %}
{% if oUIBlock.GetDecorationClasses() is not empty %}
<span class="{{ oUIBlock.GetDecorationClasses() }}"></span>
{% endif %}
{% endblock %}
</div>
<div class="ibo-activity-entry--main-information-content">
{% block iboActivityEntryMainInformationContent %}
{# Content is printed as raw because it is stored as HTML in the database and should have been sanitized before storage, so we can assume it is safe #}
{{ oUIBlock.GetContent()|raw }}
{% endblock %}
</div>
</div>
<div class="ibo-activity-entry--sub-information" data-role="ibo-activity-entry--sub-information">
{% block iboActivityEntrySubInformation %}
<span class="ibo-activity-entry--datetime" data-role="ibo-activity-entry--datetime" data-tooltip-content="{{ oUIBlock.GetFormattedDateTime() }}">{{ oUIBlock.GetFormattedDateTime() }}</span>

View File

@@ -0,0 +1,15 @@
{% extends 'layouts/activity-panel/activity-entry/layout.html.twig' %}
{% block iboActivityEntryExtraClasses %}ibo-transition-entry{% endblock %}
{% block iboActivityEntryType %}transition{% endblock %}
{% block iboActivityEntryExtraDataAttributes %}data-original-state-code="{{ oUIBlock.GetOriginalStateCode() }}" data-target-state-code="{{ oUIBlock.GetTargetStateCode() }}"{% endblock %}
{% block iboActivityEntryMainInformationIcon %}
<span class="fas fa-fw fa-map-signs"></span>
{% endblock %}
{% block iboActivityEntryMainInformationContent %}
{% set sOriginalStateLabelAsHtml = '<span class="ibo-transition-entry--original-state-label">' ~ oUIBlock.GetOriginalStateLabel() ~ '</span>' %}
{% set sTargetStateLabelAsHtml = '<span class="ibo-transition-entry--target-state-label">' ~ oUIBlock.GetTargetStateLabel() ~ '</span>' %}
{{ 'Change:State_Changed_NewValue_OldValue'|dict_format(sTargetStateLabelAsHtml, sOriginalStateLabelAsHtml)|raw }}
{% endblock %}

View File

@@ -51,7 +51,7 @@
{% endif %}
<label class="ibo-activity-panel--tab-action"
data-tooltip-content="{{ 'UI:Layout:ActivityPanel:Tab:Activity:Toolbar:EditsFilter:Tooltip'|dict_s }}">
<input type="checkbox" name="edits" data-role="ibo-activity-panel--activity-filter" data-target-entry-types="edit" checked />
<input type="checkbox" name="edits" data-role="ibo-activity-panel--activity-filter" data-target-entry-types="edits" checked />
{{ 'UI:Layout:ActivityPanel:Tab:Activity:Toolbar:EditsFilter:Title'|dict_s }}
</label>
</div>
@@ -72,8 +72,17 @@
</div>
</div>
<div class="ibo-activity-panel--body">
{% if oUIBlock.GetGroupedEntries()|length > 0 %}
{% for aEntryGroup in oUIBlock.GetGroupedEntries() %}
{{ include('layouts/activity-panel/entry-group.html.twig', {aEntryGroup: aEntryGroup}) }}
{% endfor %}
{% else %}
<div class="ibo-activity-panel--body--placeholder">
<div class="ibo-activity-panel--body--placeholder-image ibo-svg-illustration--container">
{{ source("illustrations/undraw_reading_time.svg") }}
</div>
<div class="ibo-activity-panel--body--placeholder-hint">{{ 'UI:Layout:ActivityPanel:NoEntry:Placeholder:Hint'|dict_s }}</div>
</div>
{% endif %}
</div>
</div>