N°3123 - Refactor Directories

This commit is contained in:
Eric
2020-12-02 13:18:01 +01:00
parent d1b12ee04b
commit 15aa9e508c
259 changed files with 862 additions and 869 deletions

View File

@@ -0,0 +1,293 @@
<?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 AttributeDateTime;
use Combodo\iTop\Application\UI\Base\UIBlock;
use DateTime;
use UserRights;
/**
* Class ActivityEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 3.0.0
*/
class ActivityEntry extends UIBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-activity-entry';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/activity-entry/layout';
// Specific constants
/** @var string DEFAULT_ORIGIN */
public const DEFAULT_ORIGIN = 'unknown';
/** @var string DEFAULT_TYPE */
public const DEFAULT_TYPE = 'generic';
/** @var string DEFAULT_DECORATION_CLASSES */
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-mortar-pestle';
/** @var string $sType Type of entry, used for filtering (eg. case log, edits, transition, ...) */
protected $sType;
/** @var string $sDecorationClasses CSS classes to use to decorate the entry */
protected $sDecorationClasses;
/** @var string|null $sContent Raw content of the entry itself (should not have been processed / escaped) */
protected $sContent;
/** @var \DateTime $oDateTime Date / time the entry occurred */
protected $oDateTime;
/** @var string $sAuthorLogin Login of the author (user, cron, extension, ...) who made the activity of the entry */
protected $sAuthorLogin;
/** @var string $sAuthorFriendlyname */
protected $sAuthorFriendlyname;
/** @var string $sAuthorInitials */
protected $sAuthorInitials;
/** @var string $sAuthorPictureAbsUrl */
protected $sAuthorPictureAbsUrl;
/** @var bool $bIsFromCurrentUser Flag to know if the user who made the activity was the current user */
protected $bIsFromCurrentUser;
/** @var string $sOrigin Origin of the entry (case log, cron, lifecycle, user edit, ...) */
protected $sOrigin;
/**
* ActivityEntry constructor.
*
* @param \DateTime $oDateTime
* @param string $sAuthorLogin
* @param string|null $sContent
* @param string|null $sId
*
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, string $sAuthorLogin, ?string $sContent = null, ?string $sId = null)
{
parent::__construct($sId);
$this->SetType(static::DEFAULT_TYPE);
$this->SetDecorationClasses(static::DEFAULT_DECORATION_CLASSES);
$this->SetContent($sContent);
$this->SetDateTime($oDateTime);
$this->SetAuthor($sAuthorLogin);
$this->SetOrigin(static::DEFAULT_ORIGIN);
}
/**
* Set the type of the entry (eg. case log, edits, transition, ...)
*
* @param string $sType
*
* @return $this
*/
public function SetType(string $sType)
{
$this->sType = $sType;
return $this;
}
/**
* Return the type of the entry (eg. case log, edits, transition, ...)
*
* @return string
*/
public function GetType()
{
return $this->sType;
}
/**
* Set the CSS decoration classes
*
* @param string $sDecorationClasses Must be a space-separated list of CSS classes
*
* @return $this
*/
public function SetDecorationClasses(string $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
*
* @param string|null $sContent
*
* @return $this
*/
public function SetContent(?string $sContent)
{
$this->sContent = $sContent;
return $this;
}
/**
* Return the raw content without any filtering / escaping
*
* @return string
*/
public function GetContent()
{
return $this->sContent;
}
/**
* @param \DateTime $oDateTime
*
* @return $this
*/
public function SetDateTime(DateTime $oDateTime)
{
$this->oDateTime = $oDateTime;
return $this;
}
/**
* Return the date time without formatting, as per the mysql format
* @return string
*/
public function GetRawDateTime()
{
return $this->oDateTime->format(AttributeDateTime::GetInternalFormat());
}
/**
* Return the date time formatted as per the iTop config.
*
* @return string
* @throws \Exception
*/
public function GetFormattedDateTime()
{
$oDateTimeFormat = AttributeDateTime::GetFormat();
return $oDateTimeFormat->Format($this->oDateTime);
}
/**
* Set the author and its information based on the $sAuthorLogin
*
* @param string $sAuthorLogin
*
* @return $this
* @throws \OQLException
* @throws \Exception
*/
public function SetAuthor(string $sAuthorLogin)
{
$this->sAuthorLogin = $sAuthorLogin;
// 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)
{
$this->sAuthorFriendlyname = $this->sAuthorLogin;
}
else
{
// TODO 3.0.0: Check that this does not return '' when author is the CRON or an extension.
$this->sAuthorFriendlyname = UserRights::GetUserFriendlyName($this->sAuthorLogin);
}
$this->sAuthorInitials = UserRights::GetUserInitials($this->sAuthorLogin);
$this->sAuthorPictureAbsUrl = UserRights::GetContactPictureAbsUrl($this->sAuthorLogin, false);
$this->bIsFromCurrentUser = UserRights::GetUserId($this->sAuthorLogin) === UserRights::GetUserId();
return $this;
}
/**
* @return string
*/
public function GetAuthorLogin()
{
return $this->sAuthorLogin;
}
/**
* @return string
*/
public function GetAuthorFriendlyname()
{
return $this->sAuthorFriendlyname;
}
/**
* @return string
*/
public function GetAuthorInitials()
{
return $this->sAuthorInitials;
}
/**
* @return string
*/
public function GetAuthorPictureAbsUrl()
{
return $this->sAuthorPictureAbsUrl;
}
/**
* Return true if the current user is the author of the activity entry
*
* @return bool
*/
public function IsFromCurrentUser()
{
return $this->bIsFromCurrentUser;
}
/**
* Set the origin of the activity entry
*
* @param string $sOrigin
*
* @return $this
*/
protected function SetOrigin(string $sOrigin)
{
$this->sOrigin = $sOrigin;
return $this;
}
/**
* Return the origin of the activity entry
*
* @return string
*/
public function GetOrigin()
{
return $this->sOrigin;
}
}

View File

@@ -0,0 +1,129 @@
<?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 AttributeDateTime;
use CMDBChangeOp;
use DateTime;
use Exception;
use MetaModel;
use ReflectionClass;
/**
* Class ActivityEntryFactory
*
* @internal
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @since 3.0.0
*/
class ActivityEntryFactory
{
/**
* Make an ActivityEntry entry (for ActivityPanel) based on the $oChangeOp.
*
* @param \CMDBChangeOp $oChangeOp
*
* @return \Combodo\iTop\Application\UI\Base\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\Base\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.
*
* @param string $sAttCode Code of the case log attribute
* @param array $aOrmEntry
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\CaseLogEntry
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*/
public static function MakeFromCaseLogEntryArray(string $sAttCode, array $aOrmEntry)
{
$oUser = MetaModel::GetObject('User', $aOrmEntry['user_id'], false, true);
$sUserLogin = ($oUser === null) ? '' : $oUser->Get('login');
$oEntry = new CaseLogEntry(
DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $aOrmEntry['date']),
$sUserLogin,
$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\Base\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp;
/**
* Class CMDBChangeOpAttachmentAddedFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpAttachmentAddedFactory extends CMDBChangeOpFactory
{
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-paperclip';
}

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

View File

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

View File

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

View File

@@ -0,0 +1,94 @@
<?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\CMDBChangeOp;
use AttributeDateTime;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntry;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EditsEntry;
use DateTime;
use iCMDBChangeOp;
use MetaModel;
/**
* Class CMDBChangeOpFactory
*
* Default factory for CMDBChangeOp change ops
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\CMDBChangeOp
*/
class CMDBChangeOpFactory
{
/** @var string DEFAULT_TYPE Used to overload the type from the ActivityEntry */
public const DEFAULT_TYPE = EditsEntry::DEFAULT_TYPE;
/** @var string DEFAULT_DECORATION_CLASSES Used to overload the decoration classes from the ActivityEntry */
public const DEFAULT_DECORATION_CLASSES = ActivityEntry::DEFAULT_DECORATION_CLASSES;
/**
* Make an ActivityEntry from the iCMDBChangeOp $oChangeOp
*
* @param \iCMDBChangeOp $oChangeOp
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntry
* @throws \OQLException
*/
public static function MakeFromCmdbChangeOp(iCMDBChangeOp $oChangeOp)
{
$oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oChangeOp->Get('date'));
$sContent = $oChangeOp->GetDescription();
// Retrieve author login
$sAuthorLogin = static::GetUserLoginFromChangeOp($oChangeOp);
$oEntry = new ActivityEntry($oDateTime, $sAuthorLogin, $sContent);
$oEntry->SetType(static::DEFAULT_TYPE)
->SetDecorationClasses(static::DEFAULT_DECORATION_CLASSES);
return $oEntry;
}
/**
* Return the login of the $oChangeOp author or its friendlyname if the user cannot be retrieved.
*
* @param \iCMDBChangeOp $oChangeOp
*
* @return string|null
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public static function GetUserLoginFromChangeOp(iCMDBChangeOp $oChangeOp)
{
$iAuthorId = $oChangeOp->Get('user_id');
// - Set login in the friendlyname as a fallback
$sAuthorLogin = $oChangeOp->Get('userinfo');
// - Try to find user login from its ID if present (since iTop 3.0.0)
if(empty($iAuthorId) === false)
{
$oAuthor = MetaModel::GetObject('User', $iAuthorId, false, true);
if(empty($oAuthor) === false)
{
$sAuthorLogin = $oAuthor->Get('login');
}
}
return $sAuthorLogin;
}
}

View File

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

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

View File

@@ -0,0 +1,103 @@
<?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 CaseLogEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 3.0.0
*/
class CaseLogEntry extends ActivityEntry
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-caselog-entry';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/activity-entry/caselog-entry';
public const DEFAULT_TYPE = 'caselog';
public const DEFAULT_DECORATION_CLASSES = 'fas fa-fw fa-quote-left';
// Specific constants
public const DEFAULT_CASELOG_RANK = 0;
/** @var string $sAttCode Code of the corresponding case log attribute */
protected $sAttCode;
/** @var int $iCaseLogRank Rank of its case log in the host panel, can be used for highlight purposes for example */
protected $iCaseLogRank;
/**
* CaseLogEntry constructor.
*
* @param \DateTime $oDateTime
* @param \User $sAuthorLogin
* @param string $sAttCode
* @param string $sContent
* @param string|null $sId
*
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, string $sAuthorLogin, string $sAttCode, string $sContent, ?string $sId = null)
{
parent::__construct($oDateTime, $sAuthorLogin, $sContent, $sId);
$this->sAttCode = $sAttCode;
$this->SetCaseLogRank(static::DEFAULT_CASELOG_RANK);
$this->SetOrigin('caselog:'.$this->sAttCode);
}
/**
* Return the code of the corresponding case log attribute
*
* @return string
*/
public function GetAttCode()
{
return $this->sAttCode;
}
/**
* Set the rank of the case log in the host panel
*
* @param int $iCaseLogRank
*
* @return $this
*/
public function SetCaseLogRank(int $iCaseLogRank)
{
$this->iCaseLogRank = $iCaseLogRank;
return $this;
}
/**
* Return the rank of the case log in the host panel
*
* @return int
*/
public function GetCaseLogRank()
{
return $this->iCaseLogRank;
}
}

View File

@@ -0,0 +1,206 @@
<?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;
use Dict;
use Exception;
use MetaModel;
/**
* Class EditsEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 3.0.0
*/
class EditsEntry extends ActivityEntry
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-edits-entry';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/activity-entry/edits-entry';
public const DEFAULT_TYPE = 'edits';
public 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|null $sId
*
* @throws \OQLException
*/
public function __construct(DateTime $oDateTime, string $sAuthorLogin, string $sObjectClass, ?string $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(array $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(string $sAttCode, string $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(string $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\Base\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,148 @@
<?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;
use MetaModel;
/**
* Class TransitionEntry
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry
* @internal
* @since 3.0.0
*/
class TransitionEntry extends ActivityEntry
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-transition-entry';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/activity-entry/transition-entry';
public const DEFAULT_TYPE = 'transition';
public 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|null $sId
*
* @throws \CoreException
* @throws \OQLException
*/
public function __construct(
DateTime $oDateTime, string $sAuthorLogin, string $sObjectClass, string $sOriginStateCode, string $sTargetStateCode,
?string $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(string $sObjectClass, string $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 $sObjectClass
* @param string $sStateCode
*
* @return $this
* @throws \CoreException
*/
public function SetTargetState(string $sObjectClass, string $sStateCode)
{
$this->sTargetStateCode = $sStateCode;
$this->sTargetStateLabel = MetaModel::GetStateLabel($sObjectClass, $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

@@ -0,0 +1,602 @@
<?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;
use AttributeDateTime;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntry;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\CaseLogEntry;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm;
use Combodo\iTop\Application\UI\Base\UIBlock;
use DBObject;
use Exception;
use MetaModel;
/**
* Class ActivityPanel
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel
* @internal
* @since 3.0.0
*/
class ActivityPanel extends UIBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-activity-panel';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/layout';
public const JS_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/layout';
public const JS_FILES_REL_PATH = [
'js/layouts/activity-panel/activity-panel.js',
];
/** @var \DBObject $oObject The object for which the activity panel is for */
protected $oObject;
/**
* @var string $sObjectMode Display mode of $oObject (create, edit, view, ...)
* @see \cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
*/
protected $sObjectMode;
/** @var array $aCaseLogs Metadata of the case logs (att. code, color, ...), will be use to make the tabs and identify them easily */
protected $aCaseLogs;
/** @var ActivityEntry[] $aEntries */
protected $aEntries;
/** @var bool $bAreEntriesSorted True if the entries have been sorted by date */
protected $bAreEntriesSorted;
/** @var bool $bHasLifecycle True if the host object has a lifecycle */
protected $bHasLifecycle;
/** @var \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm $oActivityTabEntryForm New entry form for the activity tab which is different from the case log tabs */
protected $oActivityTabEntryForm;
/** @var \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm[] $aCaseLogTabsEntryForms */
protected $aCaseLogTabsEntryForms;
/**
* ActivityPanel constructor.
*
* @param \DBObject $oObject
* @param ActivityEntry[] $aEntries
* @param string|null $sId
*
* @throws \CoreException
* @throws \Exception
*/
public function __construct(DBObject $oObject, array $aEntries = [], ?string $sId = null)
{
parent::__construct($sId);
$this->InitializeCaseLogTabs();
$this->InitializeCaseLogTabsEntryForms();
$this->SetObject($oObject);
$this->SetObjectMode(cmdbAbstractObject::DEFAULT_OBJECT_MODE);
$this->SetEntries($aEntries);
$this->bAreEntriesSorted = false;
}
/**
* Set the object the panel is for, and initialize the corresponding case log tabs.
*
* @param \DBObject $oObject
*
* @return $this
* @throws \CoreException
* @throws \Exception
*/
protected function SetObject(DBObject $oObject)
{
$this->oObject = $oObject;
$sObjectClass = get_class($this->oObject);
// Initialize the case log tabs
$this->InitializeCaseLogTabs();
$this->InitializeCaseLogTabsEntryForms();
$aCaseLogAttCodes = MetaModel::GetCaseLogs($sObjectClass);
foreach($aCaseLogAttCodes as $sCaseLogAttCode)
{
$this->AddCaseLogTab($sCaseLogAttCode);
}
// Check if object has a lifecycle
$this->bHasLifecycle = !empty(MetaModel::GetStateAttributeCode($sObjectClass));
return $this;
}
/**
* Return the object for which the activity panel is for
*
* @return \DBObject
*/
public function GetObject()
{
return $this->oObject;
}
/**
* Return the object id for which the activity panel is for
*
* @return int
*/
public function GetObjectId(): int {
return $this->oObject->GetKey();
}
/**
* Return the object class for which the activity panel is for
*
* @return string
*/
public function GetObjectClass(): string {
return get_class($this->oObject);
}
/**
* Set the display mode of the $oObject
*
* @param string $sMode
* @see cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
*
* @return $this
* @throws \Exception
*/
public function SetObjectMode(string $sMode)
{
// Consistency check
if(!in_array($sMode, cmdbAbstractObject::EnumObjectModes())){
throw new Exception("Activity panel: Object mode '$sMode' not allowed, should be either ".implode(' / ', cmdbAbstractObject::EnumObjectModes()));
}
$this->sObjectMode = $sMode;
return $this;
}
/**
* Return the display mode of the $oObject
*
* @see cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
* @return string
*/
public function GetObjectMode(): string
{
return $this->sObjectMode;
}
/**
* Set all entries at once.
*
* @param ActivityEntry[] $aEntries
*
* @return $this
* @throws \Exception
*/
public function SetEntries(array $aEntries)
{
// Reset entries
$this->aEntries = [];
foreach ($aEntries as $oEntry)
{
$this->AddEntry($oEntry);
}
return $this;
}
/**
* Return all the entries
*
* @return ActivityEntry[]
*/
public function GetEntries()
{
if ($this->bAreEntriesSorted === false)
{
$this->SortEntries();
}
return $this->aEntries;
}
/**
* Return all the entries grouped by author / origin (case log).
* This is useful for the template as it avoid to make the processing there.
*
* @return array
*/
public function GetGroupedEntries()
{
$aGroupedEntries = [];
$aCurrentGroup = ['author_login' => null, 'origin' => null, 'entries' => []];
$aPreviousEntryData = ['author_login' => null, 'origin' => null];
foreach($this->GetEntries() as $sId => $oEntry)
{
// New entry data
$sAuthorLogin = $oEntry->GetAuthorLogin();
$sOrigin = $oEntry->GetOrigin();
// Check if it's time to change of group
if(($sAuthorLogin !== $aPreviousEntryData['author_login']) || ($sOrigin !== $aPreviousEntryData['origin']))
{
// Flush current group if necessary
if(empty($aCurrentGroup['entries']) === false)
{
$aGroupedEntries[] = $aCurrentGroup;
}
// Init (first iteration) or reset (other iterations) current group
$aCurrentGroup = ['author_login' => $sAuthorLogin, 'origin' => $sOrigin, 'entries' => []];
}
$aCurrentGroup['entries'][] = $oEntry;
$aPreviousEntryData = ['author_login' => $sAuthorLogin, 'origin' => $sOrigin];
}
// Flush last group
if(empty($aCurrentGroup['entries']) === false)
{
$aGroupedEntries[] = $aCurrentGroup;
}
return $aGroupedEntries;
}
/**
* Sort all entries based on the their date, descending.
*
* @return $this
*/
protected function SortEntries()
{
if(count($this->aEntries) > 1)
{
uasort($this->aEntries, function($oEntryA, $oEntryB){
/** @var ActivityEntry $oEntryA */
/** @var ActivityEntry $oEntryB */
$sDateTimeA = $oEntryA->GetRawDateTime();
$sDateTimeB = $oEntryB->GetRawDateTime();
if ($sDateTimeA === $sDateTimeB)
{
return 0;
}
return ($sDateTimeA > $sDateTimeB) ? -1 : 1;
});
}
$this->bAreEntriesSorted = true;
return $this;
}
/**
* Add an $oEntry after all others, excepted if there is already an entry with the same ID in which case it replaces it.
*
* @param \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntry $oEntry
*
* @return $this
* @throws \Exception
*/
public function AddEntry(ActivityEntry $oEntry)
{
$this->aEntries[$oEntry->GetId()] = $oEntry;
$this->bAreEntriesSorted = false;
// Add case log to the panel and update metadata when necessary
if ($oEntry instanceof CaseLogEntry)
{
$sCaseLogAttCode = $oEntry->GetAttCode();
$sAuthorLogin = $oEntry->GetAuthorLogin();
// Initialize case log metadata
if ($this->HasCaseLogTab($sCaseLogAttCode) === false)
{
$this->AddCaseLogTab($sCaseLogAttCode);
}
// Add case log rank to the entry
$oEntry->SetCaseLogRank($this->aCaseLogs[$sCaseLogAttCode]['rank']);
// Update metadata
// - Message count
$this->aCaseLogs[$sCaseLogAttCode]['total_messages_count']++;
// - Authors
if(array_key_exists($sAuthorLogin, $this->aCaseLogs[$sCaseLogAttCode]['authors']) === false)
{
$this->aCaseLogs[$sCaseLogAttCode]['authors'][$sAuthorLogin] = [
'messages_count' => 0,
];
}
$this->aCaseLogs[$sCaseLogAttCode]['authors'][$sAuthorLogin]['messages_count']++;
}
return $this;
}
/**
* Remove entry of ID $sEntryId.
* Note that if there is no entry with that ID, it proceeds silently.
*
* @param string $sEntryId
*
* @return $this
*/
public function RemoveEntry(string $sEntryId)
{
if (array_key_exists($sEntryId, $this->aEntries))
{
// Recompute case logs metadata only if necessary
$oEntry = $this->aEntries[$sEntryId];
if ($oEntry instanceof CaseLogEntry)
{
$sCaseLogAttCode = $oEntry->GetAttCode();
$sAuthorLogin = $oEntry->GetAuthorLogin();
// Update metadata
// - Message count
$this->aCaseLogs[$sCaseLogAttCode]['total_messages_count']--;
// - Authors
$this->aCaseLogs[$sCaseLogAttCode]['authors'][$sAuthorLogin]['messages_count']--;
if($this->aCaseLogs[$sCaseLogAttCode]['authors'][$sAuthorLogin]['messages_count'] === 0)
{
unset($this->aCaseLogs[$sCaseLogAttCode]['authors'][$sAuthorLogin]);
}
}
unset($this->aEntries[$sEntryId]);
}
return $this;
}
/**
* Return true if there is at least one entry
*
* @return bool
*/
public function HasEntries()
{
return !empty($this->aEntries);
}
/**
* Return all the case log tabs metadata, not their entries
*
* @return array
*/
public function GetCaseLogTabs()
{
return $this->aCaseLogs;
}
/**
* @return $this
*/
protected function InitializeCaseLogTabs()
{
$this->aCaseLogs = [];
return $this;
}
/**
* Add the case log tab to the panel
* Note: Case log entries are added separately, see static::AddEntry()
*
* @param string $sAttCode
*
* @return $this
* @throws \Exception
*/
protected function AddCaseLogTab(string $sAttCode)
{
// Add case log only if not already existing
if (!array_key_exists($sAttCode, $this->aCaseLogs))
{
$this->aCaseLogs[$sAttCode] = [
'rank' => count($this->aCaseLogs) + 1,
'title' => MetaModel::GetLabel(get_class($this->oObject), $sAttCode),
'total_messages_count' => 0,
'authors' => [],
];
}
return $this;
}
/**
* Remove the case log tab from the panel.
* Note: Case log entries will not be removed.
*
* @param string $sAttCode
*
* @return $this
*/
protected function RemoveCaseLogTab(string $sAttCode)
{
if (array_key_exists($sAttCode, $this->aCaseLogs))
{
unset($this->aCaseLogs[$sAttCode]);
}
return $this;
}
/**
* Return true if the case log of $sIs code has been initialized.
*
* @param string $sAttCode
*
* @return bool
*/
public function HasCaseLogTab(string $sAttCode)
{
return isset($this->aCaseLogs[$sAttCode]);
}
/**
* Return true if there is at least one case log declared.
*
* @return bool
*/
public function HasCaseLogTabs()
{
return !empty($this->aCaseLogs);
}
/**
* Empty the caselogs entry forms
*
* @return $this
*/
protected function InitializeCaseLogTabsEntryForms()
{
$this->aCaseLogTabsEntryForms = [];
return $this;
}
/**
* Return all entry forms for all case log tabs
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm[]
*/
public function GetCaseLogTabsEntryForms(): array
{
return $this->aCaseLogTabsEntryForms;
}
/**
* Set the $oCaseLogEntryForm for the $sCaseLogId tab.
* Note: If there is no caselog for that ID, it will proceed silently.
*
* @param string $sCaseLogId
* @param \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm $oCaseLogEntryForm
*
* @return $this
*/
public function SetCaseLogTabEntryForm(string $sCaseLogId, CaseLogEntryForm $oCaseLogEntryForm)
{
if ($this->HasCaseLogTab($sCaseLogId)){
$this->aCaseLogTabsEntryForms[$sCaseLogId] = $oCaseLogEntryForm;
}
return $this;
}
/**
* Return the caselog entry form for the $sCaseLogId tab
*
* @param string $sCaseLogId
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm
*/
public function GetCaseLogTabEntryForm(string $sCaseLogId)
{
return $this->aCaseLogTabsEntryForms[$sCaseLogId];
}
/**
* @param string $sCaseLogId
*
* @return bool
*/
public function HasCaseLogTabEntryForm(string $sCaseLogId): bool
{
return !empty($this->aCaseLogTabsEntryForms[$sCaseLogId]);
}
/**
* Return true if the host object has a lifecycle
*
* @return bool
*/
public function HasLifecycle()
{
return $this->bHasLifecycle;
}
/**
* Return the formatted (user-friendly) date time format for the JS widget.
* Will be used by moment.js for instance.
*
* @return string
*/
public function GetDateTimeFormatForJSWidget()
{
$oDateTimeFormat = AttributeDateTime::GetFormat();
return $oDateTimeFormat->ToMomentJS();
}
/**
* Return the entry form for the activity tab
*
* @see $oActivityTabEntryForm
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm
*/
public function GetActivityTabEntryForm(): CaseLogEntryForm
{
return $this->oActivityTabEntryForm;
}
/**
* Set the entry form for the activity tab
*
* @param \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm $oCaseLogEntryForm
* @see $oActivityTabEntryForm
*
* @return $this
*
*/
public function SetActivityTabEntryForm(CaseLogEntryForm $oCaseLogEntryForm)
{
$this->oActivityTabEntryForm = $oCaseLogEntryForm;
return $this;
}
/**
* Return true is there is an entry form for the activity tab
*
* @return bool
*/
public function HasActivityTabEntryForm()
{
return $this->oActivityTabEntryForm !== null;
}
/**
* @inheritdoc
*/
public function GetSubBlocks()
{
$aSubBlocks = array();
foreach($this->GetCaseLogTabsEntryForms() as $sCaseLogId => $oCaseLogEntryForm) {
$aSubBlocks[$oCaseLogEntryForm->GetId()] = $oCaseLogEntryForm;
}
if ($this->HasActivityTabEntryForm()) {
$oNewEntryForm = $this->GetActivityTabEntryForm();
$aSubBlocks[$oNewEntryForm->GetId()] = $oNewEntryForm;
}
return $aSubBlocks;
}
}

View File

@@ -0,0 +1,136 @@
<?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;
use cmdbAbstractObject;
use CMDBChangeOpSetAttributeCaseLog;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\EditsEntry;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryFormFactory\CaseLogEntryFormFactory;
use DBObject;
use DBObjectSearch;
use DBObjectSet;
use Exception;
use MetaModel;
/**
* Class ActivityPanelFactory
*
* @internal
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel
* @since 3.0.0
*/
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
*
* @return \Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityPanel
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MySQLException
* @throws \OQLException
*/
public static function MakeForObjectDetails(DBObject $oObject, string $sMode = cmdbAbstractObject::DEFAULT_OBJECT_MODE)
{
$sObjClass = get_class($oObject);
$iObjId = $oObject->GetKey();
$oActivityPanel = new ActivityPanel($oObject);
$oActivityPanel->SetObjectMode($sMode);
// Prepare caselogs
$aCaseLogAttCodes = array_keys($oActivityPanel->GetCaseLogTabs());
foreach($aCaseLogAttCodes as $sCaseLogAttCode)
{
// Add new entry block
$oActivityPanel->SetCaseLogTabEntryForm($sCaseLogAttCode, CaseLogEntryFormFactory::MakeForCaselogTab($oObject, $sCaseLogAttCode, $sMode));
// Retrieve case logs entries
/** @var \ormCaseLog $oCaseLog */
$oCaseLog = $oObject->Get($sCaseLogAttCode);
foreach($oCaseLog->GetAsArray() as $aOrmEntry)
{
$oCaseLogEntry = ActivityEntryFactory::MakeFromCaseLogEntryArray($sCaseLogAttCode, $aOrmEntry);
$oActivityPanel->AddEntry($oCaseLogEntry);
}
}
// Activity tab entry form is only in view mode
// As caselog tabs input will be attached to the main object form and submit button hidden, we can't have an entry form in the activity tab as it's not for a specific caselog
if($sMode === cmdbAbstractObject::ENUM_OBJECT_MODE_VIEW) {
$oActivityPanel->SetActivityTabEntryForm(CaseLogEntryFormFactory::MakeForActivityTab($oObject, $sMode));
}
// 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');
// Note: We can't order by date (only) as something multiple CMDBChangeOp rows are inserted at the same time (eg. Delivery model of the "Demo" Organization in the sample data).
// As the DB returns rows "chronologically", we get the older first and it messes with the processing. Ordering by the ID is way much simpler and less DB CPU consuming.
$oChangesSet = new DBObjectSet($oChangesSearch, ['id' => 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');
try {
$oEntry = ActivityEntryFactory::MakeFromCmdbChangeOp($oChangeOp);
} catch (Exception $e) {
continue;
}
// If same CMDBChange and mergeable edits entry from the same author, we merge them
if (($iChangeId == $iPreviousChangeId) && ($oPreviousEditsEntry instanceof EditsEntry) && ($oEntry instanceof EditsEntry) && ($oPreviousEditsEntry->GetAuthorLogin() === $oEntry->GetAuthorLogin())) {
$oPreviousEditsEntry->Merge($oEntry);
} else {
$oActivityPanel->AddEntry($oEntry);
// Set previous edits entry
if($oEntry instanceof EditsEntry)
{
$oPreviousEditsEntry = $oEntry;
}
}
$iPreviousChangeId = $iChangeId;
}
return $oActivityPanel;
}
}

View File

@@ -0,0 +1,297 @@
<?php
/*
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\UIBlock;
/**
* Class CaseLogEntryForm
*
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm
*/
class CaseLogEntryForm extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-caselog-entry-form';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/caselog-entry-form/layout';
public const JS_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/caselog-entry-form/layout';
public const JS_FILES_REL_PATH = [
'js/layouts/activity-panel/caselog-entry-form.js',
];
/** @var string Form is autonomous and can send data on its own */
public const ENUM_SUBMIT_MODE_AUTONOMOUS = 'autonomous';
/** @var string Form is bridged to its host object form */
public const ENUM_SUBMIT_MODE_BRIDGED = 'bridged';
/** @var string Container of the form is a specific caselog tab */
public const ENUM_CONTAINER_TAB_TYPE_CASELOG = 'caselog';
/** @var string Container of the form is the activity tab */
public const ENUM_CONTAINER_TAB_TYPE_ACTIVITY = 'activity';
/** @var string */
public const DEFAULT_SUBMIT_MODE = self::ENUM_SUBMIT_MODE_AUTONOMOUS;
/** @var string */
public const DEFAULT_CONTAINER_TAB_TYPE = self::ENUM_CONTAINER_TAB_TYPE_ACTIVITY;
/**
* @var string Whether the form can send data on its own or if it's bridged with its host object form
* @see static::ENUM_SUBMIT_MODE_XXX
*/
protected $sSubmitMode;
/**
* @var string Whether the form container is a caselog tab or an activity tab
* @see static::ENUM_CONTAINER_TAB_TYPE_XXX
*/
protected $sContainerTabType;
/** @var \Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText $oTextInput The main input to write a case log entry */
protected $oTextInput;
/** @var \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu Menu for possible options on the send button */
protected $oSendButtonPopoverMenu;
/** @var array $aMainActionButtons The form main actions (send, cancel, ...) */
protected $aMainActionButtons;
/** @var array $aExtraActionButtons The form extra actions, can be populated through a public API */
protected $aExtraActionButtons;
/**
* CaseLogEntryForm constructor.
*
* @param null $sName
*/
public function __construct($sName = null)
{
parent::__construct($sName);
$this->sSubmitMode = static::DEFAULT_SUBMIT_MODE;
$this->sContainerTabType = static::DEFAULT_CONTAINER_TAB_TYPE;
$this->SetTextInput(new RichText());
$this->aMainActionButtons = [];
$this->aExtraActionButtons = [];
}
/**
* @see $sSubmitMode
*
* @return string
*/
public function GetSubmitMode(): string
{
return $this->sSubmitMode;
}
/**
* @param string $sSubmitMode
* @see $sSubmitMode
*
* @return $this
*/
public function SetSubmitMode(string $sSubmitMode)
{
$this->sSubmitMode = $sSubmitMode;
return $this;
}
/**
* Set the submit mode (autonomous, bridged) from the host object mode (create, edit, view, ...)
* eg. create => bridged, view => autonomous.
*
* @param string $sObjectMode
* @see $sSubmitMode
* @see cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
*
* @return $this
*/
public function SetSubmitModeFromHostObjectMode($sObjectMode)
{
switch ($sObjectMode){
case cmdbAbstractObject::ENUM_OBJECT_MODE_CREATE:
case cmdbAbstractObject::ENUM_OBJECT_MODE_EDIT:
$sSubmitMode = static::ENUM_SUBMIT_MODE_BRIDGED;
break;
case cmdbAbstractObject::ENUM_OBJECT_MODE_VIEW:
case cmdbAbstractObject::ENUM_OBJECT_MODE_STIMULUS:
default:
$sSubmitMode = static::ENUM_SUBMIT_MODE_AUTONOMOUS;
break;
}
$this->SetSubmitMode($sSubmitMode);
return $this;
}
/**
* Return true if the submit mode is autonomous
*
* @see $sSubmitMode
*
* @return bool
*/
public function IsSubmitAutonomous(): bool
{
return $this->GetSubmitMode() === static::ENUM_SUBMIT_MODE_AUTONOMOUS;
}
/**
* @see $sContainerTabType
*
* @return string
*/
public function GetContainerTabType(): string
{
return $this->sContainerTabType;
}
/**
* @param string $sContainerTabType
* @see $sContainerTabType
*
* @return $this
*/
public function SetContainerTabType(string $sContainerTabType)
{
$this->sContainerTabType = $sContainerTabType;
return $this;
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText
*/
public function GetTextInput(): RichText
{
return $this->oTextInput;
}
/**
* @param \Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText $oTextInput
*
* @return $this
*/
public function SetTextInput(RichText $oTextInput)
{
$this->oTextInput = $oTextInput;
return $this;
}
/**
* @return \Combodo\iTop\Application\UI\Base\UIBlock[]
*/
public function GetMainActionButtons()
{
return $this->aMainActionButtons;
}
/**
* Set all main action buttons at once, replacing all existing ones
*
* @param array $aFormActionButtons
* @return $this
*/
public function SetMainActionButtons(array $aFormActionButtons)
{
$this->aMainActionButtons = $aFormActionButtons;
return $this;
}
/**
* @param \Combodo\iTop\Application\UI\Base\UIBlock $oMainActionButton
* @return $this;
*/
public function AddMainActionButtons(UIBlock $oMainActionButton)
{
$this->aMainActionButtons[] = $oMainActionButton;
return $this;
}
/**
* @return \Combodo\iTop\Application\UI\Base\UIBlock[]
*/
public function GetExtraActionButtons()
{
return $this->aExtraActionButtons;
}
/**
* Set all extra action buttons at once, replacing all existing ones
*
* @param array $aExtraActionButtons
* @see $aExtraActionButtons
*
* @return $this
*/
public function SetExtraActionButtons(array $aExtraActionButtons)
{
$this->aExtraActionButtons = $aExtraActionButtons;
return $this;
}
/**
* @param \Combodo\iTop\Application\UI\Base\UIBlock $oExtraActionButton
*
* @return $this;
* @see $aExtraActionButtons
*
*/
public function AddExtraActionButtons(UIBlock $oExtraActionButton)
{
$this->aExtraActionButtons[] = $oExtraActionButton;
return $this;
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu
*/
public function GetSendButtonPopoverMenu(): PopoverMenu
{
return $this->oSendButtonPopoverMenu;
}
/**
* @param \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu $oCaseLogSelectionPopOverMenu
* @return $this
*/
public function SetSendButtonPopoverMenu(PopoverMenu $oCaseLogSelectionPopOverMenu)
{
$this->oSendButtonPopoverMenu = $oCaseLogSelectionPopOverMenu;
return $this;
}
/**
* Return true is there is a PopoverMenu for the send button
*
* @return bool
*/
public function HasSendButtonPopoverMenu(): bool
{
return $this->oSendButtonPopoverMenu !== null;
}
/**
* @inheritdoc
*/
public function GetSubBlocks(): array
{
$aSubBlocks = [];
$aSubBlocks[$this->GetTextInput()->GetId()] = $this->GetTextInput();
foreach ($this->GetExtraActionButtons() as $oExtraActionButton)
{
$aSubBlocks[$oExtraActionButton->GetId()] = $oExtraActionButton;
}
foreach ($this->GetMainActionButtons() as $oMainActionButton)
{
$aSubBlocks[$oMainActionButton->GetId()] = $oMainActionButton;
}
$aSubBlocks[$this->GetSendButtonPopoverMenu()->GetId()] = $this->GetSendButtonPopoverMenu();
return $aSubBlocks;
}
}

View File

@@ -0,0 +1,164 @@
<?php
/*
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryFormFactory;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonFactory;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm\CaseLogEntryForm;
use DBObject;
use DBObjectSet;
use Dict;
use JSPopupMenuItem;
use MetaModel;
use UserRights;
/**
* Class CaseLogEntryFormFactory
*
* @internal
* @author Stephen Abello <stephen.abello@combodo.com>
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryFormFactory
* @since 3.0.0
*/
class CaseLogEntryFormFactory
{
public static function MakeForCaselogTab(DBObject $oObject, string $sCaseLogAttCode, string $sObjectMode = cmdbAbstractObject::DEFAULT_OBJECT_MODE)
{
$oCaseLogEntryForm = new CaseLogEntryForm();
$oCaseLogEntryForm->SetSubmitModeFromHostObjectMode($sObjectMode)
->AddMainActionButtons(static::PrepareCancelButton())
->AddMainActionButtons(static::PrepareSendButton()->SetLabel(Dict::S('UI:Button:AddEntryAndWithChoice')))
->SetSendButtonPopoverMenu(static::PrepareSendActionSelectionPopoverMenu($oObject, $sCaseLogAttCode));
return $oCaseLogEntryForm;
}
public static function MakeForActivityTab(DBObject $oObject, string $sObjectMode = cmdbAbstractObject::DEFAULT_OBJECT_MODE)
{
$oCaseLogEntryForm = new CaseLogEntryForm();
$oCaseLogEntryForm->SetSubmitModeFromHostObjectMode($sObjectMode)
->AddMainActionButtons(static::PrepareCancelButton())
->AddMainActionButtons(static::PrepareSendButton()->SetLabel(Dict::S('UI:Button:AddEntryToWithChoice')));
$oCaseLogEntryForm->SetSendButtonPopoverMenu(static::PrepareTargetCaseLogSelectionPopoverMenu($oObject));
return $oCaseLogEntryForm;
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\Button\Button
*/
protected static function PrepareCancelButton(): Button
{
return ButtonFactory::MakeForSecondaryAction(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\Button\Button
*/
protected static function PrepareSendButton(): Button
{
$oButton = ButtonFactory::MakeForPrimaryAction(Dict::S('UI:Button:Send'), 'send', 'send');
$oButton->SetIconClass('fas fa-paper-plane');
return $oButton;
}
protected static function PrepareSendActionSelectionPopoverMenu(DBObject $oObject, string $sCaseLogAttCode): PopoverMenu
{
$sObjClass = get_class($oObject);
$oMenu = new PopoverMenu();
$sSectionId = 'send-actions';
$oMenu->AddSection($sSectionId);
$sCaseLogEntryFormDataRole = CaseLogEntryForm::BLOCK_CODE;
// Standard, just save
$oMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
new JSPopupMenuItem(
CaseLogEntryForm::BLOCK_CODE.'--add-action--'.$sCaseLogAttCode.'--save',
Dict::S('UI:Button:Save'),
<<<JS
$(this).closest('[data-role="{$sCaseLogEntryFormDataRole}"]').trigger('add_to_caselog.caselog_entry_form.itop', {caselog_att_code: '{$sCaseLogAttCode}'});
JS
)
);
$oMenu->AddItem($sSectionId, $oMenuItem);
// Transitions
// Note: This code is inspired from cmdbAbstract::DisplayModifyForm(), it might be better to factorize it
$aTransitions = $oObject->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && count($aTransitions)) {
$oSetToCheckRights = DBObjectSet::FromObject($oObject);
$aStimuli = Metamodel::EnumStimuli($sObjClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
$oMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
new JSPopupMenuItem(
CaseLogEntryForm::BLOCK_CODE.'--add-action--'.$sCaseLogAttCode.'--stimulus--'.$sStimulusCode,
$aStimuli[$sStimulusCode]->GetLabel(),
<<<JS
$(this).closest('[data-role="{$sCaseLogEntryFormDataRole}"]').trigger('add_to_caselog.caselog_entry_form.itop', {caselog_att_code: '{$sCaseLogAttCode}', stimulus_code: '{$sStimulusCode}'});
JS
)
);
$oMenu->AddItem($sSectionId, $oMenuItem);
break;
}
}
}
return $oMenu;
}
/**
* Return a PopoverMenu with the list of the caselog attributes of $oObject
*
* @param \DBObject $oObject
*
* @return \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu
* @throws \CoreException
* @throws \Exception
*/
protected static function PrepareTargetCaseLogSelectionPopoverMenu(DBObject $oObject): PopoverMenu
{
$sObjClass = get_class($oObject);
$oMenu = new PopoverMenu();
$sSectionId = 'target-caselogs';
$oMenu->AddSection($sSectionId);
$sCaseLogEntryFormDataRole = CaseLogEntryForm::BLOCK_CODE;
foreach(MetaModel::GetCaseLogs($sObjClass) as $sCaseLogAttCode) {
$oMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
new JSPopupMenuItem(
CaseLogEntryForm::BLOCK_CODE.'--target-caselog--'.$sCaseLogAttCode,
MetaModel::GetLabel($sObjClass, $sCaseLogAttCode),
<<<JS
$(this).closest('[data-role="{$sCaseLogEntryFormDataRole}"]').trigger('add_to_caselog.caselog_entry_form.itop', {caselog_att_code: '{$sCaseLogAttCode}'});
JS
)
);
$oMenu->AddItem($sSectionId, $oMenuItem);
}
return $oMenu;
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\Dashboard;
use Combodo\iTop\Application\UI\Base\UIBlock;
class DashboardColumn extends UIBlock
{
public const BLOCK_CODE = 'ibo-dashboard-column';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/dashboard/column/layout';
/** @var UIBlock[] */
protected $aUIBlocks;
/** @var int */
protected $iColumnIndex;
/** @var int */
protected $iCellIndex;
/** @var bool */
protected $bEditMode;
/** @var bool */
protected $bLastRow;
public function __construct(bool $bEditMode = false, bool $bLastRow = false)
{
parent::__construct();
$this->aUIBlocks = [];
$this->iColumnIndex = 0;
$this->iCellIndex = 0;
$this->bEditMode = $bEditMode;
$this->bLastRow = $bLastRow;
}
/**
*
* @param UIBlock $oUIBlock
*
* @return DashboardColumn
*/
public function AddUIBlock(UIBlock $oUIBlock): DashboardColumn
{
$this->aUIBlocks[] = $oUIBlock;
return $this;
}
public function GetSubBlocks()
{
return $this->aUIBlocks;
}
/**
* @return int
*/
public function GetColumnIndex(): int
{
return $this->iColumnIndex;
}
/**
* @param int $iColumnIndex
*
* @return DashboardColumn
*/
public function SetColumnIndex(int $iColumnIndex): DashboardColumn
{
$this->iColumnIndex = $iColumnIndex;
return $this;
}
/**
* @return bool
*/
public function IsEditMode(): bool
{
return $this->bEditMode;
}
/**
* @param bool $bEditMode
*
* @return DashboardColumn
*/
public function SetEditMode(bool $bEditMode): DashboardColumn
{
$this->bEditMode = $bEditMode;
return $this;
}
/**
* @return int
*/
public function GetCellIndex(): int
{
return $this->iCellIndex;
}
/**
* @param int $iCellIndex
*
* @return DashboardColumn
*/
public function SetCellIndex(int $iCellIndex): DashboardColumn
{
$this->iCellIndex = $iCellIndex;
return $this;
}
/**
* @return bool
*/
public function IsLastRow(): bool
{
return $this->bLastRow;
}
/**
* @param bool $bLastRow
*
* @return DashboardColumn
*/
public function SetLastRow(bool $bLastRow): DashboardColumn
{
$this->bLastRow = $bLastRow;
return $this;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\Dashboard;
use Combodo\iTop\Application\UI\Base\UIBlock;
class DashboardLayout extends UIBlock
{
public const BLOCK_CODE = 'ibo-dashboard';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/dashboard/layout';
/** @var DashboardRow[] */
protected $aDashboardRows;
/** @var int */
protected $iRows;
public function __construct(?string $sId = null)
{
parent::__construct($sId);
$this->aDashboardRows = [];
$this->iRows = 0;
}
/**
*
* @param \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardRow $oDashboardRow
*
* @return DashboardLayout
*/
public function AddDashboardRow(DashboardRow $oDashboardRow): DashboardLayout
{
$oDashboardRow->SetRowIndex($this->iRows);
$this->aDashboardRows[] = $oDashboardRow;
$this->iRows++;
return $this;
}
public function GetSubBlocks()
{
return $this->aDashboardRows;
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\Dashboard;
use Combodo\iTop\Application\UI\Base\UIBlock;
class DashboardRow extends UIBlock
{
public const BLOCK_CODE = 'ibo-dashboard-row';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/dashboard/row/layout';
/** @var DashboardColumn[] */
protected $aDashboardColumns;
/** @var int */
protected $iRowIndex;
/** @var int */
protected $iCols;
public function __construct(?string $sId = null)
{
parent::__construct($sId);
$this->aDashboardColumns = [];
$this->iRowIndex = 0;
$this->iCols = 0;
}
/**
*
* @param \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardColumn $oDashboardColumn
*
* @return DashboardRow
*/
public function AddDashboardColumn(DashboardColumn $oDashboardColumn): DashboardRow
{
$oDashboardColumn->SetColumnIndex($this->iCols);
$this->aDashboardColumns[] = $oDashboardColumn;
$this->iCols++;
return $this;
}
public function GetSubBlocks()
{
return $this->aDashboardColumns;
}
/**
* @return int
*/
public function GetRowIndex(): int
{
return $this->iRowIndex;
}
/**
* @param int $iRowIndex
*
* @return DashboardRow
*/
public function SetRowIndex(int $iRowIndex): DashboardRow
{
$this->iRowIndex = $iRowIndex;
return $this;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
/**
* Class Column
*
* @package Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column
* @internal
* @since 3.0.0
*/
class Column extends UIContentBlock {
// Overloaded constants
public const BLOCK_CODE = 'ibo-column';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/multi-column/column/layout';
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\MultiColumn;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\Column;
use Combodo\iTop\Application\UI\Base\UIBlock;
/**
* Class MultiColumn
*
* @package Combodo\iTop\Application\UI\Base\Layout\MultiColumn
* @internal
* @since 3.0.0
*/
class MultiColumn extends UIBlock {
// Overloaded constants
public const BLOCK_CODE = 'ibo-multi-column';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/multi-column/layout';
/** @var \Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\Column[] */
protected $aColumns;
/**
* @inheritDoc
*/
public function __construct(?string $sId = null) {
parent::__construct($sId);
$this->aColumns = [];
}
/**
* @param \Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\Column $oColumn
*
* @return $this
*/
public function AddColumn(Column $oColumn) {
$this->aColumns[] = $oColumn;
return $this;
}
/**
* Remove the column of $iIndex index.
* Note that if the column does not exists, it proceeds silently.
*
* @param int $iIndex
*
* @return $this
*/
public function RemoveColumn(int $iIndex) {
if (isset($this->aColumns[$iIndex])) {
unset($this->aColumns[$iIndex]);
}
return $this;
}
/**
* @inheritDoc
*/
public function GetSubBlocks() {
return $this->aColumns;
}
}

View File

@@ -0,0 +1,342 @@
<?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\NavigationMenu;
use ApplicationContext;
use ApplicationMenu;
use appUserPreferences;
use CMDBObjectSet;
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\NewsroomMenu\NewsroomMenu;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
use Combodo\iTop\Application\UI\Base\UIBlock;
use DBObjectSearch;
use Dict;
use MetaModel;
use UIExtKeyWidget;
use UserRights;
use utils;
/**
* Class NavigationMenu
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\NavigationMenu
* @internal
* @since 3.0.0
*/
class NavigationMenu extends UIBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-navigation-menu';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/navigation-menu/layout';
public const JS_TEMPLATE_REL_PATH = 'base/layouts/navigation-menu/layout';
public const JS_FILES_REL_PATH = [
'js/layouts/navigation-menu.js',
'js/extkeywidget.js',
'js/forms-json-utils.js',
];
/** @var string $sAppRevisionNumber */
protected $sAppRevisionNumber;
/** @var string $sAppSquareIconUrl */
protected $sAppSquareIconUrl;
/** @var string $sAppFullIconUrl */
protected $sAppFullIconUrl;
/** @var string $sAppIconLink */
protected $sAppIconLink;
/** @var array $aSiloSelection */
protected $aSiloSelection;
/** @var array $aMenuGroups */
protected $aMenuGroups;
/** @var array $aUserData */
protected $aUserData;
/** @var \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu $oUserMenu */
private $oUserMenu;
/** @var \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\NewsroomMenu\NewsroomMenu $oNewsroomMenu */
private $oNewsroomMenu;
/** @var bool $bIsExpanded */
protected $bIsExpanded;
/**
* NavigationMenu constructor.
*
* @param \ApplicationContext $oAppContext
* @param \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu $oUserMenu
* @param \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\NewsroomMenu\NewsroomMenu|null $oNewsroomMenu
* @param string|null $sId
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MySQLException
* @throws \Exception
*/
public function __construct(
ApplicationContext $oAppContext, PopoverMenu $oUserMenu, NewsroomMenu $oNewsroomMenu = null, ?string $sId = null
) {
parent::__construct($sId);
$this->sAppRevisionNumber = utils::GetAppRevisionNumber();
$this->sAppSquareIconUrl = Branding::GetCompactMainLogoAbsoluteUrl();
$this->sAppFullIconUrl = Branding::GetFullMainLogoAbsoluteUrl();
$this->sAppIconLink = MetaModel::GetConfig()->Get('app_icon_url');
$this->aSiloSelection = array();
$this->aMenuGroups = ApplicationMenu::GetMenuGroups($oAppContext->GetAsHash());
$this->oUserMenu = $oUserMenu;
$this->oNewsroomMenu = $oNewsroomMenu;
$this->ComputeExpandedState();
$this->ComputeUserData();
$this->ComputeSiloSelection();
}
/**
* @return string
*/
public function GetAppRevisionNumber()
{
return $this->sAppRevisionNumber;
}
/**
* @return string
*/
public function GetAppSquareIconUrl()
{
return $this->sAppSquareIconUrl;
}
/**
* @return string
*/
public function GetAppFullIconUrl()
{
return $this->sAppFullIconUrl;
}
/**
* @return string
*/
public function GetAppIconLink()
{
return $this->sAppIconLink;
}
/**
* @return array
*/
public function GetSiloSelection()
{
return $this->aSiloSelection;
}
/**
* @return array
*/
public function GetMenuGroups()
{
return $this->aMenuGroups;
}
/**
* @return array
*/
public function GetUserData()
{
return $this->aUserData;
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu
*/
public function GetUserMenu()
{
return $this->oUserMenu;
}
/**
* @return \Combodo\iTop\Application\UI\Base\Component\PopoverMenu\NewsroomMenu\NewsroomMenu
*/
public function GetNewsroomMenu()
{
return $this->oNewsroomMenu;
}
/**
* Return true if the menu is expanded
*
* @return bool
*/
public function IsExpanded()
{
return $this->bIsExpanded;
}
/**
* @inheritDoc
*/
public function GetSubBlocks()
{
return [$this->oUserMenu->GetId() => $this->oUserMenu, $this->GetNewsroomMenu()->GetId() => $this->GetNewsroomMenu()];
}
/**
* @return boolean
*/
public function IsNewsroomEnabled()
{
return MetaModel::GetConfig()->Get('newsroom_enabled');
}
/**
* @return void
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ComputeSiloSelection()
{
//TODO 3.0 Use components if we have the time to build select/autocomplete components before release
// List of visible Organizations
$iCount = 0;
$oSet = null;
if (MetaModel::IsValidClass('Organization'))
{
// Display the list of *favorite* organizations... but keeping in mind what is the real number of organizations
$aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null);
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count(); // total number of existing Orgs
// Now get the list of Orgs to be displayed in the menu
$oSearchFilter = DBObjectSearch::FromOQL(ApplicationMenu::GetFavoriteSiloQuery());
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
if (!empty($aFavoriteOrgs))
{
$oSearchFilter->AddCondition('id', $aFavoriteOrgs, 'IN');
}
$oSet = new CMDBObjectSet($oSearchFilter); // List of favorite orgs
}
switch ($iCount)
{
case 0:
case 1:
// No such dimension/silo or only one possible choice => nothing to select
break;
default:
$oAppContext = new ApplicationContext();
$iCurrentOrganization = $oAppContext->GetCurrentValue('org_id');
$this->aSiloSelection['html'] = '<form data-role="ibo-navigation-menu--silo-selection--form" action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">'; //<select class="org_combo" name="c[org_id]" title="Pick an organization" onChange="this.form.submit();">';
$oPage = new \CaptureWebPage();
$oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */);
$iMaxComboLength = MetaModel::GetConfig()->Get('max_combo_length');
$this->aSiloSelection['html'] .= $oWidget->DisplaySelect($oPage, $iMaxComboLength, false, '', $oSet, $iCurrentOrganization, false, 'c[org_id]', '',
array(
'iFieldSize' => 20,
'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
'sDefaultValue' => Dict::S('UI:AllOrganizations'),
));
$this->aSiloSelection['html'] .= $oPage->GetHtml();
// Add other dimensions/context information to this form
$oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here !
$oAppContext->Reset('menu'); // don't pass the menu, since a menu may expect more parameters
$this->aSiloSelection['html'] .= $oAppContext->GetForForm(); // Pass what remains, if anything...
$this->aSiloSelection['html'] .= '</form>';
$sPageJS = $oPage->GetJS();
$sPageReadyJS = $oPage->GetReadyJS();
$this->aSiloSelection['js'] =
<<<JS
$sPageJS
$sPageReadyJS
$('[data-role="ibo-navigation-menu--silo-selection--form"] #org_id').bind('extkeychange', function() { $('[data-role="ibo-navigation-menu--silo-selection--form"]').submit(); } )
$('[data-role="ibo-navigation-menu--silo-selection--form"] #label_org_id').click( function() { if ($('[data-role="ibo-navigation-menu--silo-selection--form"] #org_id').val() == '') { $(this).val(''); } } );
JS;
}
}
/**
* Compute if the menu is expanded or collapsed
*
* @return $this
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected function ComputeExpandedState()
{
$bIsExpanded = false;
// Check if menu should be opened only if we re not in demo mode
if (false === MetaModel::GetConfig()->Get('demo_mode'))
{
if (utils::ReadParam('force_menu_pane', null) === 0)
{
$bIsExpanded = false;
}
elseif (appUserPreferences::GetPref('menu_pane', 'closed') === 'opened')
{
$bIsExpanded = true;
}
}
$this->bIsExpanded = $bIsExpanded;
return $this;
}
/**
* Compute the user data displayed in the menu (organization, name, picture, ...)
*
* @return $this
* @throws \Exception
*/
protected function ComputeUserData()
{
// Use a picture set in the preferences is there is none in the user's contact
$sPictureUrl = UserRights::GetContactPictureAbsUrl('', false);
if(empty($sPictureUrl))
{
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().'images/user-pictures/' . appUserPreferences::GetPref('user_picture_placeholder', 'user-profile-default-256px.png');
}
// TODO 3.0.0 : what do we show if no contact is linked to the user ?
$aData = [
'sOrganization' => UserRights::GetContactOrganizationFriendlyname(),
'sFirstname' => UserRights::GetContactFirstname(),
'sPictureUrl' => $sPictureUrl,
'sWelcomeMessage' => Dict::Format('UI:Layout:NavigationMenu:UserInfo:WelcomeMessage:Text', UserRights::GetContactFirstname())
];
// Logon message
$sLogonMessageDictCode = (UserRights::IsAdministrator()) ? 'UI:LoggedAsMessage+Admin' : 'UI:LoggedAsMessage';
$aData['sLogonMessage'] = Dict::Format($sLogonMessageDictCode, UserRights::GetContactFriendlyname());
$this->aUserData = $aData;
return $this;
}
}

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\Base\Layout\NavigationMenu;
use ApplicationContext;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\NewsroomMenu\NewsroomMenuFactory;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuFactory;
use MetaModel;
/**
* Class NavigationMenuFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\NavigationMenu
* @internal
* @since 3.0.0
*/
class NavigationMenuFactory
{
/**
* Make a standard NavigationMenu layout for backoffice pages
*
* @return \Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenu
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MySQLException
*/
public static function MakeStandard()
{
$oNewsroomMenu = null;
if (MetaModel::GetConfig()->Get('newsroom_enabled'))
{
$oNewsroomMenu = NewsroomMenuFactory::MakeNewsroomMenuForNavigationMenu();
}
return new NavigationMenu(
new ApplicationContext(), PopoverMenuFactory::MakeUserMenuForNavigationMenu(), $oNewsroomMenu, NavigationMenu::BLOCK_CODE
);
}
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\Object;
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
use Combodo\iTop\Application\UI\Helper\UIHelper;
use DBObject;
use MetaModel;
class ObjectDetails extends Panel
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-object-details';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/object/object-details/layout';
/** @var string */
protected $sClassName;
/** @var string */
protected $sClassLabel;
/** @var string */
protected $sName;
/** @var string */
protected $sIconUrl;
/** @var string */
protected $sStatusCode;
/** @var string */
protected $sStatusLabel;
/** @var string */
protected $sStatusColor;
/**
* ObjectDetails constructor.
*
* @param \DBObject $oObject
* @param string|null $sId
*
* @throws \CoreException
*/
public function __construct(DBObject $oObject, ?string $sId = null) {
$this->sClassName = get_class($oObject);
$this->sClassLabel = MetaModel::GetName($this->GetClassName());
// Note: We get the raw name as only the front-end consumer knows when and how to encode it.
$this->sName = $oObject->GetRawName();
$this->sIconUrl = $oObject->GetIcon(false);
if(MetaModel::HasStateAttributeCode($this->sClassName)) {
$this->sStatusCode = $oObject->GetState();
$this->sStatusLabel = $oObject->GetStateLabel();
$this->sStatusColor = UIHelper::GetColorFromStatus($this->sClassName, $this->sStatusCode);
}
parent::__construct('', [], static::DEFAULT_COLOR, $sId);
}
/**
* @return string
*/
public function GetClassName(): string
{
return $this->sClassName;
}
/**
* @return string
*/
public function GetObjectName(): string
{
return $this->sName;
}
public function SetStatus($sCode, $sLabel, $sColor)
{
$this->sStatusCode = $sColor;
$this->sStatusLabel = $sLabel;
$this->sStatusColor = $sColor;
return $this;
}
public function GetStatusCode(): string
{
return $this->sStatusCode;
}
public function GetStatusLabel(): string
{
return $this->sStatusLabel;
}
public function GetStatusColor(): string
{
return $this->sStatusColor;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout\Object;
use DBObject;
/**
* Class ObjectFactory
*
* @internal
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\Object
* @since 3.0.0
*/
class ObjectFactory {
/**
* Make a standard object details layout.
*
* @return \Combodo\iTop\Application\UI\Base\Layout\Object\ObjectDetails
*/
public static function MakeDetails(DBObject $oObject) {
return new ObjectDetails($oObject);
}
}

View File

@@ -0,0 +1,323 @@
<?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\PageContent;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock;
use Combodo\iTop\Application\UI\Base\tUIContentAreas;
use Combodo\iTop\Application\UI\Base\UIBlock;
/**
* Class PageContent
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\PageContent
* @internal
* @since 3.0.0
*/
class PageContent extends UIBlock implements iUIContentBlock {
use tUIContentAreas;
// Overloaded constants
public const BLOCK_CODE = 'ibo-page-content';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/page-content/layout';
/** @var string ENUM_CONTENT_AREA_MAIN The main content area */
public const ENUM_CONTENT_AREA_MAIN = 'main';
/** @var string $sExtraHtmlContent HTML content that do not come from blocks and will be output as-is by the component */
protected $sExtraHtmlContent;
/**
* PageContent constructor.
*
* @param string|null $sId
*/
public function __construct(?string $sId = null) {
parent::__construct($sId);
$this->SetMainBlocks([]);
}
//----------------------
// Specific content area
//----------------------
/**
* Set all main blocks at once.
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock[] $aBlocks
*
* @return $this
*/
public function SetMainBlocks(array $aBlocks) {
$this->SetContentAreaBlocks(static::ENUM_CONTENT_AREA_MAIN, $aBlocks);
return $this;
}
/**
* Return all the main blocks
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock[]
* @throws \Exception
*/
public function GetMainBlocks() {
return $this->GetContentAreaBlocks(static::ENUM_CONTENT_AREA_MAIN);
}
/**
* Add the $oBlock to the main blocks.
* Note that if a block with the same ID already exists, it will be replaced.
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oBlock
*
* @return $this
*/
public function AddMainBlock(iUIBlock $oBlock) {
$this->AddBlockToContentArea(static::ENUM_CONTENT_AREA_MAIN, $oBlock);
return $this;
}
/**
* Remove the main block identified by $sBlockId.
* Note that if no block with that ID exists, it will proceed silently.
*
* @param string $sBlockId
*
* @return $this
*/
public function RemoveMainBlock(string $sBlockId) {
$this->RemoveBlockFromContentArea(static::ENUM_CONTENT_AREA_MAIN, $sBlockId);
return $this;
}
/**
* Set the extra HTML content
*
* @param string $sExtraHtmlContent
*
* @return $this
*/
public function SetExtraHtmlContent(string $sExtraHtmlContent) {
$this->sExtraHtmlContent = $sExtraHtmlContent;
return $this;
}
/**
* Get the extra HTML content as-is, no processing is done on it
*
* @return string
*/
public function GetExtraHtmlContent() {
return $this->sExtraHtmlContent;
}
//-------------------------------
// iUIContentBlock implementation
//-------------------------------
/**
* @inheritDoc
*/
public function AddHtml(string $sHtml) {
$oBlock = new Html($sHtml);
$this->AddMainBlock($oBlock);
return $this;
}
/**
* Add the $oSubBlock directly in the main area
*
* @inheritDoc
*/
public function AddSubBlock(iUIBlock $oSubBlock) {
$this->AddMainBlock($oSubBlock);
return $this;
}
/**
* Remove a specified subBlock from all the areas
*
* @param string $sId
*
* @return $this
*/
public function RemoveSubBlock(string $sId) {
foreach ($this->GetContentAreas() as $oContentArea) {
$oContentArea->RemoveSubBlock($sId);
}
return $this;
}
/**
* Check if the specified subBlock is within one of all the areas
*
* @param string $sId
*
* @return bool
*/
public function HasSubBlock(string $sId): bool {
foreach ($this->GetContentAreas() as $oContentArea) {
if ($oContentArea->HasSubBlock($sId)) {
return true;
}
}
return false;
}
/**
* Get a specific subBlock within all the areas
*
* @inheritDoc
*/
public function GetSubBlock(string $sId): ?iUIBlock {
foreach ($this->GetContentAreas() as $oContentArea) {
$oSubBlock = $oContentArea->GetSubBlock($sId);
if (!is_null($oSubBlock)) {
return $oSubBlock;
}
}
return null;
}
/**
* Set the MAIN AREA subBlocks
*
* @inheritDoc
* @return $this|\Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock
*/
public function SetSubBlocks(array $aSubBlocks): iUIContentBlock {
$this->SetMainBlocks($aSubBlocks);
return $this;
}
/**
* Get ALL the blocks in all the areas
*
* @inheritDoc
*/
public function GetSubBlocks(): array
{
$aSubBlocks = [];
foreach ($this->GetContentAreas() as $oContentArea) {
$aSubBlocks = array_merge($aSubBlocks, $oContentArea->GetSubBlocks());
}
return $aSubBlocks;
}
public function GetDeferredBlocks(): array
{
$aSubBlocks = [];
foreach ($this->GetContentAreas() as $oContentArea) {
$aSubBlocks = array_merge($aSubBlocks, $oContentArea->GetDeferredBlocks());
}
return $aSubBlocks;
}
/**
* Add the $oDeferredBlock directly in the main area
*
* @inheritDoc
*/
public function AddDeferredBlock(iUIBlock $oDeferredBlock)
{
$this->AddDeferredBlockToContentArea(static::ENUM_CONTENT_AREA_MAIN, $oDeferredBlock);
return $this;
}
/**
* Remove a specified subBlock from all the areas
*
* @param string $sId
*
* @return $this
*/
public function RemoveDeferredBlock(string $sId)
{
foreach ($this->GetContentAreas() as $oContentArea) {
$oContentArea->RemoveDeferredBlock($sId);
}
return $this;
}
/**
* Check if the specified subBlock is within one of all the areas
*
* @param string $sId
*
* @return bool
*/
public function HasDeferredBlock(string $sId): bool
{
foreach ($this->GetContentAreas() as $oContentArea) {
if ($oContentArea->HasDeferredBlock($sId)) {
return true;
}
}
return false;
}
/**
* Get a specific subBlock within all the areas
*
* @inheritDoc
*/
public function GetDeferredBlock(string $sId): ?iUIBlock
{
foreach ($this->GetContentAreas() as $oContentArea) {
$oDeferredBlock = $oContentArea->GetDeferredBlock($sId);
if (!is_null($oDeferredBlock)) {
return $oDeferredBlock;
}
}
return null;
}
/**
* Set the MAIN AREA subBlocks
*
* @inheritDoc
* @return $this|\Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock
*/
public function SetDeferredBlocks(array $aDeferredBlocks): iUIContentBlock
{
$this->SetContentAreaDeferredBlocks(self::ENUM_CONTENT_AREA_MAIN, $aDeferredBlocks);
return $this;
}
}

View File

@@ -0,0 +1,71 @@
<?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\PageContent;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityPanelFactory;
use DBObject;
/**
* Class PageContentFactory
*
* @internal
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\PageContent
* @since 3.0.0
*/
class PageContentFactory
{
/**
* Make a standard empty PageContent layout for backoffice pages.
*
* @return \Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContent
*/
public static function MakeStandardEmpty()
{
return new PageContent();
}
/**
* Make a standard object details page with the form in the middle and the logs / activity in the side panel
*
* @param \DBObject $oObject
* @param string $sMode Mode the object is being displayed (view, edit, create, ...), default is view.
*
* @see cmdbAbstractObject::ENUM_OBJECT_MODE_XXX
*
* @return \Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentWithSideContent
* @throws \CoreException
*/
public static function MakeForObjectDetails(DBObject $oObject, string $sMode = cmdbAbstractObject::DEFAULT_OBJECT_MODE)
{
$oLayout = new PageContentWithSideContent();
// Add object details layout
// TODO 3.0.0
// Add object activity layout
$oActivityPanel = ActivityPanelFactory::MakeForObjectDetails($oObject, $sMode);
$oLayout->AddSideBlock($oActivityPanel);
return $oLayout;
}
}

View File

@@ -0,0 +1,102 @@
<?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\PageContent;
use Combodo\iTop\Application\UI\Base\iUIBlock;
/**
* Class PageContentWithSideContent
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\PageContent
* @internal
* @since 3.0.0
*/
class PageContentWithSideContent extends PageContent {
// Overloaded constants
public const BLOCK_CODE = 'ibo-page-content-with-side-content';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/page-content/with-side-content';
// Specific constants
public const ENUM_CONTENT_AREA_SIDE = 'side';
/**
* PageContentWithSideContent constructor.
*
* @param string|null $sId
*/
public function __construct(?string $sId = null) {
parent::__construct($sId);
$this->SetSideBlocks([]);
}
/**
* Set all side blocks at once.
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock[] $aBlocks
*
* @return $this
*/
public function SetSideBlocks(array $aBlocks) {
$this->SetContentAreaBlocks(static::ENUM_CONTENT_AREA_SIDE, $aBlocks);
return $this;
}
/**
* Return all the side blocks
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock[]
* @throws \Exception
*/
public function GetSideBlocks() {
return $this->GetContentAreaBlocks(static::ENUM_CONTENT_AREA_SIDE);
}
/**
* Add the $oBlock to the side blocks.
* Note that if a block with the same ID already exists, it will be replaced.
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oBlock
*
* @return $this
*/
public function AddSideBlock(iUIBlock $oBlock) {
$this->AddBlockToContentArea(static::ENUM_CONTENT_AREA_SIDE, $oBlock);
return $this;
}
/**
* Remove the side block identified by $sBlockId.
* Note that if no block with that ID exists, it will proceed silently.
*
* @param string $sBlockId
*
* @return $this
*/
public function RemoveSideBlock(string $sBlockId) {
$this->RemoveBlockFromContentArea(static::ENUM_CONTENT_AREA_SIDE, $sBlockId);
return $this;
}
}

View File

@@ -0,0 +1,127 @@
<?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\TabContainer\Tab;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\UIException;
use Dict;
use TabManager;
/**
* Class AjaxTab
*
* @package Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab
* @internal
* @since 3.0.0
*/
class AjaxTab extends Tab {
// Overloaded constants
public const BLOCK_CODE = 'ibo-ajax-tab';
public const TAB_TYPE = TabManager::ENUM_TAB_TYPE_AJAX;
/** @var string The target URL to be loaded asynchronously */
private $sUrl;
/** @var bool Whether the tab should should be cached by the browser or always refreshed */
private $bCache;
/**
* @param string $sUrl
*
* @return $this
*/
public function SetUrl(string $sUrl) {
$this->sUrl = $sUrl;
return $this;
}
/**
* @return string
*/
public function GetUrl(): string {
return $this->sUrl;
}
/**
* Set whether the tab should should be cached by the browser or always refreshed
*
* @param bool $bCache
*
* @return $this
*/
public function SetCache(bool $bCache) {
$this->bCache = $bCache;
return $this;
}
/**
* Return whether the tab should should be cached by the browser or always refreshed
*
* @return string
*/
public function GetCache(): string {
return $this->bCache ? 'true' : 'false';
}
//-------------------------------
// iUIBlock implementation
//-------------------------------
/**
* @inheritDoc
*/
public function GetParameters(): array {
$aParams = parent::GetParameters();
$aParams['sURL'] = $this->GetUrl();
$aParams['sCache'] = $this->GetCache() ? 'true' : 'false';
return $aParams;
}
//-------------------------------
// iUIContentBlock implementation
//-------------------------------
/**
* @inheritDoc
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function AddHtml(string $sHtml) {
throw new UIException($this, Dict::Format('UIBlock:Error:AddBlockForbidden', $this->GetId()));
}
/**
* @inheritDoc
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function AddSubBlock(iUIBlock $oSubBlock) {
throw new UIException($this, Dict::Format('UIBlock:Error:AddBlockForbidden', $this->GetId()));
}
/**
* @inheritDoc
*/
public function GetSubBlocks(): array {
return [];
}
}

View File

@@ -0,0 +1,89 @@
<?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\TabContainer\Tab;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use TabManager;
/**
* Class Tab
*
* @package Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab
* @internal
* @since 3.0.0
*/
class Tab extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-tab';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/tab-container/tab/layout';
/** @var string */
public const TAB_TYPE = TabManager::ENUM_TAB_TYPE_HTML;
/** @var string */
protected $sTitle;
/**
* Tab constructor.
*
* @param string $sTabCode
* @param string $sTitle
*/
public function __construct(string $sTabCode, string $sTitle)
{
parent::__construct($sTabCode);
$this->sTitle = $sTitle;
}
/**
* @return string
*/
public function GetType(): string
{
return static::TAB_TYPE;
}
/**
* @return string
*/
public function GetTitle(): string
{
return $this->sTitle;
}
//-------------------------------
// iUIBlock implementation
//-------------------------------
/**
* @inheritDoc
*/
public function GetParameters(): array
{
return [
'sBlockId' => $this->GetId(),
'sTitle' => $this->GetTitle(),
'sType' => $this->GetType(),
'oBlock' => $this,
];
}
}

View File

@@ -0,0 +1,186 @@
<?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\TabContainer;
use appUserPreferences;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock;
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\AjaxTab;
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\UIException;
use Dict;
/**
* Class TabContainer
*
* @package Combodo\iTop\Application\UI\Base\Layout\TabContainer
*/
class TabContainer extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-tab-container';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/tab-container/layout';
public const JS_TEMPLATE_REL_PATH = 'base/layouts/tab-container/layout';
public const JS_FILES_REL_PATH = [
'js/layouts/tab-container.js'
];
// Specific constants
/** @var string */
public const ENUM_LAYOUT_HORIZONTAL = 'horizontal';
/** @var string */
public const ENUM_LAYOUT_VERTICAL = 'vertical';
/** @var string */
public const DEFAULT_LAYOUT = self::ENUM_LAYOUT_HORIZONTAL;
/** @var string $sName */
private $sName;
/** @var string $sPrefix */
private $sPrefix;
/** @var string $sLayout Layout of the tabs (horizontal, vertical, ...), see static::ENUM_LAYOUT_XXX */
private $sLayout;
/**
* TabContainer constructor.
*
* @param string $sName
* @param string $sPrefix
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function __construct($sName, $sPrefix)
{
$sId = null;
if (!empty($sName) || !empty($sPrefix)) {
$sId = "{$sName}".((!empty($sPrefix)) ? "-{$sPrefix}" : "");
}
parent::__construct($sId);
$this->sName = $sName;
$this->sPrefix = $sPrefix;
$this->sLayout = appUserPreferences::GetPref('tab_layout', static::DEFAULT_LAYOUT);
}
/**
* @param string $sTabCode
*
* @return bool
*/
public function TabExists(string $sTabCode): bool
{
return $this->HasSubBlock($sTabCode);
}
public function GetTab($sTabCode): ?Tab
{
/** @var \Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab $oTab */
$oTab = $this->GetSubBlock($sTabCode);
return $oTab;
}
/**
* @param string $sTabCode
* @param string $sTitle
*
* @return \Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function AddAjaxTab(string $sTabCode, string $sTitle): Tab
{
$oTab = new AjaxTab($sTabCode, $sTitle);
$this->AddSubBlock($oTab);
return $oTab;
}
/**
* @param string $sTabCode
* @param string $sTitle
*
* @return \Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function AddTab(string $sTabCode, string $sTitle): Tab
{
$oTab = new Tab($sTabCode, $sTitle);
$this->AddSubBlock($oTab);
return $oTab;
}
public function RemoveTab(string $sTabCode): self
{
$this->RemoveSubBlock($sTabCode);
return $this;
}
/**
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oSubBlock
*
* @return iUIContentBlock
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function AddSubBlock(iUIBlock $oSubBlock): iUIContentBlock
{
if (!($oSubBlock instanceof Tab)) {
throw new UIException($this, Dict::Format('UIBlock:Error:AddBlockNotTabForbidden', $oSubBlock->GetId(), $this->GetId()));
}
return parent::AddSubBlock($oSubBlock);
}
/**
* Return tab list
*
* @return array
*/
public function Get(): array
{
$aTabs = [];
foreach ($this->GetSubBlocks() as $oTab) {
$aTabs[] = $oTab->GetParameters();
}
return [
'sBlockId' => $this->GetId(),
'aTabs' => $aTabs
];
}
/**
* @param string $sLayout
*
* @return $this
*/
public function SetLayout($sLayout) {
$this->sLayout = $sLayout;
return $this;
}
/**
* @return string
*/
public function GetLayout(): string {
return $this->sLayout;
}
}

View File

@@ -0,0 +1,216 @@
<?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\TopBar;
use Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs;
use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearch;
use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreate;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\Toolbar;
use Combodo\iTop\Application\UI\Base\UIBlock;
/**
* Class TopBar
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\TopBar
* @internal
* @since 3.0.0
*/
class TopBar extends UIBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-top-bar';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/top-bar/layout';
/** @var QuickCreate|null $oQuickCreate */
protected $oQuickCreate;
/** @var GlobalSearch|null $oGlobalSearch */
protected $oGlobalSearch;
/** @var Breadcrumbs|null $oBreadcrumbs */
protected $oBreadcrumbs;
/** @var Toolbar|null */
protected $oToolbar;
/**
* TopBar constructor.
*
* @param string|null $sId
* @param \Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreate|null $oQuickCreate
* @param \Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearch|null $oGlobalSearch
* @param \Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs|null $oBreadcrumbs
*/
public function __construct(
$sId = null, QuickCreate $oQuickCreate = null, GlobalSearch $oGlobalSearch = null, Breadcrumbs $oBreadcrumbs = null
) {
parent::__construct($sId);
$this->oQuickCreate = $oQuickCreate;
$this->oGlobalSearch = $oGlobalSearch;
$this->oBreadcrumbs = $oBreadcrumbs;
}
/**
* Set the quick create component
*
* @param \Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreate $oQuickCreate
*
* @return $this
*/
public function SetQuickCreate(QuickCreate $oQuickCreate)
{
$this->oQuickCreate = $oQuickCreate;
return $this;
}
/**
* Return the global search component
*
* @return \Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreate|null
*/
public function GetQuickCreate()
{
return $this->oQuickCreate;
}
/**
* Return true if the quick create has been set
*
* @return bool
*/
public function HasQuickCreate()
{
return ($this->oQuickCreate !== null);
}
/**
* Set the global search component
*
* @param \Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearch $oGlobalSearch
*
* @return $this
*/
public function SetGlobalSearch(GlobalSearch $oGlobalSearch)
{
$this->oGlobalSearch = $oGlobalSearch;
return $this;
}
/**
* Return the global search component
*
* @return \Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearch|null
*/
public function GetGlobalSearch()
{
return $this->oGlobalSearch;
}
/**
* Return true if the global search has been set
*
* @return bool
*/
public function HasGlobalSearch()
{
return ($this->oGlobalSearch !== null);
}
/**
* Set the breadcrumbs component
*
* @param \Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs $oBreadcrumbs
*
* @return $this
*/
public function SetBreadcrumbs(Breadcrumbs $oBreadcrumbs)
{
$this->oBreadcrumbs = $oBreadcrumbs;
return $this;
}
/**
* Return the breadcrumbs component
*
* @return \Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs|null
*/
public function GetBreadcrumbs()
{
return $this->oBreadcrumbs;
}
/**
* Return true if the breadcrumb has been set
*
* @return bool
*/
public function HasBreadcrumbs()
{
return ($this->oBreadcrumbs !== null);
}
/**
* @return Toolbar|null
*/
public function GetToolbar(): ?Toolbar
{
return $this->oToolbar;
}
/**
* @param Toolbar|null $oToolbar
*
* @return TopBar
*/
public function SetToolbar(?Toolbar $oToolbar): TopBar
{
$this->oToolbar = $oToolbar;
return $this;
}
/**
* Return true if the breadcrumb has been set
*
* @return bool
*/
public function HasToolbar()
{
return ($this->oToolbar !== null);
}
/**
* @inheritDoc
*/
public function GetSubBlocks()
{
$aSubBlocks = [];
$aSubBlocksNames = ['QuickCreate', 'GlobalSearch', 'Breadcrumbs', 'Toolbar'];
foreach ($aSubBlocksNames as $sSubBlockName) {
$sHasMethodName = 'Has'.$sSubBlockName;
if (true === call_user_func_array([$this, $sHasMethodName], [])) {
$sPropertyName = 'o'.$sSubBlockName;
$aSubBlocks[$this->$sPropertyName->GetId()] = $this->$sPropertyName;
}
}
return $aSubBlocks;
}
}

View File

@@ -0,0 +1,71 @@
<?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\TopBar;
use Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs;
use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchFactory;
use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreateFactory;
use utils;
/**
* Class TopBarFactory
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Application\UI\Base\Layout\TopBar
* @internal
* @since 3.0.0
*/
class TopBarFactory
{
/**
* Make a standard TopBar layout for backoffice pages
*
* @param array|null $aBreadcrumbsEntry Current breadcrumbs entry to add
*
* @return \Combodo\iTop\Application\UI\Base\Layout\TopBar\TopBar
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public static function MakeStandard(?array $aBreadcrumbsEntry = null)
{
$oTopBar = new TopBar(TopBar::BLOCK_CODE);
if (utils::GetConfig()->Get('quick_create.enabled') === true)
{
$oTopBar->SetQuickCreate(QuickCreateFactory::MakeFromUserHistory());
}
if (utils::GetConfig()->Get('global_search.enabled') === true)
{
$oTopBar->SetGlobalSearch(GlobalSearchFactory::MakeFromUserHistory());
}
if(utils::GetConfig()->Get('breadcrumb.enabled') === true)
{
$oBreadcrumbs = new Breadcrumbs($aBreadcrumbsEntry, Breadcrumbs::BLOCK_CODE);
$oTopBar->SetBreadcrumbs($oBreadcrumbs);
}
return $oTopBar;
}
}

View File

@@ -0,0 +1,252 @@
<?php
/*
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\UIBlock;
/**
* Class UIContentBlock
* Base block containing sub-blocks
*
* @package Combodo\iTop\Application\UI\Base\Layout
* @author Eric Espie <eric.espie@combodo.com>
* @author Anne-Catherine Cognet <anne-catherine.cognet@combodo.com>
* @internal
* @since 3.0.0
*/
class UIContentBlock extends UIBlock implements iUIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-content-block';
public const HTML_TEMPLATE_REL_PATH = 'base/layouts/content-block/layout';
/** @var array */
protected $aCSSClasses;
/** @var array Array <KEY> => <VALUE> which will be output as HTML data-xxx attributes (eg. data-<KEY>="<VALUE>") */
protected $aDataAttributes;
/** @var array */
protected $aSubBlocks;
/** @var array */
protected $aDeferredBlocks;
/**
* UIContentBlock constructor.
*
* @param string|null $sName
* @param string $sContainerClass
*/
public function __construct(string $sName = null, string $sContainerClass = '')
{
parent::__construct($sName);
$this->aSubBlocks = [];
$this->aDeferredBlocks = [];
$this->aDataAttributes = [];
$this->SetCSSClasses($sContainerClass);
}
/**
* @inheritDoc
*/
public function AddHtml(string $sHtml)
{
$oBlock = new Html($sHtml);
$this->AddSubBlock($oBlock);
return $this;
}
/**
* @inheritDoc
*/
public function AddSubBlock(iUIBlock $oSubBlock)
{
$this->aSubBlocks[$oSubBlock->GetId()] = $oSubBlock;
return $this;
}
/**
* @inheritDoc
*/
public function RemoveSubBlock(string $sId)
{
if ($this->HasSubBlock($sId)) {
unset($this->aSubBlocks[$sId]);
}
return $this;
}
/**
* @inheritDoc
*/
public function HasSubBlock(string $sId): bool
{
return array_key_exists($sId, $this->aSubBlocks);
}
/**
* @inheritDoc
*/
public function GetSubBlock(string $sId): ?iUIBlock
{
return isset($this->aSubBlocks[$sId]) ? $this->aSubBlocks[$sId] : null;
}
/**
* @inheritDoc
*/
public function GetSubBlocks(): array
{
return $this->aSubBlocks;
}
/**
* @inheritDoc
*/
public function SetSubBlocks(array $aSubBlocks)
{
foreach ($aSubBlocks as $oSubBlock) {
$this->AddSubBlock($oSubBlock);
}
return $this;
}
/**
* @return string
*/
public function GetCSSClasses(): string
{
return implode(' ', $this->aCSSClasses);
}
/**
* @param string $sCSSClasses
*
* @return UIContentBlock
*/
public function SetCSSClasses(string $sCSSClasses)
{
$this->aCSSClasses = [];
$this->AddCSSClasses($sCSSClasses);
return $this;
}
/**
* @param string $sCSSClasses
*
* @return $this
*/
public function AddCSSClasses(string $sCSSClasses)
{
foreach (explode(' ', $sCSSClasses) as $sCSSClass) {
if (!empty($sCSSClass)) {
$this->aCSSClasses[$sCSSClass] = $sCSSClass;
}
}
return $this;
}
/**
* @return array
*/
public function GetDataAttributes(): array
{
return $this->aDataAttributes;
}
/**
* @param array $aDataAttributes
*
* @return $this
*/
public function SetDataAttributes(array $aDataAttributes)
{
$this->aDataAttributes = $aDataAttributes;
return $this;
}
/**
* @param string $sName
* @param string $sValue
*
* @return $this
*/
public function AddDataAttribute(string $sName, string $sValue)
{
$this->aDataAttributes[$sName] = $sValue;
return $this;
}
/**
* @inheritDoc
*/
public function AddDeferredBlock(iUIBlock $oDeferredBlock)
{
$this->aDeferredBlocks[$oDeferredBlock->GetId()] = $oDeferredBlock;
return $this;
}
/**
* @inheritDoc
*/
public function RemoveDeferredBlock(string $sId)
{
if ($this->HasDeferredBlock($sId)) {
unset($this->aDeferredBlocks[$sId]);
}
return $this;
}
/**
* @inheritDoc
*/
public function HasDeferredBlock(string $sId): bool
{
return array_key_exists($sId, $this->aDeferredBlocks);
}
/**
* @inheritDoc
*/
public function GetDeferredBlock(string $sId): ?iUIBlock
{
return isset($this->aDeferredBlocks[$sId]) ? $this->aDeferredBlocks[$sId] : null;
}
/**
* @inheritDoc
*/
public function GetDeferredBlocks(): array
{
return $this->aDeferredBlocks;
}
/**
* @inheritDoc
*/
public function SetDeferredBlocks(array $aDeferredBlocks)
{
foreach ($aDeferredBlocks as $oDeferredBlock) {
$this->AddDeferredBlock($oDeferredBlock);
}
return $this;
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Layout;
use Combodo\iTop\Application\UI\Base\iUIBlock;
/**
* Interface iUIContentBlock
*
* @package Combodo\iTop\Application\UI\Base\Layout
* @author Eric Espie <eric.espie@combodo.com>
* @author Anne-Catherine Cognet <anne-catherine.cognet@combodo.com>
* @internal
* @since 3.0.0
*/
interface iUIContentBlock {
/**
* Add raw HTML to the block
*
* @param string $sHtml
*
* @return $this
*/
public function AddHtml(string $sHtml);
/**
* Add $oSubBlock, replacing any block with the same ID
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oSubBlock
*
* @return $this
*/
public function AddSubBlock(iUIBlock $oSubBlock);
/**
* Remove the sub block identified by $sId.
* Note that if no sub block matches the ID, it proceeds silently.
*
* @param string $sId ID of the sub block to remove
*
* @return $this
*/
public function RemoveSubBlock(string $sId);
/**
* Return true if there is a sub block identified by $sId
*
* @param string $sId
*
* @return bool
*/
public function HasSubBlock(string $sId): bool;
/**
* Return the sub block identified by $sId or null if not found
*
* @param string $sId
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock|null
*/
public function GetSubBlock(string $sId): ?iUIBlock;
/**
* Return an array of all the sub blocks
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock[]
*/
public function GetSubBlocks(): array;
/**
* Set all sub blocks at once, replacing all existing ones
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock[] $aSubBlocks
*
* @return $this
*/
public function SetSubBlocks(array $aSubBlocks);
/**
* Add $oDeferredBlock, replacing any block with the same ID
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oDeferredBlock
*
* @return $this
*/
public function AddDeferredBlock(iUIBlock $oDeferredBlock);
/**
* Remove the sub block identified by $sId.
* Note that if no sub block matches the ID, it proceeds silently.
*
* @param string $sId ID of the sub block to remove
*
* @return $this
*/
public function RemoveDeferredBlock(string $sId);
/**
* Return true if there is a sub block identified by $sId
*
* @param string $sId
*
* @return bool
*/
public function HasDeferredBlock(string $sId): bool;
/**
* Return the sub block identified by $sId or null if not found
*
* @param string $sId
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock|null
*/
public function GetDeferredBlock(string $sId): ?iUIBlock;
/**
* Return an array of all the sub blocks
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock[]
*/
public function GetDeferredBlocks(): array;
/**
* Set all sub blocks at once, replacing all existing ones
*
* @param \Combodo\iTop\Application\UI\Base\iUIBlock[] $aDeferredBlocks
*
* @return $this
*/
public function SetDeferredBlocks(array $aDeferredBlocks);
}