Merge remote-tracking branch 'origin/support/3.2' into develop

# Conflicts:
#	sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php
This commit is contained in:
Eric Espie
2025-01-13 09:18:00 +01:00
65 changed files with 1746 additions and 552 deletions

View File

@@ -89,7 +89,7 @@ abstract class AsyncTask extends DBObject
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
@@ -489,7 +489,7 @@ class AsyncSendNewsroom extends AsyncTask {
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("object_class", array("allowed_values"=>null, "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("url", array("allowed_values"=>null, "sql"=>"url", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>'NOW()', "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
}

View File

@@ -6371,13 +6371,6 @@ class AttributeDateTime extends AttributeDBField
$oFormField = parent::MakeFormField($oObject, $oFormField);
// After call to the parent as it sets the current value
$oValue = $oObject->Get($this->GetCode());
if ($oValue === $this->GetNullValue()) {
$oValue = $this->GetDefaultValue($oObject);
}
$oFormField->SetCurrentValue($this->GetFormat()->Format($oValue));
return $oFormField;
}
@@ -6463,18 +6456,12 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null)
{
$sDefaultValue = $this->Get('default_value');
if (!$this->IsNullAllowed() && utils::IsNotNullOrEmptyString($sDefaultValue)) {
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
try {
$oDate = new DateTimeImmutable($sDefaultValue);
} catch (Exception $e) {
$oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([]));
}
catch (Exception $e) {
IssueLog::Error($e->getMessage(), null, [
'class' => get_class($this),
'name' => $this->GetCode(),
'stack' => $e->getTraceAsString()]);
return $this->GetNullValue();
}
return $oDate->format($this->GetInternalFormat());
}
return $this->GetNullValue();

View File

@@ -70,7 +70,7 @@ class BulkExportResult extends DBObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -33,7 +33,7 @@ class CMDBChange extends DBObject
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql"=>"origin", "default_value"=>CMDBChangeOrigin::INTERACTIVE, "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -51,7 +51,7 @@ class DBProperty extends DBObject
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
}

View File

@@ -39,7 +39,7 @@ class Event extends DBObject implements iDisplay
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"now", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -575,9 +575,15 @@ class BinaryExpression extends Expression
case 'LIKE':
$sType = 'like';
break;
case 'NOT LIKE':
$sType = 'notlike';
break;
case 'IN':
$sType = 'in';
break;
case 'NOT IN':
$sType = 'notin';
break;
default:
throw new Exception("Operator '$sOperator' not yet supported");
}
@@ -642,11 +648,27 @@ class BinaryExpression extends Expression
case 'like':
$sEscaped = preg_quote($mRight, '/');
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
$result = (int) preg_match("/$sEscaped/i", $mLeft);
$pregRes = preg_match("/$sEscaped/i", $mLeft);
if ($pregRes === false) {
throw new Exception("Error in regular expression '$sEscaped'");
}
$result = ($pregRes === 1);
break;
case 'notlike':
$sEscaped = preg_quote($mRight, '/');
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
$pregRes = preg_match("/$sEscaped/i", $mLeft);
if ($pregRes === false) {
throw new Exception("Error in regular expression '$sEscaped'");
}
$result = ($pregRes !== 1);
break;
case 'in':
$result = in_array($mLeft, $mRight);
break;
case 'notin':
$result = !in_array($mLeft, $mRight);
break;
}
return $result;
}

View File

@@ -42,8 +42,8 @@ class iTopOwnershipToken extends DBObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'NOW()', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'NOW()', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>'obj_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>'obj_key', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("token", array("allowed_values"=>null, "sql"=>'token', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -1,19 +1,13 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# Unique name of your app: used to compute stable namespaces for cache keys.
prefix_seed: combodo/itop
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# filesystem
app: cache.adapter.filesystem
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# Namespaced pools use the above "app" backend by default
pools:
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: null
templates_cache_pool:
adapter: cache.app

View File

@@ -23,9 +23,13 @@ namespace Combodo\iTop\Portal\Brick;
require_once APPROOT.'/core/moduledesign.class.inc.php';
require_once APPROOT.'/setup/compiler.class.inc.php';
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderInterface;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
use ModuleDesign;
use Combodo\iTop\DesignElement;
/**
* Description of AbstractBrick
@@ -36,7 +40,7 @@ use Combodo\iTop\DesignElement;
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.3.0
*/
abstract class AbstractBrick
abstract class AbstractBrick implements TemplatesProviderInterface
{
/** @var string ENUM_DATA_LOADING_LAZY */
const ENUM_DATA_LOADING_LAZY = 'lazy';
@@ -53,7 +57,7 @@ abstract class AbstractBrick
const DEFAULT_VISIBLE = true;
/** @var float DEFAULT_RANK */
const DEFAULT_RANK = 1.0;
/** @var string|null DEFAULT_PAGE_TEMPLATE_PATH */
/** @var string|null DEFAULT_PAGE_TEMPLATE_PATH @deprecated since 3.2.1 */
const DEFAULT_PAGE_TEMPLATE_PATH = null;
/** @var string DEFAULT_TITLE */
const DEFAULT_TITLE = '';
@@ -65,10 +69,8 @@ abstract class AbstractBrick
const DEFAULT_ALLOWED_PROFILES_OQL = '';
/** @var string DEFAULT_DENIED_PROFILES_OQL */
const DEFAULT_DENIED_PROFILES_OQL = '';
/** @var array $DEFAULT_TEMPLATES_PATH */
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
];
/** @var string TEMPLATES_BASE_PATH */
const TEMPLATES_BASE_PATH = 'itop-portal-base/portal/templates/bricks/';
/** @var string $sId */
protected $sId;
@@ -80,7 +82,7 @@ abstract class AbstractBrick
protected $bVisible;
/** @var float $fRank */
protected $fRank;
/** @var string|null $sPageTemplatePath */
/** @var string|null $sPageTemplatePath @deprecated since 3.2.1 */
protected $sPageTemplatePath;
/** @var string $sTitle */
protected $sTitle;
@@ -97,6 +99,37 @@ abstract class AbstractBrick
/** @var string $sDeniedProfilesOql */
protected $sDeniedProfilesOql;
/** @var \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService Templating provider service for registering default templates paths */
private static TemplatesProviderService $oTemplatesProviderService;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'layout.html.twig'),
);
}
/**
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService $oTemplateProviderService
*
* @return void
*/
public static function SetTemplatesProviderService(TemplatesProviderService $oTemplateProviderService): void
{
self::$oTemplatesProviderService = $oTemplateProviderService;
}
/**
* Return the templates provider service.
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService
*/
protected static function GetTemplatesProviderService(): TemplatesProviderService
{
return self::$oTemplatesProviderService;
}
/**
* Returns all enum values for the data loading modes in an array.
*
@@ -116,7 +149,9 @@ abstract class AbstractBrick
$this->bActive = static::DEFAULT_ACTIVE;
$this->bVisible = static::DEFAULT_VISIBLE;
$this->fRank = static::DEFAULT_RANK;
$this->sPageTemplatePath = static::$DEFAULT_TEMPLATES_PATH['page'];
// BEGIN cleaning 3.2.1 deprecated
$this->sPageTemplatePath = static::DEFAULT_PAGE_TEMPLATE_PATH;
// END cleaning 3.2.1 deprecated
$this->sTitle = static::DEFAULT_TITLE;
$this->sDescription = static::DEFAULT_DESCRIPTION;
$this->sDataLoading = static::DEFAULT_DATA_LOADING;
@@ -180,10 +215,12 @@ abstract class AbstractBrick
* Returns the brick page template path
*
* @return string
*
* @deprecated since 3.2.1 use GetTemplatePath('page') instead
*/
public function GetPageTemplatePath()
{
return $this->sPageTemplatePath;
return $this->GetTemplatePath('page');
}
/**
@@ -327,10 +364,13 @@ abstract class AbstractBrick
* @param string $sPageTemplatePath
*
* @return \Combodo\iTop\Portal\Brick\AbstractBrick
*
* @deprecated since 3.2.1 use SetTemplatePath('page') instead
*/
public function SetPageTemplatePath($sPageTemplatePath)
{
$this->sPageTemplatePath = $sPageTemplatePath;
$this->SetTemplatePath( 'page', $sPageTemplatePath);
return $this;
}
@@ -576,20 +616,6 @@ abstract class AbstractBrick
return ($this->sDescription !== null && $this->sDescription !== '');
}
/**
* @param $sTemplateId
* @param $sTemplatePath
*
* @return void
* @since 3.2.1
*/
public static function SetDefaultTemplatePath($sTemplateId, $sTemplatePath)
{
if(array_key_exists($sTemplateId, static::$DEFAULT_TEMPLATES_PATH)) {
static::$DEFAULT_TEMPLATES_PATH[$sTemplateId] = $sTemplatePath;
}
}
/**
* Load the brick's data from the xml passed as a ModuleDesignElement.
* This is used to set all the brick attributes at once.
@@ -634,7 +660,7 @@ abstract class AbstractBrick
{
/** @var \Combodo\iTop\DesignElement $oTemplateNode */
$oTemplateNode = $oTemplateNodeList->item(0);
$this->SetPageTemplatePath($oTemplateNode->GetText(static::DEFAULT_PAGE_TEMPLATE_PATH));
$this->SetTemplatePath('page', $oTemplateNode->GetText(static::DEFAULT_PAGE_TEMPLATE_PATH));
}
break;
case 'title':
@@ -678,32 +704,48 @@ abstract class AbstractBrick
}
/**
* Load brick configuration that is not part of the brick definition but is part of the portal global properties.
* Override the brick default template path.
* Template is managed by the TemplatesProviderService.
*
* @param $aPortalProperties
*
* @return void
* @since 3.2.1
*
* @param string $sTemplateId
* @param string $sTileTemplatePath
*
* @return \Combodo\iTop\Portal\Brick\PortalBrick
*/
public static function LoadClassDefinitionFromPortalProperties($aPortalProperties)
public function SetTemplatePath(string $sTemplateId, string $sTileTemplatePath): AbstractBrick
{
// Check if they are any brick templates
if(!array_key_exists('bricks', $aPortalProperties['templates']) || !is_array($aPortalProperties['templates']['bricks'])) {
return;
}
// Get the bricks templates
$aBricksTemplates = $aPortalProperties['templates']['bricks'];
$sClassFQCN = static::class;
// Get the current brick templates
$aCurrentBricksTemplates = array_key_exists($sClassFQCN, $aBricksTemplates) ? $aBricksTemplates[$sClassFQCN] : [];
foreach($aCurrentBricksTemplates as $sTemplateKey => $sTemplate) {
// Clean the template id
$sTemplateId = str_ireplace($sClassFQCN.':', '', $sTemplateKey);
// Call the set method for the template
static::SetDefaultTemplatePath($sTemplateId, $sTemplate);
}
static::GetTemplatesProviderService()->OverrideInstanceTemplatePath($this, $sTemplateId, $sTileTemplatePath);
return $this;
}
/**
* Returns the brick template path.
* Template is managed by the TemplatesProviderService.
*
* @since 3.2.1
*
* @param string $sTemplateId template identifier
*
* @return string template path
*/
public function GetTemplatePath(string $sTemplateId): string
{
return static::GetTemplatesProviderService()->GetProviderInstanceTemplatePath($this, $sTemplateId);
}
/**
* Returns true if this brick template path is overridden.
*
* @since 3.2.1
*
* @param string $sTemplateId template identifier
*
* @return string|null
*/
public function HasInstanceOverriddenTemplate(string $sTemplateId): ?string
{
return static::GetTemplatesProviderService()->HasInstanceOverriddenTemplate($this, $sTemplateId);
}
}

View File

@@ -21,6 +21,8 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use Dict;
use DOMFormatException;
@@ -38,11 +40,10 @@ class AggregatePageBrick extends PortalBrick
// Overloaded constants
const DEFAULT_DECORATION_CLASS_HOME = 'fas fa-tachometer-alt';
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fas fa-tachometer-alt fa-2x';
/** @var string @deprecated since 3.2.1 */
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig';
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
];
// Overloaded variables
public static $sRouteName = 'p_aggregatepage_brick';
@@ -51,6 +52,15 @@ class AggregatePageBrick extends PortalBrick
*/
private $aAggregatePageBricks = array();
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'aggregate-page/layout.html.twig')
);
}
/**
* AggregatePageBrick constructor.
*/

View File

@@ -19,9 +19,9 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use DOMFormatException;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
use UserRights;
use ModuleDesign;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
@@ -48,21 +48,20 @@ class BrickCollection
private $aHomeOrdering;
/** @var array $aNavigationMenuOrdering */
private $aNavigationMenuOrdering;
/** @var \array $aCombodoPortalInstanceConf
* @since 3.2.1
*/
private $aCombodoPortalInstanceConf;
/**
* BrickCollection constructor.
*
* @param \ModuleDesign $oModuleDesign
* @param $aCombodoPortalInstanceConf
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService $oTemplatesProviderService
*
* @throws \Exception
* @since 3.2.1 Added $aCombodoPortalInstanceConf parameter
*
* @since 3.2.1 Added $oTemplatesProviderService parameter
* Important: The service is not directly used, but the injection ensure that the service is initialized.
* Bricks may need to use the service to get the templates.
*/
public function __construct(ModuleDesign $oModuleDesign, array $aCombodoPortalInstanceConf)
public function __construct(ModuleDesign $oModuleDesign, TemplatesProviderService $oTemplatesProviderService)
{
$this->oModuleDesign = $oModuleDesign;
$this->aAllowedBricks = null;
@@ -70,7 +69,6 @@ class BrickCollection
$this->iDisplayedInNavigationMenu = 0;
$this->aHomeOrdering = array();
$this->aNavigationMenuOrdering = array();
$this->aCombodoPortalInstanceConf = $aCombodoPortalInstanceConf;
$this->Load();
}
@@ -202,10 +200,6 @@ class BrickCollection
{
if (class_exists($sBrickClass))
{
// Load the portal properties that are common to all bricks of this type
$sBrickClass::LoadClassDefinitionFromPortalProperties($this->aCombodoPortalInstanceConf['properties']);
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */
$oBrick = new $sBrickClass();

View File

@@ -20,8 +20,10 @@
namespace Combodo\iTop\Portal\Brick;
use DOMFormatException;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
/**
* Description of BrowseBrick
@@ -32,13 +34,23 @@ use Combodo\iTop\DesignElement;
*/
class BrowseBrick extends PortalBrick
{
/** @var string DEFAULT_PAGE_TEMPLATE_PATH */
/**
* @var string DEFAULT_PAGE_TEMPLATE_PATH
* @deprecated 3.2.1
*/
const DEFAULT_MODE_LIST_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig';
/** @var string DEFAULT_MODE_MOSAIC_TEMPLATE_PATH */
/**
* @var string DEFAULT_MODE_MOSAIC_TEMPLATE_PATH
* @deprecated 3.2.1
*/
const DEFAULT_MODE_MOSAIC_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig';
/** @var string DEFAULT_MODE_TREE_TEMPLATE_PATH */
/**
* @var string DEFAULT_MODE_TREE_TEMPLATE_PATH
* @deprecated 3.2.1
*/
const DEFAULT_MODE_TREE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig';
/** @var string ENUM_BROWSE_MODE_LIST */
const ENUM_BROWSE_MODE_LIST = 'list';
/** @var string ENUM_BROWSE_MODE_TREE */
@@ -84,13 +96,6 @@ class BrowseBrick extends PortalBrick
const DEFAULT_ACTION_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL;
/** @var int DEFAULT_LIST_LENGTH */
const DEFAULT_LIST_LENGTH = 20;
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
'mode-list'=> self::DEFAULT_MODE_LIST_TEMPLATE_PATH,
'mode-mosaic'=> self::DEFAULT_MODE_MOSAIC_TEMPLATE_PATH,
'mode-tree'=> self::DEFAULT_MODE_TREE_TEMPLATE_PATH,
];
// Overloaded variables
public static $sRouteName = 'p_browse_brick';
@@ -111,6 +116,18 @@ class BrowseBrick extends PortalBrick
/** @var int $iDefaultListLength */
protected $iDefaultListLength;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'browse/layout.html.twig'),
TemplateDefinitionDto::Create('page_list', static::TEMPLATES_BASE_PATH . 'browse/mode_list.html.twig'),
TemplateDefinitionDto::Create('page_tree', static::TEMPLATES_BASE_PATH . 'browse/mode_tree.html.twig'),
TemplateDefinitionDto::Create('page_mosaic', static::TEMPLATES_BASE_PATH . 'browse/mode_mosaic.html.twig'),
);
}
/**
* BrowseBrick constructor.
*/
@@ -369,13 +386,8 @@ class BrowseBrick extends PortalBrick
$oTemplateNode = $oModeNode->GetOptionalElement('template');
if (($oTemplateNode !== null) && ($oTemplateNode->GetText() !== null))
{
$sTemplatePath = $oTemplateNode->GetText();
$this->SetTemplatePath('page_'.$sModeId, $oTemplateNode->GetText());
}
else
{
$sTemplatePath = static::$DEFAULT_TEMPLATES_PATH['mode-'.$sModeId];
}
$aModeData['template'] = $sTemplatePath;
$this->AddAvailableBrowseMode($sModeId, $aModeData);
}

View File

@@ -20,8 +20,8 @@
namespace Combodo\iTop\Portal\Brick;
use DOMFormatException;
use Combodo\iTop\DesignElement;
use DOMFormatException;
/**
* Description of CreateBrick
@@ -35,11 +35,6 @@ class CreateBrick extends PortalBrick
// Overloaded constants
const DEFAULT_DECORATION_CLASS_HOME = 'fas fa-plus';
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fas fa-plus fa-2x';
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/create/modal.html.twig';
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
];
/** @var string DEFAULT_CLASS */
const DEFAULT_CLASS = '';

View File

@@ -20,8 +20,10 @@
namespace Combodo\iTop\Portal\Brick;
use DOMFormatException;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
/**
* Description of FilterBrick
@@ -34,13 +36,12 @@ class FilterBrick extends PortalBrick
{
// Overloaded constants
const DEFAULT_VISIBLE_NAVIGATION_MENU = false;
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/filter/tile.html.twig';
/**
* @deprecated 3.2.1
*/
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/filter/tile.html.twig';
const DEFAULT_DECORATION_CLASS_HOME = 'fas fa-search';
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fas fa-search fa-2x';
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
];
/** @var string DEFAULT_TARGET_BRICK_CLASS */
const DEFAULT_TARGET_BRICK_CLASS = 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick';
/** @var string DEFAULT_SEARCH_PLACEHOLDER_VALUE */
@@ -63,6 +64,15 @@ class FilterBrick extends PortalBrick
/** @var string $sSearchSubmitClass */
protected $sSearchSubmitClass;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('tile', static::TEMPLATES_BASE_PATH . 'filter/tile.html.twig')
);
}
/**
* FilterBrick constructor.
*/

View File

@@ -20,11 +20,14 @@
namespace Combodo\iTop\Portal\Brick;
use Exception;
use DOMFormatException;
use DBSearch;
use MetaModel;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DBSearch;
use DOMFormatException;
use Exception;
use MetaModel;
use ModuleDesign;
class ManageBrick extends PortalBrick
{
@@ -51,26 +54,28 @@ class ManageBrick extends PortalBrick
/** @var string ENUM_DISPLAY_MODE_BAR */
const ENUM_DISPLAY_MODE_BAR = 'bar-chart';
/** @var string ENUM_PAGE_TEMPLATE_PATH_TABLE */
/** @var string ENUM_PAGE_TEMPLATE_PATH_TABLE
* @deprecated since 3.2.1
* */
const ENUM_PAGE_TEMPLATE_PATH_TABLE = 'itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig';
/** @var string ENUM_PAGE_TEMPLATE_PATH_CHART */
/** @var string ENUM_PAGE_TEMPLATE_PATH_CHART
* @deprecated since 3.2.1
* */
const ENUM_PAGE_TEMPLATE_PATH_CHART = 'itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig';
// Overloaded constants
/** Overloaded constants */
const DEFAULT_DECORATION_CLASS_HOME = 'fas fa-pen-square';
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fas fa-pen-square fa-2x';
/**
* @deprecated 3.2.1
*/
const DEFAULT_PAGE_TEMPLATE_PATH = self::ENUM_PAGE_TEMPLATE_PATH_TABLE;
const DEFAULT_DATA_LOADING = self::ENUM_DATA_LOADING_LAZY;
/**
* @deprecated 3.2.1
*/
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig';
const DEFAULT_TILE_CONTROLLER_ACTION = 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::TileAction';
const DEFAULT_LAYOUT_CHART_TEMPLATE_PATH = self::ENUM_PAGE_TEMPLATE_PATH_CHART;
const DEFAULT_LAYOUT_TABLE_TEMPLATE_PATH = self::ENUM_PAGE_TEMPLATE_PATH_TABLE;
const DEFAULT_LAYOUT_BADGE_TEMPLATE_PATH = self::ENUM_PAGE_TEMPLATE_PATH_TABLE;
const DEFAULT_TILE_CHART_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig';
const DEFAULT_TILE_TOP_LIST_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig';
const DEFAULT_TILE_BADGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-badge.html.twig';
const DEFAULT_TILE_DEFAULT_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig';
const DEFAULT_POPUP_EXPORT_EXCEL_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig';
/** @var string DEFAULT_OQL */
const DEFAULT_OQL = '';
@@ -91,40 +96,94 @@ class ManageBrick extends PortalBrick
/** @var bool DEFAULT_GROUP_SHOW_OTHERS */
const DEFAULT_GROUP_SHOW_OTHERS = true;
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
'layout-chart' => self::DEFAULT_LAYOUT_CHART_TEMPLATE_PATH,
'layout-table' => self::DEFAULT_LAYOUT_TABLE_TEMPLATE_PATH,
'layout-badge' => self::DEFAULT_LAYOUT_BADGE_TEMPLATE_PATH,
'tile-chart' => self::DEFAULT_TILE_CHART_TEMPLATE_PATH,
'tile-top-list' => self::DEFAULT_TILE_TOP_LIST_TEMPLATE_PATH,
'tile-badge' => self::DEFAULT_TILE_BADGE_TEMPLATE_PATH,
'tile-default' => self::DEFAULT_TILE_DEFAULT_TEMPLATE_PATH,
'popup-export-excel' => self::DEFAULT_POPUP_EXPORT_EXCEL_TEMPLATE_PATH,
];
/** @var array $aDisplayModes */
static $aDisplayModes = array(
public static array $aDisplayModes = array(
self::ENUM_DISPLAY_MODE_LIST,
self::ENUM_DISPLAY_MODE_PIE,
self::ENUM_DISPLAY_MODE_BAR,
);
/** @var array $aTileModes */
public static $aTileModes = array(
public static array $aTileModes = array(
self::ENUM_TILE_MODE_TEXT,
self::ENUM_TILE_MODE_BADGE,
self::ENUM_TILE_MODE_PIE,
self::ENUM_TILE_MODE_BAR,
self::ENUM_TILE_MODE_TOP,
);
/** Initialized in its getter as we need DEFAULT_TEMPLATE static values to be accessible */
/** @var array $aDefaultPresentationData */
private static $aDefaultPresentationData = [];
/** Specific data for the current brick, including brick definition overloads */
/** @var array $aPresentationData */
public $aPresentationData = [];
/** @var array $aPresentationData
* @deprecated since 3.2.1
*/
public static $aPresentationData = array(
self::ENUM_TILE_MODE_BADGE => array(
'decorationCssClass' => 'fas fa-id-card fa-2x',
'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-badge.html.twig',
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
self::ENUM_TILE_MODE_TOP => array(
'decorationCssClass' => 'fas fa-signal fa-rotate-270 fa-2x',
'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig',
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
self::ENUM_TILE_MODE_PIE => array(
'decorationCssClass' => 'fas fa-chart-pie fa-2x',
'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig',
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART,
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_PIE,
'need_details' => false,
),
self::ENUM_TILE_MODE_BAR => array(
'decorationCssClass' => 'fas fa-chart-bar fa-2x',
'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig',
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART,
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_BAR,
'need_details' => false,
),
self::ENUM_TILE_MODE_TEXT => array(
'decorationCssClass' => 'fas fa-pen-square fa-2x',
'tileTemplate' => self::DEFAULT_TILE_TEMPLATE_PATH,
'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE,
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
);
/** @var array $aDefaultTileData */
private static array $aDefaultTileData = [
self::ENUM_TILE_MODE_BADGE => [
'decorationCssClass' => 'fas fa-id-card fa-2x',
],
self::ENUM_TILE_MODE_TOP => [
'decorationCssClass' => 'fas fa-list-ol fa-2x',
],
self::ENUM_TILE_MODE_PIE => [
'decorationCssClass' => 'fas fa-chart-pie fa-2x',
],
self::ENUM_TILE_MODE_TEXT => [
'decorationCssClass' => 'fas fa-pen-square fa-2x',
],
self::ENUM_TILE_MODE_BAR => [
'decorationCssClass' => 'fas fa-chart-bar fa-2x',
],
];
/** @var array $aDefaultLayoutData */
private static array $aDefaultLayoutData = [
self::ENUM_DISPLAY_MODE_LIST => [
'need_details' => true,
],
self::ENUM_DISPLAY_MODE_PIE => [
'need_details' => false,
],
self::ENUM_DISPLAY_MODE_BAR => [
'need_details' => false,
],
];
// Overloaded variables
public static $sRouteName = 'p_manage_brick';
@@ -153,51 +212,22 @@ class ManageBrick extends PortalBrick
protected $bGroupShowOthers;
/** @var int $iDefaultListLength */
protected $iDefaultListLength;
/** @var string $sPopupExportExcelTemplatePath */
protected $sPopupExportExcelTemplatePath;
/**
* Returns true if the $sDisplayMode need objects details for rendering.
*
* @param string $sDisplayMode
*
* @return bool
*/
static public function AreDetailsNeededForDisplayMode($sDisplayMode)
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
$bNeedDetails = false;
foreach (static::GetDefaultPresentationData() as $aData)
{
if ($aData['layoutDisplayMode'] === $sDisplayMode)
{
$bNeedDetails = $aData['need_details'];
break;
}
}
return $bNeedDetails;
}
/**
* Returns the page template path for the $sDisplayMode
*
* @param string $sDisplayMode
*
* @return string
*/
static public function GetPageTemplateFromDisplayMode($sDisplayMode)
{
$sTemplate = static::$DEFAULT_TEMPLATES_PATH['page'];
foreach (static::GetDefaultPresentationData() as $aData)
{
if ($aData['layoutDisplayMode'] === $sDisplayMode)
{
$sTemplate = $aData['layoutTemplate'];
break;
}
}
return $sTemplate;
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('tile', static::TEMPLATES_BASE_PATH . 'manage/tile-default.html.twig'),
TemplateDefinitionDto::Create('tile_badge', static::TEMPLATES_BASE_PATH. 'manage/tile-badge.html.twig'),
TemplateDefinitionDto::Create('tile_chart', static::TEMPLATES_BASE_PATH . 'manage/tile-chart.html.twig'),
TemplateDefinitionDto::Create('tile_top_list', static::TEMPLATES_BASE_PATH . 'manage/tile-top-list.html.twig'),
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'manage/layout.html.twig'),
TemplateDefinitionDto::Create('page_table', static::TEMPLATES_BASE_PATH . 'manage/layout-table.html.twig'),
TemplateDefinitionDto::Create('page_chart', static::TEMPLATES_BASE_PATH . 'manage/layout-chart.html.twig'),
TemplateDefinitionDto::Create('mode_chart_bar', static::TEMPLATES_BASE_PATH . 'manage/mode-bar-chart.html.twig', true, self::ENUM_DISPLAY_MODE_BAR),
TemplateDefinitionDto::Create('mode_chart_pie', static::TEMPLATES_BASE_PATH . 'manage/mode-pie-chart.html.twig', true,self::ENUM_DISPLAY_MODE_PIE),
);
}
/**
@@ -219,12 +249,106 @@ class ManageBrick extends PortalBrick
$this->iGroupLimit = static::DEFAULT_GROUP_LIMIT;
$this->bGroupShowOthers = static::DEFAULT_GROUP_SHOW_OTHERS;
$this->iDefaultListLength = static::DEFAULT_LIST_LENGTH;
$this->sPopupExportExcelTemplatePath = static::$DEFAULT_TEMPLATES_PATH['popup-export-excel'];
// This is hardcoded for now, we might allow area grouping on another attribute in the future
$this->AddGrouping('areas', array('attribute' => 'finalclass'));
}
/**
* Returns true if the $sDisplayMode need objects details for rendering.
*
* @deprecated since 3.2.1
*
* @param string $sDisplayMode
*
* @return bool
*/
static public function AreDetailsNeededForDisplayMode($sDisplayMode)
{
$bNeedDetails = false;
foreach (static::$aPresentationData as $aData)
{
if ($aData['layoutDisplayMode'] === $sDisplayMode)
{
$bNeedDetails = $aData['need_details'];
break;
}
}
return $bNeedDetails;
}
/**
* Returns if the $sLayoutMode need objects details for rendering.
*
* @since 3.2.1
*
* @param string $sLayoutMode
*
* @return bool
*/
public function IsDetailsNeeded(string $sLayoutMode): bool
{
return static::$aDefaultLayoutData[$sLayoutMode]['need_details'];
}
/**
* Returns the page template path for the $sDisplayMode
*
* @deprecated since 3.2.1
*
* @param string $sDisplayMode
* @return string
*/
static public function GetPageTemplateFromDisplayMode($sDisplayMode)
{
$sTemplate = static::DEFAULT_PAGE_TEMPLATE_PATH;
foreach (static::$aPresentationData as $aData)
{
if ($aData['layoutDisplayMode'] === $sDisplayMode)
{
$sTemplate = $aData['layoutTemplate'];
break;
}
}
return $sTemplate;
}
/**
* Returns the page template path for the $sDisplayMode
*
* @since 3.2.1
*
* @param string $sDisplayMode
*
* @return string
*/
public function GetPageTemplate(string $sDisplayMode): string
{
return match ($sDisplayMode) {
self::ENUM_DISPLAY_MODE_BAR, self::ENUM_DISPLAY_MODE_PIE => $this->GetTemplatePath('page_chart'),
default => $this->GetTemplatePath('page_table'),
};
}
/**
* Returns the page template path for the $sDisplayMode
* @since 3.2.1
*
* @return string
*/
public function GetTileTemplate(): string
{
return match ($this->GetTileMode()) {
self::ENUM_TILE_MODE_BADGE => $this->GetTemplatePath('tile_badge'),
self::ENUM_TILE_MODE_PIE, self::ENUM_TILE_MODE_BAR => $this->GetTemplatePath('tile_chart'),
self::ENUM_TILE_MODE_TOP => $this->GetTemplatePath('tile_top_list'),
default => $this->GetTemplatePath('tile'),
};
}
/**
* Returns the brick oql
*
@@ -319,10 +443,24 @@ class ManageBrick extends PortalBrick
return $this->sTileMode;
}
/**
* @deprecated since 3.2.1
*/
public function GetDecorationCssClass()
{
return static::GetDefaultPresentationData()[$this->sTileMode]['decorationCssClass'];
return static::$aPresentationData[$this->sTileMode]['decorationCssClass'];
}
/**
* @since 3.2.1
*
* @return mixed|string
*/
public function GetDecoration()
{
return static::$aDefaultTileData[$this->sTileMode]['decorationCssClass'];
}
/**
* Sets the tile mode (display)
*
@@ -337,65 +475,21 @@ class ManageBrick extends PortalBrick
return $this;
}
public static function GetDefaultPresentationData()
{
/** If the table isn't initialized yet, do it now */
if (count(static::$aDefaultPresentationData) === 0) {
static::$aDefaultPresentationData = array(
self::ENUM_TILE_MODE_BADGE => array(
'decorationCssClass' => 'fas fa-id-card fa-2x',
'tileTemplate' => static::$DEFAULT_TEMPLATES_PATH['tile-badge'],
'layoutTemplate' => static::$DEFAULT_TEMPLATES_PATH['layout-table'],
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
self::ENUM_TILE_MODE_TOP => array(
'decorationCssClass' => 'fas fa-signal fa-rotate-270 fa-2x',
'tileTemplate' => static::$DEFAULT_TEMPLATES_PATH['tile-top-list'],
'layoutTemplate' => static::$DEFAULT_TEMPLATES_PATH['layout-table'],
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
self::ENUM_TILE_MODE_PIE => array(
'decorationCssClass' => 'fas fa-chart-pie fa-2x',
'tileTemplate' => static::$DEFAULT_TEMPLATES_PATH['tile-chart'],
'layoutTemplate' => static::$DEFAULT_TEMPLATES_PATH['layout-chart'],
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_PIE,
'need_details' => false,
),
self::ENUM_TILE_MODE_BAR => array(
'decorationCssClass' => 'fas fa-chart-bar fa-2x',
'tileTemplate' => static::$DEFAULT_TEMPLATES_PATH['tile-chart'],
'layoutTemplate' => static::$DEFAULT_TEMPLATES_PATH['layout-chart'],
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_BAR,
'need_details' => false,
),
self::ENUM_TILE_MODE_TEXT => array(
'decorationCssClass' => 'fas fa-pen-square fa-2x',
'tileTemplate' => static::$DEFAULT_TEMPLATES_PATH['tile-default'],
'layoutTemplate' => static::$DEFAULT_TEMPLATES_PATH['layout-table'],
'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST,
'need_details' => true,
),
);
}
return static::$aDefaultPresentationData;
}
/**
* @deprecated since 3.2.1
*
* @param string $sTileMode
*
* @return string[] parameters for specified type, default parameters if type is invalid
*/
public function GetPresentationDataForTileMode($sTileMode)
{
if (isset(static::GetDefaultPresentationData()[$sTileMode]))
if (isset(static::$aPresentationData[$sTileMode]))
{
return static::GetDefaultPresentationData()[$sTileMode];
return static::$aPresentationData[$sTileMode];
}
return static::GetDefaultPresentationData()[static::DEFAULT_TILE_MODE];
return static::$aPresentationData[static::DEFAULT_TILE_MODE];
}
/**
@@ -505,16 +599,7 @@ class ManageBrick extends PortalBrick
$this->iDefaultListLength = $iDefaultListLength;
return $this;
}
public function GetPopupExportExcelTemplatePath() {
return $this->sPopupExportExcelTemplatePath;
}
public function SetPopupExportExcelTemplatePath($sPopupExportExcelTemplatePath) {
$this->sPopupExportExcelTemplatePath = $sPopupExportExcelTemplatePath;
return $this;
}
/**
* Adds a grouping.
*
@@ -829,10 +914,11 @@ class ManageBrick extends PortalBrick
case 'tile';
$this->SetTileMode($oDisplayNode->GetText(static::DEFAULT_TILE_MODE));
$aTileParametersForType = $this->GetPresentationDataForTileMode($this->sTileMode);
$this->SetTileTemplatePath($aTileParametersForType['tileTemplate']);
$this->SetPageTemplatePath($aTileParametersForType['layoutTemplate']);
if($this->sDecorationClassHome === static::DEFAULT_DECORATION_CLASS_HOME){
$this->sDecorationClassHome = static::$aDefaultTileData[$this->GetTileMode()]['decorationCssClass'];
$this->SetDecorationClassNavigationMenu(static::$aDefaultTileData[$this->GetTileMode()]['decorationCssClass']);
$this->SetDecorationClassHome(static::$aDefaultTileData[$this->GetTileMode()]['decorationCssClass']);
}
break;
}
}
@@ -966,6 +1052,19 @@ class ManageBrick extends PortalBrick
}
}
break;
case 'templates':
$aTemplatesIds = static::GetTemplatesProviderService()->GetRegister()->GetProviderTemplatesIds(self::class);
/** @var TemplateDefinitionDto $oTemplateDefinition */
foreach ($aTemplatesIds as $sTemplateId) {
$oTemplateNodeList = $oBrickSubNode->GetNodes('template[@id='.ModuleDesign::XPathQuote($sTemplateId).']');
if ($oTemplateNodeList->length > 0) {
/** @var \Combodo\iTop\DesignElement $oTemplateNode */
$oTemplateNode = $oTemplateNodeList->item(0);
$this->SetTemplatePath($sTemplateId, $oTemplateNode->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
}
}
break;
}
}
@@ -1013,10 +1112,10 @@ class ManageBrick extends PortalBrick
// Checking the navigation icon
$sDecorationClassNavigationMenu = $this->GetDecorationClassNavigationMenu();
if (empty($sDecorationClassNavigationMenu) && isset(static::GetDefaultPresentationData()[$this->sTileMode]))
if (empty($sDecorationClassNavigationMenu) && isset(static::$aDefaultTileData[$this->sTileMode]))
{
/** @var string $sDecorationClassNavigationMenu */
$sDecorationClassNavigationMenu = static::GetDefaultPresentationData()[$this->sTileMode]['decorationCssClass'];
$sDecorationClassNavigationMenu = static::$aDefaultTileData[$this->sTileMode]['decorationCssClass'];
if (!empty($sDecorationClassNavigationMenu))
{
$this->SetDecorationClassNavigationMenu($sDecorationClassNavigationMenu);

View File

@@ -1,62 +0,0 @@
<?php
namespace Combodo\iTop\Portal\Brick;
/**
* Description of ObjectBrick
*
* @package Combodo\iTop\Portal\Brick
* @since 3.2.1
* @author Stephen Abello <stephen.abello@combodo.com>
*/
abstract class ObjectBrick extends AbstractBrick
{
/** @var string DEFAULT_PAGE_TEMPLATE_PATH */
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/object/layout.html.twig';
/** @var string DEFAULT_MODAL_TEMPLATE_PATH */
const DEFAULT_MODAL_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/object/modal.html.twig';
/** @var string DEFAULT_MODE_LOADER_TEMPLATE_PATH */
const DEFAULT_MODE_LOADER_TEMPLATE_PATH = 'itop-portal-base/portal/templates/modal/mode_loader.html.twig';
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'modal' => self::DEFAULT_MODAL_TEMPLATE_PATH,
'mode_loader' => self::DEFAULT_MODE_LOADER_TEMPLATE_PATH,
];
/**
* @param $aCombodoPortalInstanceConf
*
* @return void
*/
public static function InitializeSelf($aCombodoPortalInstanceConf): void
{
if(array_key_exists('properties', $aCombodoPortalInstanceConf))
{
static::LoadClassDefinitionFromPortalProperties($aCombodoPortalInstanceConf['properties']);
}
}
/**
* @return string
*/
public static function GetPageDefaultTemplatePath(): string
{
return static::$DEFAULT_TEMPLATES_PATH['page'];
}
/**
* @return string
*/
public static function GetModalDefaultTemplatePath(): string
{
return static::$DEFAULT_TEMPLATES_PATH['modal'];
}
/**
* @return string
*/
public static function GetModeLoaderDefaultTemplatePath(): string
{
return static::$DEFAULT_TEMPLATES_PATH['mode_loader'];
}
}

View File

@@ -20,9 +20,11 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
use ModuleDesign;
use Combodo\iTop\DesignElement;
/**
* Description of PortalBrick
@@ -63,11 +65,6 @@ abstract class PortalBrick extends AbstractBrick
/** @var string DEFAULT_OPENING_TARGET */
const DEFAULT_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL;
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
];
/** @var string|null $sRouteName */
static $sRouteName = null;
/** @var array $aOpeningTargets */
@@ -75,6 +72,8 @@ abstract class PortalBrick extends AbstractBrick
/** @var int $iWidth */
protected $iWidth;
/** @var bool width in pixel flag */
public bool $bIsWidthPixel = false;
/** @var int $iHeight */
protected $iHeight;
/** @var bool $bModal */
@@ -87,7 +86,7 @@ abstract class PortalBrick extends AbstractBrick
protected $sDecorationClassHome;
/** @var string $sDecorationClassNavigationMenu */
protected $sDecorationClassNavigationMenu;
/** @var string $sTileTemplatePath */
/** @var string $sTileTemplatePath @deprecated since 3.2.1 */
protected $sTileTemplatePath;
/** @var string|null $sTileControllerAction */
protected $sTileControllerAction;
@@ -104,6 +103,17 @@ abstract class PortalBrick extends AbstractBrick
/** @var string $sTitleNavigationMenu */
protected $sTitleNavigationMenu;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('tile', static::TEMPLATES_BASE_PATH . 'tile.html.twig'),
);
}
/**
* @return string|null
*/
@@ -126,7 +136,9 @@ abstract class PortalBrick extends AbstractBrick
$this->bVisibleNavigationMenu = static::DEFAULT_VISIBLE_NAVIGATION_MENU;
$this->sDecorationClassHome = static::DEFAULT_DECORATION_CLASS_HOME;
$this->sDecorationClassNavigationMenu = static::DEFAULT_DECORATION_CLASS_NAVIGATION_MENU;
$this->sTileTemplatePath = static::$DEFAULT_TEMPLATES_PATH['tile'];
// BEGIN cleaning 3.2.1 deprecated
$this->sTileTemplatePath = static::DEFAULT_TILE_TEMPLATE_PATH;
// END cleaning 3.2.1 deprecated
$this->sTileControllerAction = static::DEFAULT_TILE_CONTROLLER_ACTION;
$this->sOpeningTarget = static::DEFAULT_OPENING_TARGET;
}
@@ -255,10 +267,12 @@ abstract class PortalBrick extends AbstractBrick
* Returns the brick tile template path
*
* @return string
*
* @deprecated since 3.2.1 use GetTemplatePath('tile') instead
*/
public function GetTileTemplatePath()
{
return $this->sTileTemplatePath;
return $this->GetTemplatePath('tile');
}
/**
@@ -431,10 +445,13 @@ abstract class PortalBrick extends AbstractBrick
* @param string $sTileTemplatePath
*
* @return \Combodo\iTop\Portal\Brick\PortalBrick
*
* @deprecated since 3.2.1 use SetTemplatePath('tile') instead
*/
public function SetTileTemplatePath($sTileTemplatePath)
{
$this->sTileTemplatePath = $sTileTemplatePath;
$this->SetTemplatePath('tile', $sTileTemplatePath);
return $this;
}
@@ -488,7 +505,9 @@ abstract class PortalBrick extends AbstractBrick
switch ($oBrickSubNode->nodeName)
{
case 'width':
$this->SetWidth((int)$oBrickSubNode->GetText(static::DEFAULT_WIDTH));
$sWidth = $oBrickSubNode->GetText(static::DEFAULT_WIDTH);
$this->bIsWidthPixel = str_contains($sWidth, 'px');
$this->SetWidth((int)$sWidth);
break;
case 'height':
@@ -531,7 +550,7 @@ abstract class PortalBrick extends AbstractBrick
{
/** @var \Combodo\iTop\DesignElement $oTemplateNode */
$oTemplateNode = $oTemplateNodeList->item(0);
$this->SetTileTemplatePath($oTemplateNode->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
$this->SetTemplatePath('tile', $oTemplateNode->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
}
break;

View File

@@ -20,6 +20,8 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
/**
@@ -32,17 +34,10 @@ use DOMFormatException;
class UserProfileBrick extends PortalBrick
{
// Overloaded constants
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig';
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/user-profile/tile.html.twig';
const DEFAULT_VISIBLE_NAVIGATION_MENU = false;
const DEFAULT_VISIBLE_HOME = false;
const DEFAUT_TITLE = 'Brick:Portal:UserProfile:Title';
const DEFAULT_DECORATION_CLASS_HOME = 'glyphicon glyphicon-user';
const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'glyphicon glyphicon-user';
protected static $DEFAULT_TEMPLATES_PATH = [
'page' => self::DEFAULT_PAGE_TEMPLATE_PATH,
'tile' => self::DEFAULT_TILE_TEMPLATE_PATH,
];
/** @var bool DEFAULT_SHOW_PICTURE_FORM */
const DEFAULT_SHOW_PICTURE_FORM = true;
/** @var bool DEFAULT_SHOW_PREFERENCES_FORM */
@@ -62,6 +57,15 @@ class UserProfileBrick extends PortalBrick
/** @var bool $bShowPasswordForm */
protected $bShowPasswordForm;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'user-profile/layout.html.twig'),
);
}
/**
* UserProfileBrick constructor.
*/

View File

@@ -20,7 +20,11 @@
namespace Combodo\iTop\Portal\Controller;
use \Symfony\Bundle\FrameworkBundle\Controller\AbstractController as SymfonyAbstractController;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderInterface;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as SymfonyAbstractController;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Service\Attribute\Required;
@@ -31,8 +35,19 @@ use Symfony\Contracts\Service\Attribute\Required;
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.3.0
*/
abstract class AbstractController extends SymfonyAbstractController
abstract class AbstractController extends SymfonyAbstractController implements TemplatesProviderInterface
{
const TEMPLATES_BASE_PATH = 'itop-portal-base/portal/templates/';
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'layout.html.twig'),
TemplateDefinitionDto::Create('modal', static::TEMPLATES_BASE_PATH . 'modal/layout.html.twig'),
);
}
/**
* @var \Symfony\Component\Routing\RouterInterface symfony router
*
@@ -46,6 +61,25 @@ abstract class AbstractController extends SymfonyAbstractController
$this->oRouter = $oRouter;
}
/** @var TemplatesProviderService templates provider service */
private TemplatesProviderService $oTemplatesService;
#[Required]
public function SetTemplatesService(TemplatesProviderService $oTemplatesService): void
{
$this->oTemplatesService = $oTemplatesService;
}
/**
* Return the templates provider service.
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService
*/
protected function GetTemplatesProviderService(): TemplatesProviderService
{
return $this->oTemplatesService;
}
/**
* Unlike {@see \Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait::redirectToRoute()}, this method directly calls the route controller without creating a redirection client side
*
@@ -104,4 +138,33 @@ abstract class AbstractController extends SymfonyAbstractController
return $aRouteDefaults['_controller'];
}
/**
* Returns the controller template path
*
* @since 3.2.1
*
* @param string $sTemplateId
*
* @return string
*/
public function GetTemplatePath(string $sTemplateId): string
{
return static::GetTemplatesProviderService()->GetProviderInstanceTemplatePath($this, $sTemplateId);
}
/**
* Sets the brick template path
*
* @since 3.2.1
* @param string $sTemplateId
* @param string $sTileTemplatePath
*
* @return \Combodo\iTop\Portal\Controller\AbstractController
*/
public function SetTemplatePath(string $sTemplateId, string $sTileTemplatePath): AbstractController
{
static::GetTemplatesProviderService()->OverrideInstanceTemplatePath($this, $sTemplateId, $sTileTemplatePath);
return $this;
}
}

View File

@@ -75,7 +75,7 @@ class AggregatePageBrickController extends BrickController
$aTilesRendering = $this->GetBricksTileRendering($oRequest, $aAggregatePageBricks);
$sLayoutTemplate = $oBrick->GetPageTemplatePath();
$sLayoutTemplate = $oBrick->GetTemplatePath('page');
$aData = array(
'oBrick' => $oBrick,
'aggregatepage_bricks' => $aAggregatePageBricks,

View File

@@ -495,13 +495,13 @@ class BrowseBrickController extends BrickController
// - Create a template for that browse mode,
// - Add the mode to those available in the brick configuration,
// - Create a router and add a route for the new browse mode
if ($oBrick->GetPageTemplatePath() !== null)
if ($oBrick->HasInstanceOverriddenTemplate('page'))
{
$sTemplatePath = $oBrick->GetPageTemplatePath();
$sTemplatePath = $oBrick->GetTemplatePath('page');
}
else
{
$sTemplatePath = $aBrowseModes[$sBrowseMode]['template'];
$sTemplatePath = $oBrick->GetTemplatePath('page_' .$sBrowseMode);
}
$oResponse = $this->render($sTemplatePath, $aData);
}

View File

@@ -23,7 +23,6 @@ namespace Combodo\iTop\Portal\Controller;
use Combodo\iTop\Portal\Brick\BrickCollection;
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Class CreateBrickController

View File

@@ -21,6 +21,8 @@
namespace Combodo\iTop\Portal\Controller;
use Combodo\iTop\Portal\Brick\BrickCollection;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -33,6 +35,15 @@ use Symfony\Component\HttpFoundation\Response;
*/
class DefaultController extends AbstractController
{
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('home', static::TEMPLATES_BASE_PATH . 'home/layout.html.twig'),
);
}
/**
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param \Combodo\iTop\Portal\Brick\BrickCollection $oBricksCollection
@@ -72,7 +83,7 @@ class DefaultController extends AbstractController
}
// Home page template
$sTemplatePath = $this->getParameter('combodo.portal.instance.conf')['properties']['templates']['home'];
$sTemplatePath = $this->GetTemplatePath('home');
return $this->render($sTemplatePath, $aData);
}

View File

@@ -40,6 +40,8 @@ use Combodo\iTop\Portal\Helper\RequestManipulatorHelper;
use Combodo\iTop\Portal\Helper\ScopeValidatorHelper;
use Combodo\iTop\Portal\Helper\SecurityHelper;
use Combodo\iTop\Portal\Routing\UrlGenerator;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DBObject;
use DBObjectSet;
use DBSearch;
@@ -70,10 +72,20 @@ use utils;
*/
class ManageBrickController extends BrickController
{
/** @var string EXCEL_EXPORT_TEMPLATE_PATH
/**
* @var string EXCEL_EXPORT_TEMPLATE_PATH
* @deprecated since 3.2.1
*/
const EXCEL_EXPORT_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig';
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('modal_export_excel', static::TEMPLATES_BASE_PATH . 'bricks/manage/popup-export-excel.html.twig'),
);
}
/**
* @param \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection
@@ -123,7 +135,7 @@ class ManageBrickController extends BrickController
$sDisplayMode = $oBrick->GetDefaultDisplayMode();
}
$aData = $this->GetData($oRequest, $sBrickId, $sGroupingTab, $oBrick::AreDetailsNeededForDisplayMode($sDisplayMode));
$aData = $this->GetData($oRequest, $sBrickId, $sGroupingTab, $oBrick->IsDetailsNeeded($sDisplayMode));
$aExportFields = $oBrick->GetExportFields();
$aData = $aData + array(
@@ -137,7 +149,7 @@ class ManageBrickController extends BrickController
}
else
{
$sLayoutTemplate = $oBrick::GetPageTemplateFromDisplayMode($sDisplayMode);
$sLayoutTemplate = $oBrick->GetPageTemplate($sDisplayMode);
$oResponse = $this->render($sLayoutTemplate, $aData);
}
@@ -169,7 +181,7 @@ class ManageBrickController extends BrickController
$aData = array();
}
return $this->render($oBrick->GetTileTemplatePath(), $aData);
return $this->render($oBrick->GetTileTemplate(), $aData);
}
/**
@@ -283,7 +295,7 @@ class ManageBrickController extends BrickController
'sWikiUrl' => 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Auser%3Alists#excel_export',
);
return $this->render($oBrick->GetPopupExportExcelTemplatePath(), $aData);
return $this->render($this->GetTemplatePath('modal_export_excel'), $aData);
}
/**

View File

@@ -29,7 +29,6 @@ use BinaryExpression;
use Combodo\iTop\Form\Field\DateTimeField;
use Combodo\iTop\Portal\Brick\BrickCollection;
use Combodo\iTop\Portal\Brick\CreateBrick;
use Combodo\iTop\Portal\Brick\ObjectBrick;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
use Combodo\iTop\Portal\Helper\NavigationRuleHelper;
@@ -38,6 +37,8 @@ use Combodo\iTop\Portal\Helper\RequestManipulatorHelper;
use Combodo\iTop\Portal\Helper\ScopeValidatorHelper;
use Combodo\iTop\Portal\Helper\SecurityHelper;
use Combodo\iTop\Portal\Routing\UrlGenerator;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use Combodo\iTop\Renderer\Bootstrap\FieldRenderer\BsLinkedSetFieldRenderer;
use DBObject;
use DBObjectSearch;
@@ -75,6 +76,18 @@ class ObjectController extends BrickController
const DEFAULT_PAGE_NUMBER = 1;
const DEFAULT_LIST_LENGTH = 10;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH. 'bricks/object/layout.html.twig'),
TemplateDefinitionDto::Create('modal', static::TEMPLATES_BASE_PATH. 'bricks/object/modal.html.twig'),
TemplateDefinitionDto::Create('mode_create', static::TEMPLATES_BASE_PATH.'bricks/object/mode_create.html.twig', true, 'create'),
TemplateDefinitionDto::Create('mode_loader', static::TEMPLATES_BASE_PATH.'modal/mode_loader.html.twig', true, 'loader'),
);
}
/**
* @param \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper
* @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidatorHelper
@@ -101,7 +114,7 @@ class ObjectController extends BrickController
protected array $aCombodoPortalInstanceConf = []
)
{
ObjectBrick::InitializeSelf($this->aCombodoPortalInstanceConf);
}
/**
@@ -237,7 +250,7 @@ class ObjectController extends BrickController
if ($oRequest->isXmlHttpRequest()) {
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation)) {
$oResponse = $this->render(ObjectBrick::GetModalDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
} else {
$oResponse = new JsonResponse($aData);
}
@@ -251,7 +264,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render(ObjectBrick::GetPageDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
}
return $oResponse;
@@ -312,7 +325,7 @@ class ObjectController extends BrickController
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation))
{
$oResponse = $this->render(ObjectBrick::GetModalDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
}
else
{
@@ -332,7 +345,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render(ObjectBrick::GetPageDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
}
return $oResponse;
@@ -539,11 +552,11 @@ class ObjectController extends BrickController
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation))
{
$oResponse = $this->render(ObjectBrick::GetModalDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
}
elseif ($sOperation === 'redirect')
{
$oResponse = $this->render(ObjectBrick::GetModeLoaderDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('mode_loader'), $aData);
}
else
{
@@ -552,7 +565,7 @@ class ObjectController extends BrickController
}
else
{
$oResponse = $this->render(ObjectBrick::GetPageDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
}
return $oResponse;
@@ -1016,12 +1029,12 @@ class ObjectController extends BrickController
if ($oRequest->isXmlHttpRequest())
{
$oResponse = $this->render(ObjectBrick::GetModalDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
}
else
{
//throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
$oResponse = $this->render(ObjectBrick::GetPageDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
}
}
else
@@ -1602,7 +1615,7 @@ class ObjectController extends BrickController
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation))
{
$oResponse = $this->render(ObjectBrick::GetModalDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
}
else
{
@@ -1622,7 +1635,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render(ObjectBrick::GetPageDefaultTemplatePath(), $aData);
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
}
return $oResponse;
@@ -1656,7 +1669,7 @@ class ObjectController extends BrickController
if (!empty($sBrickId))
{
$oBrick = $this->oBrickCollection->GetBrickById($sBrickId);
$sTemplatePath = $oBrick->GetPageTemplatePath();
$sTemplatePath = $oBrick->GetTemplatePath('page');
$aData['sBrickId'] = $sBrickId;
$aData['oBrick'] = $oBrick;

View File

@@ -166,7 +166,7 @@ class UserProfileBrickController extends BrickController
$this->ManageUserProfileBrickExtensibility($sTab, $aData);
$oResponse = $this->render($oBrick->GetPageTemplatePath(), $aData);
$oResponse = $this->render($oBrick->GetTemplatePath('page'), $aData);
}
return $oResponse;

View File

@@ -0,0 +1,127 @@
<?php
namespace Combodo\iTop\Portal\DataCollector;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
/**
* Template information collector for Symfony profiler.
*
* @package Combodo\iTop\Portal\DataCollector
* @since 3.2.1
*/
class PortalCollector extends AbstractDataCollector
{
/**
* Constructor.
*
*
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService $oTemplatesProviderService
*/
public function __construct(private readonly TemplatesProviderService $oTemplatesProviderService)
{
}
/** @inheritdoc */
public function collect(Request $request, Response $response, Throwable $exception = null): void
{
$oRegister = $this->oTemplatesProviderService->GetRegister();
$aTemplatesDefinitions = $oRegister->GetTemplatesDefinitions();
$this->data = [
'templates_definitions' => $aTemplatesDefinitions,
'instances_overridden_templates' => $this->oTemplatesProviderService->GetInstancesOverriddenTemplatesPaths(),
'templates_count' => $this->ComputeOverridesCount($aTemplatesDefinitions),
'ui_version' => $oRegister->GetUIVersion(),
];
}
/**
* @return string|null
*/
public static function getTemplate(): ?string
{
return 'itop-portal-base/portal/templates/data_collector/portal.html.twig';
}
/**
* @return array
* @noinspection PhpUnused
*/
public function GetTemplatesDefinitions(): array
{
return $this->data['templates_definitions'];
}
/**
* @return array
* @noinspection PhpUnused
*/
public function GetInstancesOverriddenTemplates(): array
{
return $this->data['instances_overridden_templates'];
}
/**
* @return array
* @noinspection PhpUnused
*/
public function GetTemplatesCount(): array
{
return $this->data['templates_count'];
}
/**
* @return string
* @noinspection PhpUnused
*/
public function GetUIVersion(): string
{
return $this->data['ui_version'];
}
private function ComputeOverridesCount($aTemplatesDefinitions): array
{
$iCount = 0;
$iOverridesCount = 0;
$aExtensions = [];
foreach($aTemplatesDefinitions as $templates){
foreach ($templates as $template) {
$aMatches = [];
preg_match('#([\w-]+)/#', $template->GetPath(), $aMatches);
if(!in_array($aMatches[1], $aExtensions)){
$aExtensions[] = $aMatches[1];
}
$iCount++;
if ($template->IsOverridden()) {
$iOverridesCount++;
}
}
}
return [
'count' => $iCount,
'providers_count' => count($aTemplatesDefinitions),
'overrides_count' => $iOverridesCount,
'extensions_count' => count($aExtensions),
'bricks_count' => count($this->oTemplatesProviderService->GetInstancesOverriddenTemplatesPaths()),
];
}
/**
* @return string
* @noinspection PhpUnused
*/
public function GetInstanceDescriptionName(): string
{
return 'portal';
}
}

View File

@@ -21,9 +21,10 @@ namespace Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXml
use Combodo\iTop\Application\Branding;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Helper\UIExtensionsHelper;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderInterface;
use DOMFormatException;
use Exception;
use ReflectionClass;
use Symfony\Component\DependencyInjection\Container;
use utils;
@@ -74,6 +75,7 @@ class Basic extends AbstractConfiguration
$aPortalConf = array(
'properties' => array(
'id' => $_ENV['PORTAL_ID'],
'ui_version' => '2017',
'name' => 'Page:DefaultTitle',
'logo' => Branding::GetPortalLogoAbsoluteUrl(),
'favicon' => Branding::GetPortalFavIconAbsoluteUrl(),
@@ -85,7 +87,6 @@ class Basic extends AbstractConfiguration
'templates' => array(
'layout' => 'itop-portal-base/portal/templates/layout.html.twig',
'home' => 'itop-portal-base/portal/templates/home/layout.html.twig',
'bricks' => array(),
),
'urlmaker_class' => null,
'triggers_query' => null,
@@ -117,6 +118,7 @@ class Basic extends AbstractConfiguration
{
switch ($oPropertyNode->nodeName)
{
case 'ui_version':
case 'name':
case 'urlmaker_class':
case 'triggers_query':
@@ -186,19 +188,22 @@ class Basic extends AbstractConfiguration
$aPortalConf['properties']['templates'][$sNodeId] = $oSubNode->GetText(null);
break;
default:
// Try to accept the value as a global brick template, brick id format is "FQCN:page"
[$sBrickFQCN, $sPage] = explode(':', $sNodeId);
if (utils::IsNotNullOrEmptyString($sBrickFQCN) && utils::IsNotNullOrEmptyString($sPage))
{
$aPortalConf['properties']['templates']['bricks'][$sBrickFQCN][$sPage] = $oSubNode->GetText(null);
break;
$aMatches = [];
// allowed format is: <class implementing TemplatesProviderInterface>:<template_id>
if(preg_match('#([\w\\\d_]+):(\w+)#', $sNodeId, $aMatches)){
try{
$oClass = new ReflectionClass($aMatches[1]);
if($oClass->implementsInterface(TemplatesProviderInterface::class)){
$aPortalConf['properties']['templates'][$aMatches[1]][$aMatches[2]] = $oSubNode->GetText(null);
break;
}
}
catch(Exception){}
}
throw new DOMFormatException(
'Value "'.$sNodeId.'" is not handled for template[@id]',
'Template ID "'.$sNodeId.'" is not handled in module design templates property',
null, null, $oSubNode
);
break;
}
break;
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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\Portal\Service\TemplatesProvider;
/**
* Template definition DTO.
*
* Describes a template.
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
class TemplateDefinitionDto
{
/**
* Create a new template definition instance.
*
* @param string $sTemplateId template identifier
* @param string $sPath template path
* @param bool $isOverridable flag set when the template is overridable
* @param string|null $sAlias template alias
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto
*/
public static function Create(string $sTemplateId, string $sPath, bool $isOverridable = true, ?string $sAlias = null): TemplateDefinitionDto
{
return new TemplateDefinitionDto($sTemplateId, $sPath, $isOverridable, $sAlias);
}
/** @var bool $bIsOverridden flag set when overriding a template */
private bool $bIsOverridden = false;
/** @var string|null $sInitialValue Initial template value */
private ?string $sInitialValue;
/**
* Constructor.
*
* @param string $sId template identifier
* @param string|null $sPath template path
* @param bool|null $bIsOverridable flag set when the template is overridable
* @param string|null $sAlias template alias
*/
private function __construct(
private readonly string $sId,
private ?string $sPath = null,
private readonly ?bool $bIsOverridable = false,
private readonly ?string $sAlias = null,
)
{
// save overridable values
$this->sInitialValue = $sPath;
}
/**
* Return the template identifier.
*
* @return string
*/
public function GetId(): string
{
return $this->sId !== null ? $this->sId : '';
}
/**
* Return the template path.
*
* @param bool $bInitialValue Return the initial value instead of the overridden one.
*
* @return string
*/
public function GetPath(bool $bInitialValue = false): string
{
if($bInitialValue){
return $this->sInitialValue !== null ? $this->sInitialValue : '';
}
return $this->sPath !== null ? $this->sPath : '';
}
/**
* Return the overridable state.
*
* @return bool
*/
public function IsOverridable(): bool
{
return $this->bIsOverridable !== null ? $this->bIsOverridable : false;
}
/**
* Return the template alias.
*
* @return string
*/
public function GetAlias(): string
{
return $this->sAlias !== null ? $this->sAlias : '';
}
/**
* Override a template.
*
* @param string $sPath template path
*
* @return $this
*/
public function OverrideTemplatePath(string $sPath): TemplateDefinitionDto
{
if ($this->IsOverridable() && $sPath !== $this->sPath) {
$this->sPath = $sPath;
$this->bIsOverridden = true;
}
return $this;
}
/**
* Return the overridden flag.
*
* @noinspection PhpUnused
*/
public function IsOverridden(): bool
{
return $this->bIsOverridden;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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\Portal\Service\TemplatesProvider;
/**
* Template provider interface.
*
* This interface is used to register templates in the templates provider service.
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
interface TemplatesProviderInterface
{
/**
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister $oTemplatesRegister
*
* @return void
*/
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void;
}

View File

@@ -0,0 +1,323 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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\Portal\Service\TemplatesProvider;
use Combodo\iTop\Portal\Brick\AbstractBrick;
use Combodo\iTop\Portal\Controller\AbstractController;
use Combodo\iTop\Portal\Controller\DefaultController;
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
use Exception;
use ReflectionClass;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
/**
* Service responsible for managing portal templates.
*
* The templates provider interface allows any provider to register templates for the portal.
* The templates registered may be overridden by the portal configuration and for a specific instance (brick instance).
*
* Templates are defined in module_design properties section, under the templates key.
* The layouts for home and default layout still allow to be defined in the portal configuration.
* Otherwise, the templates for providers are defined as follows:
* <template id="{class implementing TemplatesProviderInterface}:{template_id}">{path to template}</template>
*
* Templates are store in register object.
* This register is cached.
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
class TemplatesProviderService
{
/** @var \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister templates register */
protected TemplatesRegister $oTemplateRegister;
/** @var array instances overridden templates paths */
protected array $aInstancesOverriddenTemplatesPaths = [];
/**
* TemplatesService constructor.
*
* @param array $aCombodoPortalInstanceConf configuration for the current portal instance
* @param \Symfony\Contracts\Cache\CacheInterface $templatesCachePool cache pool for templates
*
* @throws \Psr\Cache\InvalidArgumentException
*/
public function __construct(array $aCombodoPortalInstanceConf, CacheInterface $templatesCachePool)
{
// template register cache
$this->oTemplateRegister = $templatesCachePool->get('portal_templates_register', function (ItemInterface $item) use ($aCombodoPortalInstanceConf): TemplatesRegister {
// initialize register
return $this->InitializeTemplatesRegister($aCombodoPortalInstanceConf);
});
// brick should be able to give the templates with GetTileTemplate, GetPageTemplate (so it needs to know the service)
// a more elegant way would be to use a controller to achieve this
AbstractBrick::SetTemplatesProviderService($this);
}
/**
* Initialize templates register.
* Store the UI version defined in portal instance configuration.
* Iterate throw TemplatesProviderInterface implementations to register templates.
* Override templates with portal instance configuration.
*
* @throws \ReflectionException
*/
private function InitializeTemplatesRegister(array $aCombodoPortalInstanceConf): TemplatesRegister
{
// UI version
$sUIVersion = 'unset';
if (isset($aCombodoPortalInstanceConf['properties']['ui_version'])) {
$sUIVersion = $aCombodoPortalInstanceConf['properties']['ui_version'];
}
// create template register
$oTemplateRegister = new TemplatesRegister($sUIVersion);
// search for templates providers
// only non-abstract classes are discovered.
// classes implementing the interface needs to call the parent to ensure abstracted class levels templates are registered.
$oTemplatesProviders = InterfaceDiscovery::GetInstance()->FindItopClasses(TemplatesProviderInterface::class);
// register default templates...
foreach ($oTemplatesProviders as $oTemplateProvider) {
$oTemplateProvider::RegisterTemplates($oTemplateRegister);
}
// overrides the templates declared in portal configuration...
foreach ($aCombodoPortalInstanceConf['properties']['templates'] as $sKey => $oValue) {
switch ($sKey) {
case 'layout': // legacy configuration
$oTemplateDefinition = $oTemplateRegister->GetTemplateDefinition(AbstractController::class, 'page');
$oTemplateDefinition->OverrideTemplatePath($oValue);
break;
case 'home': // legacy configuration
$oTemplateDefinition = $oTemplateRegister->GetTemplateDefinition(DefaultController::class, 'home');
$oTemplateDefinition->OverrideTemplatePath($oValue);
break;
default:
if (is_array($oValue)) {
foreach ($oValue as $sTemplateId => $sTemplatePath) {
$oTemplateDefinition = $oTemplateRegister->GetTemplateDefinition($sKey, $sTemplateId);
$oTemplateDefinition?->OverrideTemplatePath($sTemplatePath);
}
}
break;
}
}
return $oTemplateRegister;
}
/**
* Override an object instance template path.
*
* @param object $oObject object instance
* @param string $sTemplateId the template id
* @param string $sTemplatePath the template path
*
* @return $this
*/
public function OverrideInstanceTemplatePath(object $oObject, string $sTemplateId, string $sTemplatePath): TemplatesProviderService
{
// get object UUID
$sObjectId = spl_object_id($oObject);
// initialize overridden object templates and information
if (array_key_exists($sObjectId, $this->aInstancesOverriddenTemplatesPaths) === false) {
$this->aInstancesOverriddenTemplatesPaths[$sObjectId] = [];
$this->aInstancesOverriddenTemplatesPaths[$sObjectId]['templates'] = [];
// friendly id
$sId = $sObjectId;
if ($oObject instanceof AbstractBrick) {
$sId = $oObject->GetId();
}
// store object information
$this->aInstancesOverriddenTemplatesPaths[$sObjectId]['info'] = [
'class' => get_class($oObject),
'id' => $sId,
];
}
// store template path
$this->aInstancesOverriddenTemplatesPaths[$sObjectId]['templates'][$sTemplateId] = $sTemplatePath;
return $this;
}
/**
* Get a template path.
*
* @param string $sProviderClass the templates provider class
* @param string $sTemplateId the template id
* @param bool $bIsInitial
*
* @return string|null
* @throws \ReflectionException
*/
public function GetTemplatePath(string $sProviderClass, string $sTemplateId, bool $bIsInitial = false): ?string
{
if ($this->oTemplateRegister->IsProviderExists($sProviderClass)) {
// I
// SERVICE DECLARATION
// the provider class is known by service
// the class register its templates with service
// search for the template definition
$oTemplateDefinition = $this->oTemplateRegister->GetTemplateDefinition($sProviderClass, $sTemplateId);
// return the template path
return $oTemplateDefinition?->GetPath($bIsInitial);
} else {
// II
// LEGACY DECLARATION
// the provider class is unknown by service
// the class register its templates with legacy constants
return $this->GetLegacyTemplatePath($sProviderClass, $sTemplateId);
}
}
/**
* @param string $sProviderClass
* @param string $sTemplateId
*
* @return string|null
* @throws \ReflectionException
*/
private function GetLegacyTemplatePath(string $sProviderClass, string $sTemplateId): ?string
{
// reflexion for class
$oReflexion = new ReflectionClass($sProviderClass);
// class defined constants
$aClassDefinedConstants = array_diff($oReflexion->getConstants(), $oReflexion->getParentClass()->getConstants());
// return the constant if exists
return match ($sTemplateId) {
'page' => array_key_exists('DEFAULT_PAGE_TEMPLATE_PATH', $aClassDefinedConstants) ? $oReflexion->getConstant('DEFAULT_PAGE_TEMPLATE_PATH') : null,
'tile' => array_key_exists('DEFAULT_TILE_TEMPLATE_PATH', $aClassDefinedConstants) ? $oReflexion->getConstant('DEFAULT_TILE_TEMPLATE_PATH') : null,
default => null,
};
}
/**
* Get a provider instance template path.
*
* @param object $oObject
* @param string $sTemplateId
*
* @return string|null
*
*/
public function GetProviderInstanceTemplatePath(object $oObject, string $sTemplateId): ?string
{
// object UUID
$sObjectId = spl_object_id($oObject);
// get the provider instance class name
$sCurrentClass = get_class($oObject);
// get template definition if it exists
$oTemplateDefinition = $this->oTemplateRegister->GetTemplateDefinition($sCurrentClass, $sTemplateId);
$sId = $oTemplateDefinition != null ? $oTemplateDefinition->GetId() : $sTemplateId;
// if instance override exists, return it
if (array_key_exists($sObjectId, $this->aInstancesOverriddenTemplatesPaths)
&& array_key_exists($sId, $this->aInstancesOverriddenTemplatesPaths[$sObjectId]['templates'])) {
return $this->aInstancesOverriddenTemplatesPaths[$sObjectId]['templates'][$sId];
}
// now, we search in class hierarchy for a template
do {
$oParent = null;
try {
// get template path for current class
$sTemplate = $this->GetTemplatePath($sCurrentClass, $sTemplateId);
if ($sTemplate !== null) {
return $sTemplate;
}
// no template defined at this level, try parent class
$oReflexion = new ReflectionClass($sCurrentClass);
$oParent = $oReflexion->getParentClass();
if ($oParent) {
$sCurrentClass = $oReflexion->getParentClass()->getName();
}
}
catch (Exception) {
}
} while ($oParent); // continue while parent class exists
return null; // no template found
}
/**
* Return the register.
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister
*/
public function GetRegister(): TemplatesRegister
{
return $this->oTemplateRegister;
}
/**
* Return instances overridden templates paths.
*
* @return array
*/
public function GetInstancesOverriddenTemplatesPaths(): array
{
return $this->aInstancesOverriddenTemplatesPaths;
}
/**
* Returns true if brick template path is overridden.
*
* @param object $oObject object instance
* @param string $sTemplateId template identifier
*
* @return bool
*/
public function HasInstanceOverriddenTemplate(object $oObject, string $sTemplateId): bool
{
// object UUID
$sObjectId = spl_object_id($oObject);
return (array_key_exists($sObjectId, $this->aInstancesOverriddenTemplatesPaths)
&& array_key_exists($sTemplateId, $this->aInstancesOverriddenTemplatesPaths[$sObjectId]['templates']));
}
}

View File

@@ -0,0 +1,133 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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\Portal\Service\TemplatesProvider;
/**
* Template register.
*
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
class TemplatesRegister
{
/** @var array Templates definitions (possibly altered by portal configuration) */
private array $aTemplatesDefinitions = [];
public function __construct(private string $sTemplateUIVersion = 'unset')
{
}
/**
* @return string
*/
public function GetUIVersion(): string
{
return $this->sTemplateUIVersion;
}
public function IsProviderExists(string $sProviderId): bool
{
return array_key_exists($sProviderId, $this->aTemplatesDefinitions);
}
public function IsTemplateExists(string $sProviderId, string $sTemplateId): bool
{
return array_key_exists($sProviderId, $this->aTemplatesDefinitions) && array_key_exists($sTemplateId, $this->aTemplatesDefinitions[$sProviderId]);
}
/**
* Register templates.
*
* @param string $sProviderId the templates provider id
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto ...$aTemplatesDefinitions
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister
*/
public function RegisterTemplates(string $sProviderId, TemplateDefinitionDto...$aTemplatesDefinitions): TemplatesRegister
{
// prevent child classes to erase parent templates
if (array_key_exists($sProviderId, $this->aTemplatesDefinitions)) {
return $this;
}
// register templates...
$this->aTemplatesDefinitions[$sProviderId] = [];
foreach ($aTemplatesDefinitions as $oTemplateDefinition) {
$this->aTemplatesDefinitions[$sProviderId][$oTemplateDefinition->GetId()] = $oTemplateDefinition;
}
return $this;
}
/**
* Get a template definition.
*
* @param string $sProviderId the templates provider id
* @param string $sTemplateId the template id
*
* @return TemplateDefinitionDto|null
*/
public function GetTemplateDefinition(string $sProviderId, string $sTemplateId): ?TemplateDefinitionDto
{
// retrieve template path
if (array_key_exists($sProviderId, $this->aTemplatesDefinitions)) {
// search in template definitions
if (array_key_exists($sTemplateId, $this->aTemplatesDefinitions[$sProviderId])) {
return $this->aTemplatesDefinitions[$sProviderId][$sTemplateId];
}
// search in aliases
foreach ($this->aTemplatesDefinitions[$sProviderId] as $item) {
/** @var \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto $item */
if ($item->GetAlias() === $sTemplateId) {
return $item;
}
}
}
return null;
}
/**
* @param string $sProviderId
*
* @return array
*/
public function GetProviderTemplatesIds(string $sProviderId): array
{
return array_map(fn($oTemplateDefinition) => $oTemplateDefinition->GetId(), $this->aTemplatesDefinitions[$sProviderId] ?? ['tile', 'page']);
}
/**
* Return templates definitions.
*
* @return array
*/
public function GetTemplatesDefinitions(): array
{
return $this->aTemplatesDefinitions;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Copyright (C) 2013-2023 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\Portal\Twig;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* New Twig function useful for extending or including template handled by templates provider service.
*
* @package Combodo\iTop\Portal\Twig
* @since 3.2.1
*/
class TemplatesTwigExtension extends AbstractExtension
{
const DEFAULT_PROVIDER_CLASS = 'Combodo\\iTop\\Portal\\Controller\\AbstractController';
public function __construct(private readonly TemplatesProviderService $oTemplatesService)
{
}
/** @inheritdoc */
public function getFunctions(): array
{
return [
new TwigFunction('template', [$this, 'GetTemplate'], ['id' => null, 'provider' => null, 'provider_instance' => null]),
new TwigFunction('template_initial', [$this, 'GetInitialTemplate'], ['id' => null, 'provider' => null]),
];
}
/**
* Retrieve the path of the desired template (maybe overridden by configuration or by instance).
*
* @param string $sId template identifier
* @param string $sProviderClass provider class FQN
* @param object|null $oProviderInstance the provider instance
*
* @return string the template path
* @throws \ReflectionException
*/
public function GetTemplate(string $sId, string $sProviderClass = self::DEFAULT_PROVIDER_CLASS, object $oProviderInstance = null): string
{
if ($oProviderInstance === null) {
return $this->oTemplatesService->GetTemplatePath($sProviderClass, $sId);
} else {
return $this->oTemplatesService->GetProviderInstanceTemplatePath($oProviderInstance, $sId);
}
}
/**
* Retrieve the initial path of the desired template (hardcoded).
*
* @param string $sId template identifier
* @param string $sProviderClass provider class FQN
*
* @return string the template path
* @throws \ReflectionException
*/
public function GetInitialTemplate(string $sId, string $sProviderClass = self::DEFAULT_PROVIDER_CLASS): string
{
return $this->oTemplatesService->GetTemplatePath($sProviderClass, $sId, true);
}
}

View File

@@ -1,5 +1,4 @@
{# itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig #}
{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick') %}
{% block pPageBodyClass %}{{ parent() }} page_aggregate_page_brick home{% endblock %}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/browse/layout.html.twig #}
{# Browse brick base layout #}
{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick') %}
{% block pPageBodyClass %}{{ parent() }} page_browse_brick page_browse_brick_as_{{ sBrowseMode }}{% endblock %}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig #}
{# Browse brick list mode layout #}
{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick', oBrick) %}
{% block bBrowseMainContent%}
<table id="brick-content-table" class="object-list table table-striped table-bordered responsive" cellspacing="0" width="100%">
@@ -11,8 +9,8 @@
{% block pPageLiveScripts %}
{{ parent() }}
<script type="text/javascript">
<script type="text/javascript">
var sBrowseMode = '{{ sBrowseMode }}';
var sDataLoading = '{{ sDataLoading }}';
var oLevelsProperties = {{ aLevelsProperties|raw }};
@@ -22,8 +20,8 @@
var iSearchThrottle = 600;
var oKeyTimeout;
var aKeyTimeoutFilteredKeys = [9, 16, 17, 18, 19, 27, 33, 34, 35, 36, 37, 38, 39, 40]; // Tab, Shift, Ctrl, Alt, Pause, Esc, Page Up/Down, Home, End, Left/Up/Right/Down arrows
// Show a loader inside the table
// Show a loader inside the table
var showTableLoader = function(oElem)
{
oElem.children('tbody').html('<tr><td class="datatables_overlay" colspan="100">' + $('#page_overlay').html() + '</td></tr>');
@@ -32,8 +30,8 @@
var getColumnsDefinition = function()
{
var aColumnsDefinition = [];
for(sKey in oLevelsProperties)
for (sKey in oLevelsProperties)
{
// Level main column
aColumnsDefinition.push({
@@ -189,8 +187,8 @@
},
},
});
// Level's fields columns
// Level's fields columns
if(oLevelsProperties[sKey].fields !== undefined)
{
for(var i in oLevelsProperties[sKey].fields)
@@ -231,15 +229,15 @@
}
}
}
return aColumnsDefinition;
return aColumnsDefinition;
};
$(document).ready(function()
$(document).ready(function ()
{
showTableLoader($('#brick-content-table'));
// Note : Those options should be externalized in an library so we can use them on any DataTables for the portal.
// Note : Those options should be externalized in an library so we can use them on any DataTables for the portal.
// We would just have to override / complete the necessary elements
oTable = $('#brick-content-table').DataTable({
"language": {
@@ -331,8 +329,8 @@
// Note : The '.off()' call is to unbind event from DataTables that where triggered before we could intercept anything
$('#brick-content-table_filter input').off().on('keyup', function(event){
var me = this;
// We trigger the search only if those keys where not pressed
// We trigger the search only if those keys where not pressed
if(aKeyTimeoutFilteredKeys.indexOf(event.which) < 0)
{
clearTimeout(oKeyTimeout);
@@ -348,8 +346,8 @@
showTableLoader($(this));
}
});
// Auto collapse item actions popup
// Auto collapse item actions popup
$('body').on('click', function(){
$('table .item-action-wrapper.collapse.in').collapse('hide');
});

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig #}
{# Browse brick mosaic mode layout #}
{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick', oBrick) %}
{% block bBrowseMainContent %}
<div id="brick_content_mosaic">

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig #}
{# Browse brick tree mode layout #}
{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick', oBrick) %}
{#
Documentation :

View File

@@ -1,44 +0,0 @@
{# itop-portal-base/portal/templates/bricks/create/layout.html.twig #}
{# Create brick base layout #}
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalTitle %}
{{ sPageTitle|dict_s }}
{% endblock %}
{% block pModalBody %}
<p>{{ 'Brick:Portal:Create:ChooseType'|dict_s }}</p>
<ul id="{{ sLeafClassesListId }}">
{% for aLeafClass in aLeafClasses %}
<li><a href="#" data-target-class="{{ aLeafClass.id }}">{{ aLeafClass.name }}</a></li>
{% endfor %}
</ul>
<script type="text/javascript">
$(document).ready(function(){
$('#{{ sLeafClassesListId }} a').off('click').on('click', function(oEvent){
oEvent.preventDefault();
// Preparing target class url
var sUrl = '{{ app['url_generator'].generate('p_object_create', {sObjectClass : '-sObjectClass-'})|raw }}';
sUrl = sUrl.replace(/-sObjectClass-/, $(this).attr('data-target-class') );
sUrl = CombodoGlobalToolbox.AddParameterToUrl(sUrl, 'ar_token', '{{ ar_token }}');
// Creating a new modal
CombodoModal.OpenModal({
base_modal: {
usage: 'replace',
selector: $(this).closest('.modal'),
},
content: {
endpoint: sUrl,
},
});
});
});
</script>
{% endblock %}
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:Button:Cancel'|dict_s }}</button>
{% endblock %}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/layout.html.twig #}
{# Brick base layout #}
{% extends app['combodo.portal.instance.conf'].properties.templates.layout %}
{% extends template('page') %}
{% block pPageTitle %}
{# Overloading the default template's title to show the brick's title #}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig #}
{# Manage brick base layout #}
{% extends 'itop-portal-base/portal/templates/bricks/manage/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\ManageBrick', oBrick) %}
{% block pPageBodyClass %}{{ parent() }} page_manage_brick{% endblock %}
@@ -8,7 +6,7 @@
{% block pMainContentHolder %}
<div class="panel panel-default">
<div class="panel-body">
{% include 'itop-portal-base/portal/templates/bricks/manage/mode-' ~ sDisplayMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
{% include template(sDisplayMode, 'Combodo\\iTop\\Portal\\Brick\\ManageBrick', oBrick) with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
</div>
</div>
{% endblock %}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig #}
{# Manage brick base layout #}
{% extends 'itop-portal-base/portal/templates/bricks/manage/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\ManageBrick', oBrick) %}
{% block pPageBodyClass %}{{ parent() }} page_manage_brick{% endblock %}

View File

@@ -1,5 +1,4 @@
{# itop-portal-base/portal/templates/bricks/manage/layout.html.twig #}
{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick') %}
{% block pMainHeaderTitle %}{{ oBrick.GetTitle()|dict_s }} {% if iCount >= 0 %} ({{ iCount }}){% endif %} {% endblock %}
@@ -9,7 +8,7 @@
{% for sDisplay in oBrick.GetAvailablesDisplayModes %}
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': sDisplay}) }}{% if app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][oBrick.GetRouteName]['hash'] }}{% endif %}"
id="btn_tab_for_{{ sDisplay }}"
class="btn btn-default {% if sDisplay == oBrick.GetPresentationDataForTileMode(sDisplayMode).layoutDisplayMode %}active{% endif %}">
class="btn btn-default {% if sDisplay == sDisplayMode %}active{% endif %}">
{{ ('Brick:Portal:Manage:DisplayMode:' ~ sDisplay)|dict_s }}
</a>
{% endfor %}

View File

@@ -10,9 +10,9 @@
id="brick-{{ oBrick.GetId }}"
data-brick-id="{{ oBrick.GetId }}">
<div>
<div class="tile_title"><span class="icon fas fa-{{ oBrick.GetDecorationCssClass }}"></span> {{ oBrick.GetTitle()|dict_s }}
<div class="tile_title"><span class="icon {{ oBrick.GetDecorationClassHome }}"></span> {{ oBrick.GetTitle()|dict_s }}
({{ iCount }})</span> </div>
{% include 'itop-portal-base/portal/templates/bricks/manage/mode-' ~ oBrick.GetTileMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
{% include template(oBrick.GetTileMode, 'Combodo\\iTop\\Portal\\Brick\\ManageBrick', oBrick) with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
</div>
</a>
{% endblock %}

View File

@@ -11,7 +11,7 @@
data-brick-id="{{ oBrick.GetId }}">
<div style="background-color:#fff;padding:0.25em;">
<div class="tile_title">
<span class="icon fas fa-signal fa-rotate-270"></span> {{ oBrick.GetTitle()|dict_s }}
<span class="icon {{ oBrick.GetDecorationClassHome }}"></span> {{ oBrick.GetTitle()|dict_s }}
({{ iCount }})</span> </div>
<table class="table table-sm">
<thead>

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/object/layout.html.twig #}
{# Object brick base layout #}
{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %}
{% extends template('page') %}
{% block pPageBodyClass %}{{ parent() }} page_object_brick page_object_brick_as_{{ sMode }}{% endblock %}
@@ -27,7 +25,7 @@
{% block pMainContentHolder%}
<div class="panel panel-default">
<div class="panel-body">
{% include 'itop-portal-base/portal/templates/bricks/object/mode_' ~ sMode ~ '.html.twig' %}
{% include template(sMode, 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
</div>
</div>
{% endblock %}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/object/modal.html.twig #}
{# Object brick base layout #}
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% extends template('modal') %}
{% block pModalTitle %}
{% if form.title_clipboard_text is defined %}
@@ -19,5 +17,5 @@
{% endblock %}
{% block pModalBody %}
{% include 'itop-portal-base/portal/templates/bricks/object/mode_' ~ sMode ~ '.html.twig' with {tIsModal: true} %}
{% include template(sMode, 'Combodo\\iTop\\Portal\\Controller\\ObjectController') with {tIsModal: true} %}
{% endblock %}

View File

@@ -1,5 +1,3 @@
{# itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig #}
{# Object brick apply stimulus layout #}
{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %}
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}

View File

@@ -1,5 +1,3 @@
{# itop-portal-base/portal/templates/bricks/object/mode_create.html.twig #}
{# Object brick edit layout #}
{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %}
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/object/mode_view.html.twig #}
{# Object brick view layout #}
{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %}
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}

View File

@@ -1,6 +1,4 @@
{# itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig #}
{# User profile brick base layout #}
{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %}
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick') %}
{% if sTab == "" %}
{% set sTab = "user-info" %}

View File

@@ -0,0 +1,170 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{# toolbar text & icon #}
{% set icon %}
<span class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor"><path d="M192 64l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32zM82.7 207c-15.3 8.8-20.5 28.4-11.7 43.7l32 55.4c8.8 15.3 28.4 20.5 43.7 11.7l55.4-32c15.3-8.8 20.5-28.4 11.7-43.7l-32-55.4c-8.8-15.3-28.4-20.5-43.7-11.7L82.7 207zM288 192c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0zm64 160c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0zM160 384l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32zM32 352c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0z"/></svg>
</span>
<span class="sf-toolbar-label">Portal{% if collector.GetTemplatesCount.overrides_count > 0 %} <span style=" color: var(--sf-toolbar-yellow-600);"> Overrides</span>{% endif %}</span>
{% endset %}
{# toolbar panel #}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Templates registered</b><span class="sf-toolbar-status">{{ collector.GetTemplatesCount.count }}</span>
</div>
{% if collector.GetTemplatesCount.overrides_count %}
<div class="sf-toolbar-info-piece">
<b>Templates overridden</b><span class="sf-toolbar-status">{{ collector.GetTemplatesCount.overrides_count }}</span>
</div>
{% endif %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }}
{% endblock %}
{% block head %}
{{ parent() }}
<style>
.overridden_value {
color: var(--color-link);
}
.old_value {
color: grey;
text-decoration: line-through;
}
</style>
{% endblock %}
{% block menu %}
{# menu #}
<span class="label label-status-none">
<span class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor"><path d="M192 64l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32zM82.7 207c-15.3 8.8-20.5 28.4-11.7 43.7l32 55.4c8.8 15.3 28.4 20.5 43.7 11.7l55.4-32c15.3-8.8 20.5-28.4 11.7-43.7l-32-55.4c-8.8-15.3-28.4-20.5-43.7-11.7L82.7 207zM288 192c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0zm64 160c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0zM160 384l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32zM32 352c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0z"/></svg>
</span>
<strong>Portal</strong>
<span class="count">
{{ collector.GetTemplatesCount.count }}
</span>
</span>
{% endblock %}
{% block panel %}
{# title #}
<h2>Templates</h2>
{# metrics #}
<div class="metrics">
{# ui version #}
<div class="metric">
<span class="value">{{ collector.GetUIVersion }}</span>
<span class="label">UI Version</span>
</div>
<div class="metric-divider"></div>
{# templates #}
<div class="metric-group">
<div class="metric">
<span class="value">{{ collector.GetTemplatesCount.count }}</span>
<span class="label">Templates</span>
</div>
<div class="metric">
<span class="value">{{ collector.GetTemplatesCount.overrides_count }}</span>
<span class="label">Overridden</span>
</div>
</div>
{# additional info #}
<div class="metric-divider"></div>
<div class="metric-group">
<div class="metric">
<span class="value">{{ collector.GetTemplatesCount.extensions_count }}</span>
<span class="label">Extensions</span>
</div>
<div class="metric">
<span class="value">{{ collector.GetTemplatesCount.providers_count }}</span>
<span class="label">Providers</span>
</div>
</div>
</div>
{# Instances overloads #}
<h2>Bricks declared templates list</h2>
{# help #}
<p class="help">
Bricks overridden templates are templates defined in brick declarations.
</p>
{% if collector.GetInstancesOverriddenTemplates|length == 0 %}
No instance overridden template.
{% else %}
<table>
<thead>
<tr>
<th>Brick</th>
<th>Class</th>
<th>ID</th>
<th>Template</th>
</tr>
</thead>
{% for instance,item in collector.GetInstancesOverriddenTemplates %}
{% for id,template in item.templates %}
<tr>
<td>{{ item.info.id }}</td>
<td>{{ item.info.class }}</td>
<td>{{ id }}</td>
<td>{{ template }}</td>
</tr>
{% endfor %}
{% endfor %}
</table>
{% endif %}
{# templates list #}
<h2>Templates list</h2>
{# help #}
<p class="help">
Templates doesn't necessary covers all existing templates, only the ones that are registered in the portal.
</p>
<table>
<thead>
<tr>
<th>Provider</th>
<th>ID</th>
<th>Template</th>
<th>Alias</th>
<th>Override</th>
</tr>
</thead>
{% for provider,item in collector.GetTemplatesDefinitions %}
{% for id,template in item %}
<tr {% if template.IsOverridden %}class="overridden_value"{% endif %}>
<td>{{ provider }}</td>
<td>{{ template.Id }}</td>
<td>{{ template.path }}{% if template.IsOverridden %}
<div class="old_value">{{ template.GetPath(true) }}</div>{% endif %}</td>
<td>{{ template.Alias }}</td>
<td>{% if template.IsOverridable %}
{% if template.IsOverridden %}
Yes
{% else %}
No
{% endif %}
{% else %}
Not allowed
{% endif %}
</td>
</tr>
{% endfor %}
{% endfor %}
</table>
{% endblock %}

View File

@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b::getLoader();
return ComposerAutoloaderInitd751713988987e9331980363e24189ce::getLoader();

View File

@@ -14,7 +14,6 @@ return array(
'Combodo\\iTop\\Portal\\Brick\\CreateBrick' => $baseDir . '/src/Brick/CreateBrick.php',
'Combodo\\iTop\\Portal\\Brick\\FilterBrick' => $baseDir . '/src/Brick/FilterBrick.php',
'Combodo\\iTop\\Portal\\Brick\\ManageBrick' => $baseDir . '/src/Brick/ManageBrick.php',
'Combodo\\iTop\\Portal\\Brick\\ObjectBrick' => $baseDir . '/src/Brick/ObjectBrick.php',
'Combodo\\iTop\\Portal\\Brick\\PortalBrick' => $baseDir . '/src/Brick/PortalBrick.php',
'Combodo\\iTop\\Portal\\Brick\\PropertyNotFoundException' => $baseDir . '/src/Brick/PropertyNotFoundException.php',
'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick' => $baseDir . '/src/Brick/UserProfileBrick.php',
@@ -28,6 +27,7 @@ return array(
'Combodo\\iTop\\Portal\\Controller\\ObjectController' => $baseDir . '/src/Controller/ObjectController.php',
'Combodo\\iTop\\Portal\\Controller\\SessionMessageController' => $baseDir . '/src/Controller/SessionMessageController.php',
'Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController' => $baseDir . '/src/Controller/UserProfileBrickController.php',
'Combodo\\iTop\\Portal\\DataCollector\\PortalCollector' => $baseDir . '/src/DataCollector/PortalCollector.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\AbstractConfiguration' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Basic' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php',
@@ -60,6 +60,10 @@ return array(
'Combodo\\iTop\\Portal\\Kernel' => $baseDir . '/src/Kernel.php',
'Combodo\\iTop\\Portal\\Routing\\ItopExtensionsExtraRoutes' => $baseDir . '/src/Routing/ItopExtensionsExtraRoutes.php',
'Combodo\\iTop\\Portal\\Routing\\UrlGenerator' => $baseDir . '/src/Routing/UrlGenerator.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplateDefinitionDto' => $baseDir . '/src/Service/TemplatesProvider/TemplateDefinitionDto.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesProviderInterface' => $baseDir . '/src/Service/TemplatesProvider/TemplatesProviderInterface.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesProviderService' => $baseDir . '/src/Service/TemplatesProvider/TemplatesProviderService.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesRegister' => $baseDir . '/src/Service/TemplatesProvider/TemplatesRegister.php',
'Combodo\\iTop\\Portal\\Twig\\AppExtension' => $baseDir . '/src/Twig/AppExtension.php',
'Combodo\\iTop\\Portal\\Twig\\AppGlobal' => $baseDir . '/src/Twig/AppGlobal.php',
'Combodo\\iTop\\Portal\\Twig\\AppVariable' => $baseDir . '/src/Twig/AppVariable.php',
@@ -67,6 +71,7 @@ return array(
'Combodo\\iTop\\Portal\\Twig\\CurrentUserAccessor' => $baseDir . '/src/Twig/CurrentUserAccessor.php',
'Combodo\\iTop\\Portal\\Twig\\PortalBlockExtension' => $baseDir . '/src/Twig/PortalBlockExtension.php',
'Combodo\\iTop\\Portal\\Twig\\PortalTwigContext' => $baseDir . '/src/Twig/PortalTwigContext.php',
'Combodo\\iTop\\Portal\\Twig\\TemplatesTwigExtension' => $baseDir . '/src/Twig/TemplatesTwigExtension.php',
'Combodo\\iTop\\Portal\\UrlMaker\\AbstractPortalUrlMaker' => $baseDir . '/src/UrlMaker/AbstractPortalUrlMaker.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractStringVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractStringVariableAccessor.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractVariableAccessor.php',

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b
class ComposerAutoloaderInitd751713988987e9331980363e24189ce
{
private static $loader;
@@ -22,14 +22,13 @@ class ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitd751713988987e9331980363e24189ce', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitd751713988987e9331980363e24189ce', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitd751713988987e9331980363e24189ce::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
return $loader;

View File

@@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
class ComposerStaticInitd751713988987e9331980363e24189ce
{
public static $prefixLengthsPsr4 = array (
'C' =>
@@ -34,7 +34,6 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
'Combodo\\iTop\\Portal\\Brick\\CreateBrick' => __DIR__ . '/../..' . '/src/Brick/CreateBrick.php',
'Combodo\\iTop\\Portal\\Brick\\FilterBrick' => __DIR__ . '/../..' . '/src/Brick/FilterBrick.php',
'Combodo\\iTop\\Portal\\Brick\\ManageBrick' => __DIR__ . '/../..' . '/src/Brick/ManageBrick.php',
'Combodo\\iTop\\Portal\\Brick\\ObjectBrick' => __DIR__ . '/../..' . '/src/Brick/ObjectBrick.php',
'Combodo\\iTop\\Portal\\Brick\\PortalBrick' => __DIR__ . '/../..' . '/src/Brick/PortalBrick.php',
'Combodo\\iTop\\Portal\\Brick\\PropertyNotFoundException' => __DIR__ . '/../..' . '/src/Brick/PropertyNotFoundException.php',
'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick' => __DIR__ . '/../..' . '/src/Brick/UserProfileBrick.php',
@@ -48,6 +47,7 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
'Combodo\\iTop\\Portal\\Controller\\ObjectController' => __DIR__ . '/../..' . '/src/Controller/ObjectController.php',
'Combodo\\iTop\\Portal\\Controller\\SessionMessageController' => __DIR__ . '/../..' . '/src/Controller/SessionMessageController.php',
'Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController' => __DIR__ . '/../..' . '/src/Controller/UserProfileBrickController.php',
'Combodo\\iTop\\Portal\\DataCollector\\PortalCollector' => __DIR__ . '/../..' . '/src/DataCollector/PortalCollector.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\AbstractConfiguration' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Basic' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php',
'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php',
@@ -80,6 +80,10 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
'Combodo\\iTop\\Portal\\Kernel' => __DIR__ . '/../..' . '/src/Kernel.php',
'Combodo\\iTop\\Portal\\Routing\\ItopExtensionsExtraRoutes' => __DIR__ . '/../..' . '/src/Routing/ItopExtensionsExtraRoutes.php',
'Combodo\\iTop\\Portal\\Routing\\UrlGenerator' => __DIR__ . '/../..' . '/src/Routing/UrlGenerator.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplateDefinitionDto' => __DIR__ . '/../..' . '/src/Service/TemplatesProvider/TemplateDefinitionDto.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesProviderInterface' => __DIR__ . '/../..' . '/src/Service/TemplatesProvider/TemplatesProviderInterface.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesProviderService' => __DIR__ . '/../..' . '/src/Service/TemplatesProvider/TemplatesProviderService.php',
'Combodo\\iTop\\Portal\\Service\\TemplatesProvider\\TemplatesRegister' => __DIR__ . '/../..' . '/src/Service/TemplatesProvider/TemplatesRegister.php',
'Combodo\\iTop\\Portal\\Twig\\AppExtension' => __DIR__ . '/../..' . '/src/Twig/AppExtension.php',
'Combodo\\iTop\\Portal\\Twig\\AppGlobal' => __DIR__ . '/../..' . '/src/Twig/AppGlobal.php',
'Combodo\\iTop\\Portal\\Twig\\AppVariable' => __DIR__ . '/../..' . '/src/Twig/AppVariable.php',
@@ -87,6 +91,7 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
'Combodo\\iTop\\Portal\\Twig\\CurrentUserAccessor' => __DIR__ . '/../..' . '/src/Twig/CurrentUserAccessor.php',
'Combodo\\iTop\\Portal\\Twig\\PortalBlockExtension' => __DIR__ . '/../..' . '/src/Twig/PortalBlockExtension.php',
'Combodo\\iTop\\Portal\\Twig\\PortalTwigContext' => __DIR__ . '/../..' . '/src/Twig/PortalTwigContext.php',
'Combodo\\iTop\\Portal\\Twig\\TemplatesTwigExtension' => __DIR__ . '/../..' . '/src/Twig/TemplatesTwigExtension.php',
'Combodo\\iTop\\Portal\\UrlMaker\\AbstractPortalUrlMaker' => __DIR__ . '/../..' . '/src/UrlMaker/AbstractPortalUrlMaker.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractStringVariableAccessor' => __DIR__ . '/../..' . '/src/VariableAccessor/AbstractStringVariableAccessor.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractVariableAccessor' => __DIR__ . '/../..' . '/src/VariableAccessor/AbstractVariableAccessor.php',
@@ -98,9 +103,9 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitd751713988987e9331980363e24189ce::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd751713988987e9331980363e24189ce::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd751713988987e9331980363e24189ce::$classMap;
}, null, ClassLoader::class);
}

View File

@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => 'c9beba0ceadf11c644a8eb1b254efff9b17d804c',
'reference' => '4f6d514694b9813b9a5ebda4c38f29b4847ff9c3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => 'c9beba0ceadf11c644a8eb1b254efff9b17d804c',
'reference' => '4f6d514694b9813b9a5ebda4c38f29b4847ff9c3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@@ -16,6 +16,7 @@
<module_design id="itop-portal" xsi:type="portal" _delta="define">
<properties>
<name>portal:itop-portal</name>
<ui_version>2017</ui_version>
<!-- Can be either a fileref or a relative path to the file (To be tested). Takes over env-xxx/branding/portal-logo.png -->
<!-- Priority order is <logo> from xml > env-xxx/branding/portal-logo.png > /images/logo-itop-dark-bg.svg -->
<!--<fileref ref="brt_6a2be154b2a62659d3332c513bdad715" />-->

View File

@@ -32,7 +32,7 @@ SetupWebPage::AddModule(
// Documentation
//
'doc.manual_setup' => 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().':admin:cron',
'doc.manual_setup' => '',
'doc.more_information' => '',
// Default settings

View File

@@ -87,7 +87,7 @@ class ExtensionInstallation extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("label", array("allowed_values"=>null, "sql"=>"label", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("source", array("allowed_values"=>null, "sql"=>"source", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("installed", array("allowed_values"=>null, "sql"=>"installed", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("installed", array("allowed_values"=>null, "sql"=>"installed", "default_value"=>'NOW()', "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists

View File

@@ -109,7 +109,11 @@ class InterfaceDiscovery
// guess all the autoload class maps from the extensions
$aAutoloadClassMaps = glob(APPROOT.'env-'.utils::GetCurrentEnvironment().'/*/vendor/composer/autoload_classmap.php');
if($aAutoloadClassMaps === false) {
$aAutoloadClassMaps = [];
}
$aAutoloadClassMaps[] = APPROOT.'lib/composer/autoload_classmap.php';
$aAutoloadClassMaps[] = APPROOT.'env-'.utils::GetCurrentEnvironment().'/itop-portal-base/portal/vendor/composer/autoload_classmap.php';
if ($this->GetCacheMode() === self::CACHE_DYNAMIC) {
$this->oCacheService->Store('InterfaceDiscovery', 'autoload_classmaps', $aAutoloadClassMaps);

View File

@@ -92,7 +92,7 @@ class SynchroDataSource extends cmdbAbstractObject
'depends_on' => array(),
)));
//MetaModel::Init_AddAttribute(new AttributeDateTime("last_synchro_date", array("allowed_values"=>null, "sql"=>"last_synchro_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
//MetaModel::Init_AddAttribute(new AttributeDateTime("last_synchro_date", array("allowed_values"=>null, "sql"=>"last_synchro_date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
// Format: seconds (int)
MetaModel::Init_AddAttribute(new AttributeDuration('full_load_periodicity', array(
@@ -2029,7 +2029,7 @@ class SynchroReplica extends DBObject implements iDisplay
MetaModel::Init_AddAttribute(new AttributeDateTime('status_last_seen', array(
'allowed_values' => null,
'sql' => 'status_last_seen',
'default_value' => '',
'default_value' => 'NOW()',
'is_null_allowed' => false,
'depends_on' => array(),
)));

View File

@@ -246,15 +246,12 @@ PHP
// Given
$oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
self::assertNull($defaultValue, 'Empty default value for DateTime attribute should give null default value');
self::assertEmpty($oField->GetCurrentValue(), 'Empty default value for DateTime attribute should give empty form field');
}
public function testDateEmptyDefaultReturnsNullAsDefaultValue()
@@ -262,87 +259,86 @@ PHP
// Given
$oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
self::assertNull($defaultValue, 'Empty default value for Date attribute should give null default value');
self::assertEmpty($oField->GetCurrentValue(), 'Empty default value for DateTime attribute should give empty form field');
}
public function testDateTimeNowAsDefaultGivesCurrentDateAsDefaultValue()
{
// Given
$oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'now', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute = new AttributeDateTime('start_date', [
'sql' => 'start_date',
'is_null_allowed' => false,
'default_value' => 'NOW()',
'allowed_values' => null,
'depends_on' => [],
'always_load_in_tables' => false
]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
$sNow = date($oDateAttribute->GetInternalFormat());
self::assertEquals($sNow, $defaultValue, 'Now as default value for DateTime attribute should give current date as default value');
self::assertEquals($sNow, $oField->GetCurrentValue(), 'Now as default value for DateTime attribute should give current date as form field');
}
public function testDateNowAsDefaultGivesCurrentDateAsDefaultValue()
{
// Given
$oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'now', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute = new AttributeDate('start_date', [
'sql' => 'start_date',
'is_null_allowed' => false,
'default_value' => 'NOW()',
'allowed_values' => null,
'depends_on' => [],
'always_load_in_tables' => false
]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
$sNow = date($oDateAttribute->GetInternalFormat());
self::assertEquals($sNow, $defaultValue, 'Now as default value for Date attribute should give current date as default value');
self::assertEquals($sNow, $oField->GetCurrentValue(), 'Now as default value for Date attribute should give current date as form field');
}
public function testDateTimeIntervalAsDefaultGivesCorrectDateAsDefaultValue()
{
// Given
$oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '+1day', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute = new AttributeDateTime('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
$oDate = new \DateTimeImmutable('+1day');
$sExpected = $oDate->format($oDateAttribute->GetInternalFormat());
self::assertEquals($sExpected, $defaultValue, 'Interval as default value for DateTime attribute should give correct date as default value');
self::assertEquals($sExpected, $oField->GetCurrentValue(), 'Interval as default value for DateTime attribute should give correct date as form field');
}
public function testDateIntervalAsDefaultGivesCorrectDateAsDefaultValue()
{
// Given
$oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => '+1day', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute = new AttributeDate('start_date', ['sql' => 'start_date', 'is_null_allowed' => false, 'default_value' => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]);
$oDateAttribute->SetHostClass('WorkOrder');
$oWorkOrder = MetaModel::NewObject('WorkOrder');
//When
$defaultValue = $oDateAttribute->GetDefaultValue();
$oField = $oDateAttribute->MakeFormField($oWorkOrder);
// Then
$oDate = new \DateTimeImmutable('+1day');
$sExpected = $oDate->format($oDateAttribute->GetInternalFormat());
self::assertEquals($sExpected, $defaultValue, 'Interval as default value for Date attribute should give correct date as default value');
self::assertEquals($sExpected, $oField->GetCurrentValue(), 'Interval as default value for Date attribute should give correct date as form field');
}
}

View File

@@ -131,6 +131,8 @@ class ExpressionEvaluateTest extends ItopDataTestCase
array("'the quick brown dog' LIKE '%QU_ICK%'", 0),
array('"400 (km/h)" LIKE "400%"', 1),
array('"400 (km/h)" LIKE "100%"', 0),
array('"400 (km/h)" NOT LIKE "400%"', 0),
array('"400 (km/h)" NOT LIKE "100%"', 1),
array('"2020-06-12" > "2020-06-11"', 1),
array('"2020-06-12" < "2020-06-11"', 0),
array('" 2020-06-12" > "2020-06-11"', 0), // Leading spaces => a string
@@ -142,7 +144,14 @@ class ExpressionEvaluateTest extends ItopDataTestCase
array('"2020-06-12 00:00:00" = "2020-06-12"', 0),
// IN operator
array("'a' IN ('a')", true),
array("'a' IN ('b')", false),
array("'a' IN ('a', 'b')", true),
array("'z' IN ('a', 'b')", false),
array("'a' NOT IN ('a')", false),
array("'a' NOT IN ('b')", true),
array("'a' NOT IN ('a', 'b')", false),
array("'z' NOT IN ('a', 'b')", true),
// Logical operators
array('0 AND 0', 0),