From 886db5d6ad31b63bc99adc03e68c8b6e6c74c777 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Thu, 9 Jan 2025 10:49:35 +0100 Subject: [PATCH 1/7] =?UTF-8?q?N=C2=B07145=20-=20UI=20-=20Init=20Value=20D?= =?UTF-8?q?ateTime=20and=20Date=20with=20Day=20Time=20(use=20OQL=20syntax?= =?UTF-8?q?=20for=20default=20value)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/attributedef.class.inc.php | 21 +--------- core/event.class.inc.php | 2 +- .../core/AttributeDefinitionTest.php | 40 +++++++++---------- 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index dc29a2289..25846ef42 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -6346,13 +6346,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; } @@ -6438,18 +6431,8 @@ class AttributeDateTime extends AttributeDBField public function GetDefaultValue(DBObject $oHostObject = null) { $sDefaultValue = $this->Get('default_value'); - if (!$this->IsNullAllowed() && utils::IsNotNullOrEmptyString($sDefaultValue)) { - try { - $oDate = new DateTimeImmutable($sDefaultValue); - } - catch (Exception $e) { - IssueLog::Error($e->getMessage(), null, [ - 'class' => get_class($this), - 'name' => $this->GetCode(), - 'stack' => $e->getTraceAsString()]); - return $this->GetNullValue(); - } - + if (utils::IsNotNullOrEmptyString($sDefaultValue)) { + $oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([])); return $oDate->format($this->GetInternalFormat()); } return $this->GetNullValue(); diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 6a731f1cc..4deadbdd6 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -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()))); diff --git a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php index e875fbacb..5b25b455b 100644 --- a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php +++ b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php @@ -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'); } } \ No newline at end of file From bf6277fcd298bb6980cf4206148110fdbde1f920 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Thu, 9 Jan 2025 11:22:28 +0100 Subject: [PATCH 2/7] =?UTF-8?q?N=C2=B07145=20-=20UI=20-=20Init=20Value=20D?= =?UTF-8?q?ateTime=20and=20Date=20with=20Day=20Time=20(support=20raw=20dat?= =?UTF-8?q?e=20values)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/attributedef.class.inc.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 25846ef42..4c146e058 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -6432,7 +6432,11 @@ class AttributeDateTime extends AttributeDBField { $sDefaultValue = $this->Get('default_value'); if (utils::IsNotNullOrEmptyString($sDefaultValue)) { - $oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([])); + try { + $oDate = new DateTimeImmutable($sDefaultValue); + } catch (Exception $e) { + $oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([])); + } return $oDate->format($this->GetInternalFormat()); } return $this->GetNullValue(); From dc8f521b12989252e10974697e365ffce8a0cb07 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Thu, 9 Jan 2025 11:56:55 +0100 Subject: [PATCH 3/7] =?UTF-8?q?N=C2=B07145=20-=20UI=20-=20Init=20Value=20D?= =?UTF-8?q?ateTime=20and=20Date=20with=20Day=20Time=20(Add=20default=20val?= =?UTF-8?q?ues=20on=20mandatory=20dates)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/asynctask.class.inc.php | 4 ++-- core/bulkexport.class.inc.php | 2 +- core/cmdbchange.class.inc.php | 2 +- core/dbproperty.class.inc.php | 2 +- core/ownershiplock.class.inc.php | 4 ++-- setup/moduleinstallation.class.inc.php | 2 +- synchro/synchrodatasource.class.inc.php | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/asynctask.class.inc.php b/core/asynctask.class.inc.php index dce31e80a..c8a3a417d 100644 --- a/core/asynctask.class.inc.php +++ b/core/asynctask.class.inc.php @@ -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()))); } diff --git a/core/bulkexport.class.inc.php b/core/bulkexport.class.inc.php index 284ffd544..c6b99afff 100644 --- a/core/bulkexport.class.inc.php +++ b/core/bulkexport.class.inc.php @@ -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()))); diff --git a/core/cmdbchange.class.inc.php b/core/cmdbchange.class.inc.php index a14a58acd..e69673e22 100644 --- a/core/cmdbchange.class.inc.php +++ b/core/cmdbchange.class.inc.php @@ -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()))); diff --git a/core/dbproperty.class.inc.php b/core/dbproperty.class.inc.php index 8574fea12..b03aae2fe 100644 --- a/core/dbproperty.class.inc.php +++ b/core/dbproperty.class.inc.php @@ -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()))); } diff --git a/core/ownershiplock.class.inc.php b/core/ownershiplock.class.inc.php index 2441ff0bb..b9d4c751b 100644 --- a/core/ownershiplock.class.inc.php +++ b/core/ownershiplock.class.inc.php @@ -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()))); diff --git a/setup/moduleinstallation.class.inc.php b/setup/moduleinstallation.class.inc.php index f380492d7..81b68a55a 100644 --- a/setup/moduleinstallation.class.inc.php +++ b/setup/moduleinstallation.class.inc.php @@ -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 diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index d940fe8a4..d711a31cc 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -91,7 +91,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( @@ -2027,7 +2027,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(), ))); From ec324bb28e12a86fdb9bc09316e8c45721a04af6 Mon Sep 17 00:00:00 2001 From: XGUI Date: Fri, 10 Jan 2025 11:12:03 +0100 Subject: [PATCH 4/7] =?UTF-8?q?N=C2=B07746=20-=20Caselog=20edition=20butto?= =?UTF-8?q?n=20active=20even=20if=20the=20user=20has=20a=20read-only=20acc?= =?UTF-8?q?ess=20to=20the=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UI/Base/Layout/ActivityPanel/ActivityPanel.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php b/sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php index 2d38da682..ef70eecb6 100644 --- a/sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php +++ b/sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php @@ -763,7 +763,7 @@ class ActivityPanel extends UIBlock */ public function IsComposeButtonEnabled(): bool { - return $this->HasAnEditableCaseLogTab() && $this->IsCaseLogsSubmitAutonomous(); + return $this->HasAnEditableCaseLogTab() && $this->IsCaseLogsSubmitAutonomous() && $this->HasUserModifyRights(); } /** @@ -974,4 +974,14 @@ class ActivityPanel extends UIBlock $this->bShowMultipleEntriesSubmitConfirmation = appUserPreferences::GetPref('activity_panel.show_multiple_entries_submit_confirmation', static::DEFAULT_SHOW_MULTIPLE_ENTRIES_SUBMI_CONFIRMATION) !== 'false'; return $this; } + + + /** + * @return bool + * @throws \CoreException + */ + protected function HasUserModifyRights(): bool + { + return \UserRights::IsActionAllowed($this->GetObjectClass(), UR_ACTION_MODIFY); + } } \ No newline at end of file From 93dba0644d3e14f31185ce5dbefeb1892cc0c376 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 10 Jan 2025 16:21:12 +0100 Subject: [PATCH 5/7] =?UTF-8?q?N=C2=B05791=20-=20Handle=20NOT=20IN=20and?= =?UTF-8?q?=20NOT=20LIKE=20too?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/oql/expression.class.inc.php | 24 ++++++++++++++++++- .../core/ExpressionEvaluateTest.php | 9 +++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index ea797a2b5..aa53587fa 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -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; } diff --git a/tests/php-unit-tests/unitary-tests/core/ExpressionEvaluateTest.php b/tests/php-unit-tests/unitary-tests/core/ExpressionEvaluateTest.php index 6b9847fe0..2c0951e4c 100644 --- a/tests/php-unit-tests/unitary-tests/core/ExpressionEvaluateTest.php +++ b/tests/php-unit-tests/unitary-tests/core/ExpressionEvaluateTest.php @@ -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), From 9fadbb5eb1edc337308299e99aa2429df8757962 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com> Date: Mon, 13 Jan 2025 08:04:49 +0100 Subject: [PATCH 6/7] =?UTF-8?q?N=C2=B08031=20-=20Make=20all=20portal=20bri?= =?UTF-8?q?cks=20use=20custom=20templates=20for=20all=20templates=20(#696)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Service implementation --- .../portal/config/packages/cache.yaml | 22 +- .../portal/src/Brick/AbstractBrick.php | 138 +++--- .../portal/src/Brick/AggregatePageBrick.php | 18 +- .../portal/src/Brick/BrickCollection.php | 20 +- .../portal/src/Brick/BrowseBrick.php | 48 ++- .../portal/src/Brick/CreateBrick.php | 7 +- .../portal/src/Brick/FilterBrick.php | 22 +- .../portal/src/Brick/ManageBrick.php | 393 +++++++++++------- .../portal/src/Brick/ObjectBrick.php | 62 --- .../portal/src/Brick/PortalBrick.php | 41 +- .../portal/src/Brick/UserProfileBrick.php | 18 +- .../src/Controller/AbstractController.php | 67 ++- .../AggregatePageBrickController.php | 2 +- .../src/Controller/BrowseBrickController.php | 6 +- .../src/Controller/CreateBrickController.php | 1 - .../src/Controller/DefaultController.php | 13 +- .../src/Controller/ManageBrickController.php | 22 +- .../src/Controller/ObjectController.php | 41 +- .../Controller/UserProfileBrickController.php | 2 +- .../src/DataCollector/PortalCollector.php | 127 ++++++ .../PortalXmlConfiguration/Basic.php | 27 +- .../TemplateDefinitionDto.php | 145 +++++++ .../TemplatesProviderInterface.php | 39 ++ .../TemplatesProviderService.php | 323 ++++++++++++++ .../TemplatesProvider/TemplatesRegister.php | 133 ++++++ .../src/Twig/TemplatesTwigExtension.php | 81 ++++ .../bricks/aggregate-page/layout.html.twig | 3 +- .../templates/bricks/browse/layout.html.twig | 4 +- .../bricks/browse/mode_list.html.twig | 40 +- .../bricks/browse/mode_mosaic.html.twig | 4 +- .../bricks/browse/mode_tree.html.twig | 4 +- .../templates/bricks/create/modal.html.twig | 44 -- .../portal/templates/bricks/layout.html.twig | 4 +- .../bricks/manage/layout-chart.html.twig | 6 +- .../bricks/manage/layout-table.html.twig | 4 +- .../templates/bricks/manage/layout.html.twig | 5 +- .../bricks/manage/tile-chart.html.twig | 4 +- .../bricks/manage/tile-top-list.html.twig | 2 +- .../templates/bricks/object/layout.html.twig | 6 +- .../templates/bricks/object/modal.html.twig | 6 +- .../object/mode_apply_stimulus.html.twig | 4 +- .../bricks/object/mode_edit.html.twig | 4 +- .../bricks/object/mode_view.html.twig | 4 +- .../bricks/user-profile/layout.html.twig | 4 +- .../templates/data_collector/portal.html.twig | 170 ++++++++ .../portal/vendor/autoload.php | 2 +- .../vendor/composer/autoload_classmap.php | 7 +- .../portal/vendor/composer/autoload_real.php | 9 +- .../vendor/composer/autoload_static.php | 15 +- .../portal/vendor/composer/installed.php | 4 +- .../2.x/itop-portal/datamodel.itop-portal.xml | 1 + .../InterfaceDiscovery/InterfaceDiscovery.php | 4 + 52 files changed, 1681 insertions(+), 501 deletions(-) delete mode 100644 datamodels/2.x/itop-portal-base/portal/src/Brick/ObjectBrick.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/DataCollector/PortalCollector.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplateDefinitionDto.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesProviderInterface.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesProviderService.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesRegister.php create mode 100644 datamodels/2.x/itop-portal-base/portal/src/Twig/TemplatesTwigExtension.php delete mode 100644 datamodels/2.x/itop-portal-base/portal/templates/bricks/create/modal.html.twig create mode 100644 datamodels/2.x/itop-portal-base/portal/templates/data_collector/portal.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml index 5e374f117..36f2c77c9 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml @@ -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 diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php index ff360b09d..217896e45 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php @@ -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 * @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); } } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php index 4a0d09215..6fff14c08 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php @@ -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. */ diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php index 571325a16..d6de6cf96 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php @@ -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(); diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrowseBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrowseBrick.php index 138ffa015..ef91ddf11 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrowseBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrowseBrick.php @@ -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); } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php index edbf8baf7..2d3745e62 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php @@ -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 = ''; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php index 6ce86067c..104d53649 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php @@ -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. */ diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php index 4026cf5a9..f09338556 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php @@ -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); diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/ObjectBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/ObjectBrick.php deleted file mode 100644 index 8af992061..000000000 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/ObjectBrick.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -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']; - } -} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php index 2630b8bef..ef5a0d12c 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php @@ -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; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/UserProfileBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/UserProfileBrick.php index 351f779f0..f18ac65a4 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/UserProfileBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/UserProfileBrick.php @@ -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. */ diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php index b7dbfec17..d4ea7eb3f 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php @@ -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 * @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; + } } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/AggregatePageBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/AggregatePageBrickController.php index 5f2d513a2..5cad88064 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/AggregatePageBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/AggregatePageBrickController.php @@ -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, diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php index 36ad615a1..1c3eeb31f 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrowseBrickController.php @@ -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); } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/CreateBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/CreateBrickController.php index 2a8772c8d..3e6cafb25 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/CreateBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/CreateBrickController.php @@ -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 diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php index 9d0be3c30..e8e2d0ae3 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php @@ -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); } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php index fef217c6b..dbc289ade 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php @@ -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); } /** diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php index c6c656d26..495dd0a11 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php @@ -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; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php index 3d5e3ca23..42efdf3b4 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php @@ -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; diff --git a/datamodels/2.x/itop-portal-base/portal/src/DataCollector/PortalCollector.php b/datamodels/2.x/itop-portal-base/portal/src/DataCollector/PortalCollector.php new file mode 100644 index 000000000..57df0697e --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/DataCollector/PortalCollector.php @@ -0,0 +1,127 @@ +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'; + } + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php index fc6fb9432..2c8f40a78 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php @@ -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: : + 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; } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplateDefinitionDto.php b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplateDefinitionDto.php new file mode 100644 index 000000000..2d72cd13f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplateDefinitionDto.php @@ -0,0 +1,145 @@ +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; + } + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesProviderInterface.php b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesProviderInterface.php new file mode 100644 index 000000000..d0eb9f484 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesProviderInterface.php @@ -0,0 +1,39 @@ +{path to 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'])); + } + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesRegister.php b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesRegister.php new file mode 100644 index 000000000..1f3787acb --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Service/TemplatesProvider/TemplatesRegister.php @@ -0,0 +1,133 @@ +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; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Twig/TemplatesTwigExtension.php b/datamodels/2.x/itop-portal-base/portal/src/Twig/TemplatesTwigExtension.php new file mode 100644 index 000000000..f7ac030f8 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Twig/TemplatesTwigExtension.php @@ -0,0 +1,81 @@ + 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); + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig index fb3fcbe48..5d79b298b 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig @@ -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 %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/layout.html.twig index a936da88b..d84b38fea 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/layout.html.twig @@ -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 %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig index 8fa0bc650..6974c95f6 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_list.html.twig @@ -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%} @@ -11,8 +9,8 @@ {% block pPageLiveScripts %} {{ parent() }} - - -{% endblock %} - -{% block pModalFooter %} - -{% endblock %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig index b1522779c..fd4b45a26 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig @@ -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 #} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig index 0684eea1b..d67803d28 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig @@ -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 %}
- {% 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} %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig index 2f01faf12..6d6fb1acf 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig @@ -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 %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig index cb36cc05d..f8f9da614 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig @@ -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 %} + class="btn btn-default {% if sDisplay == sDisplayMode %}active{% endif %}"> {{ ('Brick:Portal:Manage:DisplayMode:' ~ sDisplay)|dict_s }} {% endfor %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig index 9e48edb38..fa0149e6b 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig @@ -10,9 +10,9 @@ id="brick-{{ oBrick.GetId }}" data-brick-id="{{ oBrick.GetId }}">
-
{{ oBrick.GetTitle()|dict_s }} +
{{ oBrick.GetTitle()|dict_s }} ({{ iCount }})
- {% 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} %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig index 7c2f49773..295ce1c62 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig @@ -11,7 +11,7 @@ data-brick-id="{{ oBrick.GetId }}">
- {{ oBrick.GetTitle()|dict_s }} + {{ oBrick.GetTitle()|dict_s }} ({{ iCount }})
diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig index d15d95217..eddf390bf 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig @@ -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%}
- {% include 'itop-portal-base/portal/templates/bricks/object/mode_' ~ sMode ~ '.html.twig' %} + {% include template(sMode, 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{% endblock %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig index 9ade2e886..efa4ef223 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig @@ -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 %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig index 918c75e22..5840f8676 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig @@ -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 #} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig index c04137e21..153938e43 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig @@ -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 #} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig index c737be88e..2001e657f 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig @@ -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 #} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig index f27c5d559..0c1a84fdd 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig @@ -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" %} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/data_collector/portal.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/data_collector/portal.html.twig new file mode 100644 index 000000000..fc7b7ac8c --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/templates/data_collector/portal.html.twig @@ -0,0 +1,170 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + + {# toolbar text & icon #} + {% set icon %} + + + + Portal{% if collector.GetTemplatesCount.overrides_count > 0 %} Overrides{% endif %} + {% endset %} + + {# toolbar panel #} + {% set text %} +
+ Templates registered{{ collector.GetTemplatesCount.count }} +
+ {% if collector.GetTemplatesCount.overrides_count %} +
+ Templates overridden{{ collector.GetTemplatesCount.overrides_count }} +
+ {% endif %} + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }} + +{% endblock %} + +{% block head %} + {{ parent() }} + + + +{% endblock %} + +{% block menu %} + + {# menu #} + + + + + Portal + + {{ collector.GetTemplatesCount.count }} + + + +{% endblock %} + +{% block panel %} + + {# title #} +

Templates

+ + {# metrics #} +
+ {# ui version #} +
+ {{ collector.GetUIVersion }} + UI Version +
+
+ {# templates #} +
+
+ {{ collector.GetTemplatesCount.count }} + Templates +
+
+ {{ collector.GetTemplatesCount.overrides_count }} + Overridden +
+
+ {# additional info #} +
+
+
+ {{ collector.GetTemplatesCount.extensions_count }} + Extensions +
+
+ {{ collector.GetTemplatesCount.providers_count }} + Providers +
+
+
+ + {# Instances overloads #} +

Bricks declared templates list

+ {# help #} +

+ Bricks overridden templates are templates defined in brick declarations. +

+ {% if collector.GetInstancesOverriddenTemplates|length == 0 %} + No instance overridden template. + {% else %} +
+ + + + + + + + + {% for instance,item in collector.GetInstancesOverriddenTemplates %} + {% for id,template in item.templates %} + + + + + + + {% endfor %} + {% endfor %} +
BrickClassIDTemplate
{{ item.info.id }}{{ item.info.class }}{{ id }}{{ template }}
+ {% endif %} + + {# templates list #} +

Templates list

+ + {# help #} +

+ Templates doesn't necessary covers all existing templates, only the ones that are registered in the portal. +

+ + + + + + + + + + + + {% for provider,item in collector.GetTemplatesDefinitions %} + {% for id,template in item %} + + + + + + + + {% endfor %} + {% endfor %} +
ProviderIDTemplateAliasOverride
{{ provider }}{{ template.Id }}{{ template.path }}{% if template.IsOverridden %} +
{{ template.GetPath(true) }}
{% endif %}
{{ template.Alias }}{% if template.IsOverridable %} + {% if template.IsOverridden %} + Yes + {% else %} + No + {% endif %} + {% else %} + Not allowed + {% endif %} +
+ + +{% endblock %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php b/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php index 96bd52cc2..172f1b912 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInitdf408f3f8ea034d298269cdf7647358b::getLoader(); +return ComposerAutoloaderInitd751713988987e9331980363e24189ce::getLoader(); diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php index cb0f48185..0b815ea22 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php @@ -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', diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php index be4042c68..4f0c8d559 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php @@ -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; diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php index 400b5cc84..6c347eb1e 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php @@ -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); } diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.php index 9e4501268..3548f7778 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.php @@ -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(), diff --git a/datamodels/2.x/itop-portal/datamodel.itop-portal.xml b/datamodels/2.x/itop-portal/datamodel.itop-portal.xml index 1656ffef1..a85807d59 100644 --- a/datamodels/2.x/itop-portal/datamodel.itop-portal.xml +++ b/datamodels/2.x/itop-portal/datamodel.itop-portal.xml @@ -16,6 +16,7 @@ portal:itop-portal + 2017 diff --git a/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php b/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php index e492c0d08..2d46481b5 100644 --- a/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php +++ b/sources/Service/InterfaceDiscovery/InterfaceDiscovery.php @@ -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); From efb7831a5ae7933d7607445a9879737fb803725a Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 13 Jan 2025 08:30:23 +0100 Subject: [PATCH 7/7] =?UTF-8?q?N=C2=B07111=20-=20False=20link=20at=20the?= =?UTF-8?q?=20end=20of=20the=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datamodels/2.x/itop-tickets/module.itop-tickets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-tickets/module.itop-tickets.php b/datamodels/2.x/itop-tickets/module.itop-tickets.php index 1141d66eb..431b72f8d 100755 --- a/datamodels/2.x/itop-tickets/module.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/module.itop-tickets.php @@ -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