Compare commits

..

1 Commits

Author SHA1 Message Date
Timothee
0cac103e7d N°7383 Show data table even when there is no data after filtering 2024-12-27 18:06:07 +01:00
126 changed files with 4120 additions and 5487 deletions

View File

@@ -3439,18 +3439,8 @@ EOF
}
$sInputType = '';
$sInputId = 'att_'.$iFieldIndex;
$value = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
if ($oAttDef instanceof AttributeDateTime && !$oAttDef->IsNullAllowed() && $value === $oAttDef->GetNullValue()) {
$value = $oAttDef->GetDefaultValue($this);
if ($value !== $oAttDef->GetNullValue()) {
// Set default date
$this->Set($sAttCode, $value);
$sDisplayValue = $this->GetEditValue($sAttCode);
}
}
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
$value, $sDisplayValue, $sInputId, '', $iExpectCode,
$this->Get($sAttCode), $this->GetEditValue($sAttCode), $sInputId, '', $iExpectCode,
$aArgs, true, $sInputType);
$aAttrib = array(
'label' => '<span>'.$oAttDef->GetLabel().'</span>',

View File

@@ -249,9 +249,9 @@ class appUserPreferences extends DBObject
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "login",
"name_attcode" => "userid",
"state_attcode" => "",
"reconc_keys" => array("userid","login"),
"reconc_keys" => array("userid"),
"db_table" => "priv_app_preferences",
"db_key_field" => "id",
"db_finalclass_field" => "",
@@ -260,10 +260,8 @@ class appUserPreferences extends DBObject
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
MetaModel::Init_SetZListItems('list', array('preferences',));
MetaModel::Init_SetZListItems('default_search', array('userid'));
}
/**

View File

@@ -180,7 +180,6 @@ class utils
*/
private static $sAbsoluteUrlAppRootCache = null;
protected static function LoadParamFile($sParamFile)
{
if (!file_exists($sParamFile)) {
@@ -2394,75 +2393,53 @@ SQL;
return $bRet;
}
/**
* @param $sPath
*
* @return false|\ormDocument
* @throws \Exception
*
* @deprecated 3.2.1 use utils::GetDocumentFromSelfURL instead
*/
public static function IsSelfURL($sPath)
{
return self::GetDocumentFromSelfURL($sPath);
}
/**
* Check if the given URL is a link to download a document/image on the CURRENT iTop
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
*
* @Since 3.2.1 a local URL is transformed into a local file to read
*
* @param string $sPath
* @return false|ormDocument
* @throws Exception
*/
public static function GetDocumentFromSelfURL(string $sPath)
public static function IsSelfURL($sPath)
{
$result = false;
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php';
if (utils::StartsWith($sPath, $sPageUrl)) {
if (substr($sPath, 0, strlen($sPageUrl)) == $sPageUrl)
{
// If the URL is an URL pointing to this instance of iTop, then
// extract the "query" part of the URL and analyze it
$sQuery = parse_url($sPath, PHP_URL_QUERY);
if ($sQuery !== null) {
if ($sQuery !== null)
{
$aParams = array();
foreach (explode('&', $sQuery) as $sChunk) {
foreach(explode('&', $sQuery) as $sChunk)
{
$aParts = explode('=', $sChunk ?? '');
if (count($aParts) != 2) {
continue;
}
if (count($aParts) != 2) continue;
$aParams[$aParts[0]] = urldecode($aParts[1]);
}
$result = array_key_exists('operation', $aParams) && array_key_exists('class', $aParams) && array_key_exists('id', $aParams) && array_key_exists('field', $aParams) && ($aParams['operation'] == 'download_document');
if ($result) {
if ($result)
{
// This is a 'download_document' operation, let's retrieve the document directly from the database
$sClass = $aParams['class'];
$iKey = $aParams['id'];
$sAttCode = $aParams['field'];
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
if ($oObj) {
if ($oObj)
{
/**
* @var ormDocument $result
*/
return clone $oObj->Get($sAttCode);
$result = clone $oObj->Get($sAttCode);
return $result;
}
}
}
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.');
}
if (utils::StartsWith($sPath, utils::GetAbsoluteUrlAppRoot())) {
$sFilePath = utils::LocalPath(APPROOT.substr($sPath, strlen(utils::GetAbsoluteUrlAppRoot())));
if (false === $sFilePath) {
return false;
}
$sFilePath = APPROOT.$sFilePath;
return ormDocument::FromFile($sFilePath);
}
return false;
return $result;
}
/**
@@ -2470,28 +2447,68 @@ SQL;
* - an URL pointing to a blob (image/document) on the current iTop server
* - an http(s) URL
* - the local file system (but only if you are an administrator)
*
* @param string|null $sPath
* @param string $sPath
* @return ormDocument|null
* @throws Exception
*/
public static function FileGetContentsAndMIMEType($sPath)
{
if (utils::IsNullOrEmptyString($sPath)) {
$oUploadedDoc = null;
$aKnownExtensions = array(
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream',
);
$sData = null;
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
$sFileName = 'uploaded-file'; // Default name for downloaded-files
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
if(empty($sPath))
{
// Empty path (NULL or '') means that there is no input, making an empty document.
return new ormDocument('', '', '');
$oUploadedDoc = new ormDocument('', '', '');
}
if (static::IsURL($sPath)) {
$oUploadedDoc = static::GetDocumentFromSelfURL($sPath);
if ($oUploadedDoc) {
return $oUploadedDoc;
elseif (static::IsURL($sPath))
{
if ($oUploadedDoc = static::IsSelfURL($sPath))
{
// Nothing more to do, we've got it !!
}
// Remote file, let's use the HTTP headers to find the MIME Type
$sData = @file_get_contents($sPath);
if ($sData === false) {
IssueLog::Error(<<<TXT
else
{
// Remote file, let's use the HTTP headers to find the MIME Type
$sData = @file_get_contents($sPath);
if ($sData === false)
{
IssueLog::Error(<<<TXT
Failed to load the file from URL. This can happen for multiple reasons:
- Invalid URL
- URL using HTTPS with an untrusted certificate on the remote server
@@ -2500,40 +2517,54 @@ TXT
, LogChannels::CORE, [
'URL' => $sPath,
]);
throw new Exception("Failed to load the file from the URL '$sPath'.");
}
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
$sFileName = 'uploaded-file'; // Default name for downloaded-files
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
if (isset($http_response_header)) {
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach (ormDocument::GetKnownExtensions() as $sExtValue => $sMime) {
if ($sMime === $sMimeType) {
$sExtension = '.'.$sExtValue;
break;
}
throw new Exception("Failed to load the file from the URL '$sPath'.");
}
else
{
if (isset($http_response_header))
{
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach ($aKnownExtensions as $sExtValue => $sMime) {
if ($sMime === $sMimeType) {
$sExtension = '.'.$sExtValue;
break;
}
}
}
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
if (utils::IsNotNullOrEmptyString($sPathName)) {
$sFileName = $sPathName;
}
$sFileName .= $sExtension;
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
}
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
if (utils::IsNotNullOrEmptyString($sPathName)) {
$sFileName = $sPathName;
}
$sFileName .= $sExtension;
return new ormDocument($sData, $sMimeType, $sFileName);
}
// Local file
if (UserRights::IsAdministrator()) {
else if (UserRights::IsAdministrator())
{
// Only administrators are allowed to read local files
return ormDocument::FromFile($sPath);
}
$sData = @file_get_contents($sPath);
if ($sData === false)
{
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
return null;
if (array_key_exists($sExtension, $aKnownExtensions))
{
$sMimeType = $aKnownExtensions[$sExtension];
}
else if (extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($sPath);
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
}
return $oUploadedDoc;
}
protected static function ParseHeaders($aHeaders)
@@ -3104,13 +3135,29 @@ TXT
* @throws \Exception
* @since 3.0.0
*/
public static function GetMentionedObjectsFromText(string $sText): array
public static function GetMentionedObjectsFromText(string $sText, string $sFormat = self::ENUM_TEXT_FORMAT_HTML): array
{
$aMentionedObjects = [];
$aMentionMatches = [];
$sText = html_entity_decode($sText);
// First transform text so it can be parsed
switch ($sFormat) {
case static::ENUM_TEXT_FORMAT_HTML:
$sText = static::HtmlToText($sText);
break;
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-key="([^"]*)"/i', $sText, $aMentionMatches);
default:
// Don't transform it
break;
}
// Then parse text to find objects
$aMentionedObjects = array();
$aMentionMatches = array();
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink,
// - we can't use the following (simpler) regexp that only checks data attributes on hyperlinks, which would have worked for hyperlinks pointing to any GUIs: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
// - instead we use a regexp to match the following pattern '[Some object label](<APP_ROOT_URL>...&class=<OBJECT_CLASS>&id=<OBJECT_ID>...)' which only works for the backoffice
// If we change the sanitizer, we might want to switch to the other regexp as it's universal and easier to read
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $sText, $aMentionMatches);
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];

View File

@@ -89,7 +89,7 @@ abstract class AsyncTask extends DBObject
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "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("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"=>'NOW()', "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())));
}

View File

@@ -6346,6 +6346,9 @@ class AttributeDateTime extends AttributeDBField
$oFormField = parent::MakeFormField($oObject, $oFormField);
// After call to the parent as it sets the current value
$oFormField->SetCurrentValue($this->GetFormat()->Format($oObject->Get($this->GetCode())));
return $oFormField;
}
@@ -6430,14 +6433,8 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null)
{
$sDefaultValue = $this->Get('default_value');
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
try {
$oDate = new DateTimeImmutable($sDefaultValue);
} catch (Exception $e) {
$oDate = new DateTimeImmutable(Expression::FromOQL($sDefaultValue)->Evaluate([]));
}
return $oDate->format($this->GetInternalFormat());
if (!$this->IsNullAllowed()) {
return date($this->GetInternalFormat());
}
return $this->GetNullValue();
}

View File

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

View File

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

View File

@@ -2867,14 +2867,6 @@ abstract class DBObject implements iDisplay
protected function ListChangedValues(array $aProposal)
{
$aDelta = array();
$sClass = get_class($this);
if (MetaModel::HasLifecycle($sClass) && utils::IsNotNullOrEmptyString($this->sStimulusBeingApplied)) {
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (!in_array($sStateAttCode, $aProposal)) {
// Same state but the transition was asked, act as if the state was changed
$aDelta[$sStateAttCode] = $this->m_aCurrValues[$sStateAttCode];
}
}
foreach ($aProposal as $sAtt => $proposedValue)
{
if (!array_key_exists($sAtt, $this->m_aOrigValues))
@@ -3819,7 +3811,7 @@ abstract class DBObject implements iDisplay
}
try {
$this->PostUpdateActions($this->m_aPreviousValuesForUpdatedAttributes, $sClass);
$this->PostUpdateActions($aChanges, $sClass);
}
catch (Exception $e) {
$this->LogCRUDExit(__METHOD__, 'Error: '.$e->getMessage());
@@ -4033,7 +4025,7 @@ abstract class DBObject implements iDisplay
foreach ($aUpdatedLogAttCodes as $sAttCode) {
/** @var \ormCaseLog $oUpdatedCaseLog */
$oUpdatedCaseLog = $this->Get($sAttCode);
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry(ormCaseLog::ENUM_FORMAT_HTML)));
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
}
// 3 - Trigger for those objects

View File

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

View File

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

View File

@@ -278,7 +278,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
protected static $aTagsWhiteList = array(
'html' => array(),
'body' => array(),
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id', 'data-object-key'),
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id'),
'p' => array('style', 'class'),
'blockquote' => array('style', 'class'),
'br' => array(),
@@ -354,8 +354,6 @@ class HTMLDOMSanitizer extends DOMSanitizer
'font-style',
'height',
'margin',
'margin-left',
'margin-right',
'padding',
'text-align',
'vertical-align',

View File

@@ -575,15 +575,6 @@ 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");
}
@@ -648,26 +639,7 @@ class BinaryExpression extends Expression
case 'like':
$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 '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);
$result = (int) preg_match("/$sEscaped/i", $mLeft);
break;
}
return $result;
@@ -2278,12 +2250,7 @@ class ListExpression extends Expression
*/
public function Evaluate(array $aArgs)
{
//throw new Exception('list expression not yet supported');
$aResult = [];
foreach ($this->m_aExpressions as $oExpressions) {
$aResult[] = $oExpressions->Evaluate($aArgs);
}
return $aResult;
throw new Exception('list expression not yet supported');
}
/**

View File

@@ -48,42 +48,6 @@ class ormDocument
* @since 3.1.0
*/
public const DEFAULT_DOWNLOADS_COUNT = 0;
private static $aKnownExtensions = [
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream',
];
public static function GetKnownExtensions(): array
{
return self::$aKnownExtensions;
}
protected $m_data;
protected $m_sMimeType;
@@ -112,36 +76,6 @@ class ormDocument
$this->m_iDownloadsCount = $iDownloadsCount;
}
/**
* @param string $sPath Absolute path of the document to read
*
* @return \ormDocument
* @throws \Exception
*/
public static function FromFile(string $sPath): ormDocument
{
$sPath = utils::RealPath($sPath, APPROOT);
if (false === $sPath) {
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sData = @file_get_contents($sPath);
if (false === $sData) {
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
$sMimeType = 'text/plain';
if (array_key_exists($sExtension, ormDocument::$aKnownExtensions)) {
$sMimeType = ormDocument::$aKnownExtensions[$sExtension];
} else if (extension_loaded('fileinfo')) {
$fInfo = new finfo(FILEINFO_MIME);
$sMimeType = $fInfo->file($sPath);
}
return new ormDocument($sData, $sMimeType, $sFileName);
}
public function __toString()
{
if($this->IsEmpty()) return '';

View File

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

View File

@@ -1921,45 +1921,50 @@ class UserRights
*/
protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false)
{
if ($sAuthentication === 'any') {
$oUser = self::FindUser($sLogin, 'internal', $bAllowDisabledUsers);
if ($oUser !== null) {
return $oUser;
if ($sAuthentication == 'any')
{
$oUser = self::FindUser($sLogin, 'internal');
if ($oUser == null)
{
$oUser = self::FindUser($sLogin, 'external');
}
}
else
{
if (!isset(self::$m_aCacheUsers))
{
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
}
return self::FindUser($sLogin, 'external', $bAllowDisabledUsers);
}
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
{
switch($sAuthentication)
{
case 'external':
$sBaseClass = 'UserExternal';
break;
if (!isset(self::$m_aCacheUsers)) {
self::$m_aCacheUsers = [ 'internal' => [], 'external' => [] ];
}
case 'internal':
$sBaseClass = 'UserInternal';
break;
if (! isset(self::$m_aCacheUsers[$sAuthentication]) || ! array_key_exists($sLogin, self::$m_aCacheUsers[$sAuthentication])) {
switch($sAuthentication) {
case 'external':
$sBaseClass = 'UserExternal';
break;
case 'internal':
$sBaseClass = 'UserInternal';
break;
default:
echo "<p>sAuthentication = $sAuthentication</p>\n";
assert(false); // should never happen
default:
echo "<p>sAuthentication = $sAuthentication</p>\n";
assert(false); // should never happen
}
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
$oSearch->AllowAllData();
if (!$bAllowDisabledUsers)
{
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin));
$oUser = $oSet->fetch();
self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser;
}
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
$oSearch->AllowAllData();
if (!$bAllowDisabledUsers) {
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin));
$oUser = $oSet->fetch();
self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser;
$oUser = self::$m_aCacheUsers[$sAuthentication][$sLogin];
}
return self::$m_aCacheUsers[$sAuthentication][$sLogin];
return $oUser;
}
/**

View File

@@ -952,22 +952,5 @@ HTML
</classes>
</group>
</groups>
<profiles>
<profile id="117" _delta="if_exists">
<!-- SuperUser -->
<groups>
<group id="OauthConnection">
<actions>
<action id="action:read">allow</action>
<action id="action:write">allow</action>
<action id="action:delete">allow</action>
<action id="action:bulk read">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:bulk delete">allow</action>
</actions>
</group>
</groups>
</profile>
</profiles>
</user_rights>
</itop_design>

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('DA DA', 'Danish', 'Dansk', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -4,13 +4,13 @@
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*
*/
/**
*
*/
Dict::Add('DE DE', 'German', 'Deutsch', [
'Class:OAuthClient' => 'Mailpostfach-OAuth-Client',
'Class:OAuthClient' => 'OAuth-Client',
'Class:OAuthClient/Attribute:client_id' => 'Client ID',
'Class:OAuthClient/Attribute:client_id+' => 'Eine lange Zeichenfolge, die durch den oAuth2-Provider bereitgestellt wird',
'Class:OAuthClient/Attribute:client_secret' => 'Client Secret',
@@ -40,7 +40,7 @@ Löschen Sie das Feld, um den Standardwert neu zu berechnen.',
'Class:OAuthClient/Attribute:token+' => '',
'Class:OAuthClient/Attribute:token_expiration' => 'Zugriffstoken Ablaufszeitpunkt',
'Class:OAuthClient/Attribute:token_expiration+' => '',
'Class:OAuthClientAzure' => 'Mailpostfach-OAuth-Client für Microsoft Azure',
'Class:OAuthClientAzure' => 'OAuth-Client für Microsoft Azure',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Erweiterter Scope',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'Sobald Sie hier etwas eingeben, hat es Vorrang vor der Auswahl im Feld "Scope", die dann ignoriert wird.',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope',
@@ -62,7 +62,7 @@ Löschen Sie das Feld, um den Standardwert neu zu berechnen.',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Einfach',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle' => 'Mailpostfach-OAuth-Client für Google',
'Class:OAuthClientGoogle' => 'OAuth-Client für Google',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Erweiterter Scope',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'Sobald Sie hier etwas eingeben, hat es Vorrang vor der Auswahl im Feld "Scope", die dann ignoriert wird.',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope',
@@ -84,7 +84,7 @@ Löschen Sie das Feld, um den Standardwert neu zu berechnen.',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Mailpostfach erstellen...',
'Menu:GenerateTokens' => 'Zugriffstoken generieren...',
'Menu:OAuthClient' => 'Mailpostfach-OAuth-Client',
'Menu:OAuthClient' => 'OAuth-Client',
'Menu:OAuthClient+' => '',
'Menu:RegenerateTokens' => 'Zugriffstoken neu generieren...',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'Die Kombination aus "Login" (%1$s) und "Verwendung für SMTP" (%2$s) existiert bereits.',

View File

@@ -8,7 +8,7 @@
Dict::Add('EN US', 'English', 'English', [
'Menu:CreateMailbox' => 'Create a mailbox...',
'Menu:OAuthClient' => 'OAuth Mail Access',
'Menu:OAuthClient' => 'OAuth client',
'Menu:OAuthClient+' => 'Oauth for email access',
'Menu:GenerateTokens' => 'Generate access token...',
'Menu:RegenerateTokens' => 'Regenerate access token...',
@@ -35,7 +35,7 @@ Dict::Add('EN US', 'English', 'English', [
//
Dict::Add('EN US', 'English', 'English', [
'Class:OAuthClient' => 'OAuth Mail Access',
'Class:OAuthClient' => 'OAuth Client',
'Class:OAuthClient/Attribute:provider' => 'Provider',
'Class:OAuthClient/Attribute:provider+' => '',
'Class:OAuthClient/Attribute:name' => 'Login',
@@ -73,7 +73,7 @@ EOF
//
Dict::Add('EN US', 'English', 'English', array(
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope',
'Class:OAuthClientAzure/Attribute:scope+' => 'Usually default selection is appropriate',
@@ -102,7 +102,7 @@ Dict::Add('EN US', 'English', 'English', array(
//
Dict::Add('EN US', 'English', 'English', array(
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google',
'Class:OAuthClientGoogle' => 'OAuth client for Google',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope',
'Class:OAuthClientGoogle/Attribute:scope+' => 'Usually default selection is appropriate',

View File

@@ -8,7 +8,7 @@
Dict::Add('EN GB', 'British English', 'British English', [
'Menu:CreateMailbox' => 'Create a mailbox...',
'Menu:OAuthClient' => 'OAuth Mail Access',
'Menu:OAuthClient' => 'OAuth client',
'Menu:OAuthClient+' => '',
'Menu:GenerateTokens' => 'Generate access token...',
'Menu:RegenerateTokens' => 'Regenerate access token...',
@@ -35,7 +35,7 @@ Dict::Add('EN GB', 'British English', 'British English', [
//
Dict::Add('EN GB', 'British English', 'British English', [
'Class:OAuthClient' => 'OAuth Mail Access',
'Class:OAuthClient' => 'OAuth Client',
'Class:OAuthClient/Attribute:provider' => 'Provider',
'Class:OAuthClient/Attribute:provider+' => '',
'Class:OAuthClient/Attribute:name' => 'Login',
@@ -73,7 +73,7 @@ EOF
//
Dict::Add('EN GB', 'British English', 'British English', array(
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope',
'Class:OAuthClientAzure/Attribute:scope+' => 'Usually default selection is appropriate',
@@ -102,7 +102,7 @@ Dict::Add('EN GB', 'British English', 'British English', array(
//
Dict::Add('EN GB', 'British English', 'British English', array(
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google',
'Class:OAuthClientGoogle' => 'OAuth client for Google',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope',
'Class:OAuthClientGoogle/Attribute:scope+' => 'Usually default selection is appropriate',

View File

@@ -5,10 +5,10 @@
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
* @author Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'Cliente OAuth',
'Class:OAuthClient/Attribute:client_id' => 'Id Cliente',
'Class:OAuthClient/Attribute:client_id+' => 'Una cadena larga de caracteres proporcionada por su proveedor de OAuth2',
'Class:OAuthClient/Attribute:client_secret' => 'Secreto del Cliente',
@@ -82,7 +82,7 @@ Borre el campo para recalcular el valor predeterminado',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Crear un buzón...',
'Menu:GenerateTokens' => 'Generar token de acceso...',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'Cliente OAuth',
'Menu:OAuthClient+' => 'Cliente OAuth',
'Menu:RegenerateTokens' => 'Regenerar token de acceso...',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'La combinación Inicio de sesión (%1$s) y Uso para SMTP (%2$s) ya se ha utilizado para el Cliente OAuth',

View File

@@ -4,13 +4,13 @@
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*
*/
/**
*
*/
Dict::Add('FR FR', 'French', 'Français', [
'Class:OAuthClient' => 'Client OAuth pour l\'Accès Mail',
'Class:OAuthClient' => 'Client OAuth',
'Class:OAuthClient/Attribute:client_id' => 'ID Client',
'Class:OAuthClient/Attribute:client_id+' => 'Recopier la chaine fournie par votre fournisseur OAuth2',
'Class:OAuthClient/Attribute:client_secret' => 'Code secret du client',
@@ -86,7 +86,7 @@ Pour recalculer la valeur par défaut, il faut effacer le champ',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Créer une boite mail...',
'Menu:GenerateTokens' => 'Créer un jeton d\'accès...',
'Menu:OAuthClient' => 'Client OAuth pour l\'Accès Mail',
'Menu:OAuthClient' => 'Client OAuth',
'Menu:OAuthClient+' => 'Client OAuth pour l\'email',
'Menu:RegenerateTokens' => 'Recréer un jeton d\'accès..',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'La combinaison Login (%1$s) and Utilisé pour SMTP (%2$s) a déjà été utilisée pour OAuth Client',

View File

@@ -4,13 +4,13 @@
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*
*/
/**
*
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth ügyfél',
'Class:OAuthClient/Attribute:client_id' => 'Ügyfél azonosító',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Ügyfél kulcs',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Hozzáférési token lejárata',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth ügyfél Microsoft Azure-hoz',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth ügyfél a Google-höz',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Postafiók létrehozása...',
'Menu:GenerateTokens' => 'Hozzáférési tokenek generálása...',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth ügyfél',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Hozzáférési tokenek újragenerálása...',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -4,13 +4,13 @@
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*
*/
/**
*
*/
Dict::Add('IT IT', 'Italian', 'Italiano', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'Client OAuth',
'Class:OAuthClient/Attribute:client_id' => 'ID cliente',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Segreto del cliente',
@@ -39,7 +39,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Scadenza del token di accesso',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'Client OAuth per Microsoft Azure',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Ambito avanzato',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'Non appena inserisci qualcosa qui, essa ha la precedenza sulla selezione “Ambito” che viene quindi ignorata',
'Class:OAuthClientAzure/Attribute:scope' => 'Ambito',
@@ -61,7 +61,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Semplice',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'Client OAuth per Google',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Ambito avanzato',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'Non appena inserisci qualcosa qui, essa ha la precedenza sulla selezione “Ambito” che viene quindi ignorata',
'Class:OAuthClientGoogle/Attribute:scope' => 'Ambito',
@@ -83,7 +83,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Crea una casella di posta...',
'Menu:GenerateTokens' => 'Genera token di accesso...',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'Client OAuth',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Rigenera token di accesso...',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'La combinazione Login (%1$s) e Uso per SMTP (%2$s) è già stata utilizzata per un altro Client OAuth',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('JA JP', 'Japanese', '日本語', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -11,7 +11,7 @@
* @author Thomas Casteleyn <thomas.casteleyn@super-visions.com>
*/
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -41,7 +41,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -63,7 +63,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Eenvoudig',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -85,7 +85,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -4,13 +4,13 @@
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*
*/
/**
*
*/
Dict::Add('PL PL', 'Polish', 'Polski', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'Klient OAuth',
'Class:OAuthClient/Attribute:client_id' => 'Id klienta',
'Class:OAuthClient/Attribute:client_id+' => 'Długi ciąg znaków dostarczony przez dostawcę protokołu OAuth2',
'Class:OAuthClient/Attribute:client_secret' => 'Sekretny ciąg',
@@ -80,7 +80,7 @@ Usuń pole, aby ponownie obliczyć wartość domyślną',
'Class:OAuthClient/Attribute:token+' => '',
'Class:OAuthClient/Attribute:token_expiration' => 'Wygaśnięcie tokenu dostępu',
'Class:OAuthClient/Attribute:token_expiration+' => '',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'Klient OAuth dla Microsoft Azure',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Zaawansowany zakres',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'Gdy tylko coś tu wpiszesz, będzie to miało pierwszeństwo przed wyborem „Zakres”, który zostanie następnie zignorowany',
'Class:OAuthClientAzure/Attribute:scope' => 'Zakres',
@@ -150,7 +150,7 @@ Usuń pole, aby ponownie obliczyć wartość domyślną',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Prosty',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'Klient OAuth dla Google',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Zaawansowany zakres',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'Gdy tylko coś tu wpiszesz, będzie to miało pierwszeństwo przed wyborem „Zakres”, który zostanie następnie zignorowany',
'Class:OAuthClientGoogle/Attribute:scope' => 'Zakres',
@@ -220,7 +220,7 @@ Usuń pole, aby ponownie obliczyć wartość domyślną',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Menu:CreateMailbox' => 'Utwórz skrzynkę pocztową...',
'Menu:GenerateTokens' => 'Wygeneruj token dostępu...',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'Klient OAuth',
'Menu:OAuthClient+' => '',
'Menu:RegenerateTokens' => 'Wygeneruj ponownie token dostępu...',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'Kombinacja Loginu (%1$s) i SMTP (%2$s) była już użyta dla klienta OAuth',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -38,7 +38,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -60,7 +60,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -82,7 +82,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -10,7 +10,7 @@
*
*/
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth Client~~',
'Class:OAuthClient/Attribute:client_id' => 'Client id~~',
'Class:OAuthClient/Attribute:client_id+' => 'A long string of characters provided by your OAuth2 provider~~',
'Class:OAuthClient/Attribute:client_secret' => 'Client secret~~',
@@ -40,7 +40,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClient/Attribute:token+' => '~~',
'Class:OAuthClient/Attribute:token_expiration' => 'Access token expiration~~',
'Class:OAuthClient/Attribute:token_expiration+' => '~~',
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => 'OAuth client for Microsoft Azure~~',
'Class:OAuthClientAzure/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientAzure/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientAzure/Attribute:scope' => 'Scope~~',
@@ -62,7 +62,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple' => 'Simple~~',
'Class:OAuthClientAzure/Attribute:used_scope/Value:simple+' => '~~',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)~~',
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => 'OAuth client for Google~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope' => 'Advanced scope~~',
'Class:OAuthClientGoogle/Attribute:advanced_scope+' => 'As soon as you enter something here it takes precedence over the “Scope” selection which is then ignored~~',
'Class:OAuthClientGoogle/Attribute:scope' => 'Scope~~',
@@ -84,7 +84,7 @@ Erase the field to recalculate default value~~',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)~~',
'Menu:CreateMailbox' => 'Create a mailbox...~~',
'Menu:GenerateTokens' => 'Generate access token...~~',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth client~~',
'Menu:OAuthClient+' => '~~',
'Menu:RegenerateTokens' => 'Regenerate access token...~~',
'OAuthClient:Name/UseForSMTPMustBeUnique' => 'The combination Login (%1$s) and Use for SMTP (%2$s) has already been used for OAuth Client~~',

View File

@@ -8,7 +8,7 @@
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Menu:CreateMailbox' => '创建邮箱...',
'Menu:OAuthClient' => 'OAuth Mail Access~~',
'Menu:OAuthClient' => 'OAuth客户端',
'Menu:OAuthClient+' => '',
'Menu:GenerateTokens' => '生成访问令牌...',
'Menu:RegenerateTokens' => '重新生成访问令牌...',
@@ -35,7 +35,7 @@
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClient' => 'OAuth Mail Access~~',
'Class:OAuthClient' => 'OAuth客户端',
'Class:OAuthClient/Attribute:provider' => '提供商',
'Class:OAuthClient/Attribute:provider+' => '~~',
'Class:OAuthClient/Attribute:name' => '登录',
@@ -73,7 +73,7 @@ EOF
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClientAzure' => 'OAuth Mail Access for Microsoft Azure~~',
'Class:OAuthClientAzure' => '用于微软Azure的OAuth客户端',
'Class:OAuthClientAzure/Name' => '%1$s (%2$s)',
'Class:OAuthClientAzure/Attribute:scope' => '范围',
'Class:OAuthClientAzure/Attribute:scope+' => '通常情况下使用默认选择最合适',
@@ -102,7 +102,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
//
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:OAuthClientGoogle' => 'OAuth Mail Access for Google~~',
'Class:OAuthClientGoogle' => '用于Google的OAuth客户端',
'Class:OAuthClientGoogle/Name' => '%1$s (%2$s)',
'Class:OAuthClientGoogle/Attribute:scope' => '范围',
'Class:OAuthClientGoogle/Attribute:scope+' => '通常情况下使用默认选择最合适',

View File

@@ -50,7 +50,7 @@ class PopupMenuExtension implements \iPopupMenuExtension
if ($bHasToken) {
$aScopes = $oObj->Get('scope')->GetValues();
if (in_array('IMAP', $aScopes) && class_exists('MailInboxOAuth')) {
if (in_array('IMAP', $aScopes)) {
$aParams = $oAppContext->GetAsHash();
$sMenu = 'Menu:CreateMailbox';
$sObjClass = get_class($oObj);

View File

@@ -1,13 +1,19 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
prefix_seed: combodo/itop
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# filesystem
app: cache.adapter.filesystem
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# Namespaced pools use the above "app" backend by default
pools:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
templates_cache_pool:
adapter: cache.app
# 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

View File

@@ -23,13 +23,9 @@ 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
@@ -40,7 +36,7 @@ use ModuleDesign;
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.3.0
*/
abstract class AbstractBrick implements TemplatesProviderInterface
abstract class AbstractBrick
{
/** @var string ENUM_DATA_LOADING_LAZY */
const ENUM_DATA_LOADING_LAZY = 'lazy';
@@ -57,7 +53,7 @@ abstract class AbstractBrick implements TemplatesProviderInterface
const DEFAULT_VISIBLE = true;
/** @var float DEFAULT_RANK */
const DEFAULT_RANK = 1.0;
/** @var string|null DEFAULT_PAGE_TEMPLATE_PATH @deprecated since 3.2.1 */
/** @var string|null DEFAULT_PAGE_TEMPLATE_PATH */
const DEFAULT_PAGE_TEMPLATE_PATH = null;
/** @var string DEFAULT_TITLE */
const DEFAULT_TITLE = '';
@@ -69,8 +65,6 @@ abstract class AbstractBrick implements TemplatesProviderInterface
const DEFAULT_ALLOWED_PROFILES_OQL = '';
/** @var string DEFAULT_DENIED_PROFILES_OQL */
const DEFAULT_DENIED_PROFILES_OQL = '';
/** @var string TEMPLATES_BASE_PATH */
const TEMPLATES_BASE_PATH = 'itop-portal-base/portal/templates/bricks/';
/** @var string $sId */
protected $sId;
@@ -82,7 +76,7 @@ abstract class AbstractBrick implements TemplatesProviderInterface
protected $bVisible;
/** @var float $fRank */
protected $fRank;
/** @var string|null $sPageTemplatePath @deprecated since 3.2.1 */
/** @var string|null $sPageTemplatePath */
protected $sPageTemplatePath;
/** @var string $sTitle */
protected $sTitle;
@@ -99,37 +93,6 @@ abstract class AbstractBrick implements TemplatesProviderInterface
/** @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.
*
@@ -149,9 +112,7 @@ abstract class AbstractBrick implements TemplatesProviderInterface
$this->bActive = static::DEFAULT_ACTIVE;
$this->bVisible = static::DEFAULT_VISIBLE;
$this->fRank = static::DEFAULT_RANK;
// 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;
@@ -215,12 +176,10 @@ abstract class AbstractBrick implements TemplatesProviderInterface
* Returns the brick page template path
*
* @return string
*
* @deprecated since 3.2.1 use GetTemplatePath('page') instead
*/
public function GetPageTemplatePath()
{
return $this->GetTemplatePath('page');
return $this->sPageTemplatePath;
}
/**
@@ -364,13 +323,10 @@ abstract class AbstractBrick implements TemplatesProviderInterface
* @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;
}
@@ -660,7 +616,7 @@ abstract class AbstractBrick implements TemplatesProviderInterface
{
/** @var \Combodo\iTop\DesignElement $oTemplateNode */
$oTemplateNode = $oTemplateNodeList->item(0);
$this->SetTemplatePath('page', $oTemplateNode->GetText(static::DEFAULT_PAGE_TEMPLATE_PATH));
$this->SetPageTemplatePath($oTemplateNode->GetText(static::DEFAULT_PAGE_TEMPLATE_PATH));
}
break;
case 'title':
@@ -704,48 +660,37 @@ abstract class AbstractBrick implements TemplatesProviderInterface
}
/**
* Override the brick default template path.
* Template is managed by the TemplatesProviderService.
* Load brick configuration that is not part of the brick definition but is part of the portal global properties.
*
* @param $aPortalProperties
*
* @return void
* @throws \DOMFormatException
* @since 3.2.1
*
* @param string $sTemplateId
* @param string $sTileTemplatePath
*
* @return \Combodo\iTop\Portal\Brick\PortalBrick
*/
public function SetTemplatePath(string $sTemplateId, string $sTileTemplatePath): AbstractBrick
public function LoadFromPortalProperties($aPortalProperties)
{
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);
// Get the bricks templates
$aBricksTemplates = $aPortalProperties['templates']['bricks'];
$sClassFQCN = get_class($this);
// 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
$sSetTemplateMethodName = 'Set'.$sTemplateId.'TemplatePath';
if(method_exists($this, $sSetTemplateMethodName)) {
$this->{$sSetTemplateMethodName}($sTemplate);
}
else {
throw new DOMFormatException(
'Template "'.$sTemplateId.'" is not a valid template for brick ' . $sClassFQCN,
null, null);
}
}
}
}

View File

@@ -21,8 +21,6 @@
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;
@@ -40,8 +38,6 @@ 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';
// Overloaded variables
@@ -52,15 +48,6 @@ class AggregatePageBrick extends PortalBrick
*/
private $aAggregatePageBricks = array();
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'aggregate-page/layout.html.twig')
);
}
/**
* AggregatePageBrick constructor.
*/

View File

@@ -19,9 +19,9 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use DOMFormatException;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
use UserRights;
use ModuleDesign;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
@@ -48,20 +48,21 @@ class BrickCollection
private $aHomeOrdering;
/** @var array $aNavigationMenuOrdering */
private $aNavigationMenuOrdering;
/** @var \array $aCombodoPortalInstanceConf
* @since 3.2.1
*/
private $aCombodoPortalInstanceConf;
/**
* BrickCollection constructor.
*
* @param \ModuleDesign $oModuleDesign
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService $oTemplatesProviderService
* @param $aCombodoPortalInstanceConf
*
* @throws \Exception
*
* @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.
* @since 3.2.1 Added $aCombodoPortalInstanceConf parameter
*/
public function __construct(ModuleDesign $oModuleDesign, TemplatesProviderService $oTemplatesProviderService)
public function __construct(ModuleDesign $oModuleDesign, $aCombodoPortalInstanceConf)
{
$this->oModuleDesign = $oModuleDesign;
$this->aAllowedBricks = null;
@@ -69,6 +70,7 @@ class BrickCollection
$this->iDisplayedInNavigationMenu = 0;
$this->aHomeOrdering = array();
$this->aNavigationMenuOrdering = array();
$this->aCombodoPortalInstanceConf = $aCombodoPortalInstanceConf;
$this->Load();
}
@@ -203,6 +205,9 @@ class BrickCollection
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */
$oBrick = new $sBrickClass();
// Load the portal properties that are common to all bricks of this type
$oBrick->LoadFromPortalProperties($this->aCombodoPortalInstanceConf['properties']);
// Load the brick specific properties from its XML definition
$oBrick->LoadFromXml($oBrickNode);

View File

@@ -20,10 +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;
use Combodo\iTop\DesignElement;
/**
* Description of BrowseBrick
@@ -34,23 +32,6 @@ use DOMFormatException;
*/
class BrowseBrick extends PortalBrick
{
/**
* @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
* @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
* @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 */
@@ -116,18 +97,6 @@ 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.
*/
@@ -386,8 +355,13 @@ class BrowseBrick extends PortalBrick
$oTemplateNode = $oModeNode->GetOptionalElement('template');
if (($oTemplateNode !== null) && ($oTemplateNode->GetText() !== null))
{
$this->SetTemplatePath('page_'.$sModeId, $oTemplateNode->GetText());
$sTemplatePath = $oTemplateNode->GetText();
}
else
{
$sTemplatePath = 'itop-portal-base/portal/templates/bricks/browse/mode_'.$sModeId.'.html.twig';
}
$aModeData['template'] = $sTemplatePath;
$this->AddAvailableBrowseMode($sModeId, $aModeData);
}

View File

@@ -20,8 +20,8 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\DesignElement;
use DOMFormatException;
use Combodo\iTop\DesignElement;
/**
* Description of CreateBrick
@@ -35,6 +35,8 @@ 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';
/** @var string DEFAULT_CLASS */
const DEFAULT_CLASS = '';

View File

@@ -20,10 +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;
use Combodo\iTop\DesignElement;
/**
* Description of FilterBrick
@@ -36,12 +34,10 @@ class FilterBrick extends PortalBrick
{
// Overloaded constants
const DEFAULT_VISIBLE_NAVIGATION_MENU = false;
/**
* @deprecated 3.2.1
*/
const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/filter/tile.html.twig';
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';
/** @var string DEFAULT_TARGET_BRICK_CLASS */
const DEFAULT_TARGET_BRICK_CLASS = 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick';
/** @var string DEFAULT_SEARCH_PLACEHOLDER_VALUE */
@@ -64,15 +60,6 @@ class FilterBrick extends PortalBrick
/** @var string $sSearchSubmitClass */
protected $sSearchSubmitClass;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('tile', static::TEMPLATES_BASE_PATH . 'filter/tile.html.twig')
);
}
/**
* FilterBrick constructor.
*/

View File

@@ -20,14 +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 DBSearch;
use DOMFormatException;
use Exception;
use DOMFormatException;
use DBSearch;
use MetaModel;
use ModuleDesign;
use Combodo\iTop\DesignElement;
class ManageBrick extends PortalBrick
{
@@ -54,26 +51,16 @@ class ManageBrick extends PortalBrick
/** @var string ENUM_DISPLAY_MODE_BAR */
const ENUM_DISPLAY_MODE_BAR = 'bar-chart';
/** @var string ENUM_PAGE_TEMPLATE_PATH_TABLE
* @deprecated since 3.2.1
* */
/** @var string ENUM_PAGE_TEMPLATE_PATH_TABLE */
const ENUM_PAGE_TEMPLATE_PATH_TABLE = 'itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig';
/** @var string ENUM_PAGE_TEMPLATE_PATH_CHART
* @deprecated since 3.2.1
* */
/** @var string ENUM_PAGE_TEMPLATE_PATH_CHART */
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';
@@ -97,24 +84,20 @@ class ManageBrick extends PortalBrick
const DEFAULT_GROUP_SHOW_OTHERS = true;
/** @var array $aDisplayModes */
public static array $aDisplayModes = array(
static $aDisplayModes = array(
self::ENUM_DISPLAY_MODE_LIST,
self::ENUM_DISPLAY_MODE_PIE,
self::ENUM_DISPLAY_MODE_BAR,
);
/** @var array $aTileModes */
public static array $aTileModes = array(
public static $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,
);
/** @var array $aPresentationData
* @deprecated since 3.2.1
*/
/** @var array $aPresentationData */
public static $aPresentationData = array(
self::ENUM_TILE_MODE_BADGE => array(
'decorationCssClass' => 'fas fa-id-card fa-2x',
@@ -153,38 +136,6 @@ class ManageBrick extends PortalBrick
),
);
/** @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';
@@ -213,21 +164,48 @@ class ManageBrick extends PortalBrick
/** @var int $iDefaultListLength */
protected $iDefaultListLength;
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
/**
* Returns true if the $sDisplayMode need objects details for rendering.
*
* @param string $sDisplayMode
*
* @return bool
*/
static public function AreDetailsNeededForDisplayMode($sDisplayMode)
{
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),
);
$bNeedDetails = false;
foreach (static::$aPresentationData 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_PAGE_TEMPLATE_PATH;
foreach (static::$aPresentationData as $aData)
{
if ($aData['layoutDisplayMode'] === $sDisplayMode)
{
$sTemplate = $aData['layoutTemplate'];
break;
}
}
return $sTemplate;
}
/**
@@ -254,101 +232,6 @@ class ManageBrick extends PortalBrick
$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
*
@@ -443,24 +326,10 @@ class ManageBrick extends PortalBrick
return $this->sTileMode;
}
/**
* @deprecated since 3.2.1
*/
public function GetDecorationCssClass()
{
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)
*
@@ -476,8 +345,6 @@ class ManageBrick extends PortalBrick
}
/**
* @deprecated since 3.2.1
*
* @param string $sTileMode
*
* @return string[] parameters for specified type, default parameters if type is invalid
@@ -599,7 +466,7 @@ class ManageBrick extends PortalBrick
$this->iDefaultListLength = $iDefaultListLength;
return $this;
}
/**
* Adds a grouping.
*
@@ -914,11 +781,10 @@ class ManageBrick extends PortalBrick
case 'tile';
$this->SetTileMode($oDisplayNode->GetText(static::DEFAULT_TILE_MODE));
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']);
}
$aTileParametersForType = $this->GetPresentationDataForTileMode($this->sTileMode);
$this->SetTileTemplatePath($aTileParametersForType['tileTemplate']);
$this->SetPageTemplatePath($aTileParametersForType['layoutTemplate']);
break;
}
}
@@ -1052,19 +918,6 @@ 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;
}
}
@@ -1112,10 +965,10 @@ class ManageBrick extends PortalBrick
// Checking the navigation icon
$sDecorationClassNavigationMenu = $this->GetDecorationClassNavigationMenu();
if (empty($sDecorationClassNavigationMenu) && isset(static::$aDefaultTileData[$this->sTileMode]))
if (empty($sDecorationClassNavigationMenu) && isset(static::$aPresentationData[$this->sTileMode]))
{
/** @var string $sDecorationClassNavigationMenu */
$sDecorationClassNavigationMenu = static::$aDefaultTileData[$this->sTileMode]['decorationCssClass'];
$sDecorationClassNavigationMenu = static::$aPresentationData[$this->sTileMode]['decorationCssClass'];
if (!empty($sDecorationClassNavigationMenu))
{
$this->SetDecorationClassNavigationMenu($sDecorationClassNavigationMenu);

View File

@@ -20,11 +20,9 @@
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
@@ -72,8 +70,6 @@ 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 */
@@ -86,7 +82,7 @@ abstract class PortalBrick extends AbstractBrick
protected $sDecorationClassHome;
/** @var string $sDecorationClassNavigationMenu */
protected $sDecorationClassNavigationMenu;
/** @var string $sTileTemplatePath @deprecated since 3.2.1 */
/** @var string $sTileTemplatePath */
protected $sTileTemplatePath;
/** @var string|null $sTileControllerAction */
protected $sTileControllerAction;
@@ -103,17 +99,6 @@ 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
*/
@@ -136,9 +121,7 @@ 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;
// 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;
}
@@ -267,12 +250,10 @@ 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->GetTemplatePath('tile');
return $this->sTileTemplatePath;
}
/**
@@ -445,13 +426,10 @@ 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;
}
@@ -505,9 +483,7 @@ abstract class PortalBrick extends AbstractBrick
switch ($oBrickSubNode->nodeName)
{
case 'width':
$sWidth = $oBrickSubNode->GetText(static::DEFAULT_WIDTH);
$this->bIsWidthPixel = str_contains($sWidth, 'px');
$this->SetWidth((int)$sWidth);
$this->SetWidth((int)$oBrickSubNode->GetText(static::DEFAULT_WIDTH));
break;
case 'height':
@@ -550,7 +526,7 @@ abstract class PortalBrick extends AbstractBrick
{
/** @var \Combodo\iTop\DesignElement $oTemplateNode */
$oTemplateNode = $oTemplateNodeList->item(0);
$this->SetTemplatePath('tile', $oTemplateNode->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
$this->SetTileTemplatePath($oTemplateNode->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
}
break;

View File

@@ -20,8 +20,6 @@
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;
/**
@@ -34,10 +32,14 @@ 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';
/** @var bool DEFAULT_SHOW_PICTURE_FORM */
const DEFAULT_SHOW_PICTURE_FORM = true;
/** @var bool DEFAULT_SHOW_PREFERENCES_FORM */
@@ -57,17 +59,6 @@ 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'),
TemplateDefinitionDto::Create('user_info', static::TEMPLATES_BASE_PATH.'user-profile/user_info.html.twig'),
TemplateDefinitionDto::Create('user_info_ready_js', static::TEMPLATES_BASE_PATH.'user-profile/user_info.ready.js.twig'),
);
}
/**
* UserProfileBrick constructor.
*/

View File

@@ -20,11 +20,7 @@
namespace Combodo\iTop\Portal\Controller;
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\Bundle\FrameworkBundle\Controller\AbstractController as SymfonyAbstractController;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Service\Attribute\Required;
@@ -35,23 +31,8 @@ use Symfony\Contracts\Service\Attribute\Required;
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 2.3.0
*/
abstract class AbstractController extends SymfonyAbstractController implements TemplatesProviderInterface
abstract class AbstractController extends SymfonyAbstractController
{
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'),
TemplateDefinitionDto::Create('loader', static::TEMPLATES_BASE_PATH.'helpers/loader.html.twig'),
TemplateDefinitionDto::Create('tagset_clic_handler_js', static::TEMPLATES_BASE_PATH.'helpers/tagset_clic_handler.js.twig'),
TemplateDefinitionDto::Create('session_message', static::TEMPLATES_BASE_PATH.'helpers/session_messages/session_message.html.twig'),
TemplateDefinitionDto::Create('session_messages', static::TEMPLATES_BASE_PATH.'helpers/session_messages/session_messages.html.twig'),
);
}
/**
* @var \Symfony\Component\Routing\RouterInterface symfony router
*
@@ -65,25 +46,6 @@ abstract class AbstractController extends SymfonyAbstractController implements T
$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
*
@@ -142,33 +104,4 @@ abstract class AbstractController extends SymfonyAbstractController implements T
return $aRouteDefaults['_controller'];
}
/**
* Returns the controller template path
*
* @since 3.2.1
*
* @param string $sTemplateId
*
* @return string
*/
public function GetTemplatePath(string $sTemplateId): string
{
return static::GetTemplatesProviderService()->GetProviderInstanceTemplatePath($this, $sTemplateId);
}
/**
* Sets the brick template path
*
* @since 3.2.1
* @param string $sTemplateId
* @param string $sTileTemplatePath
*
* @return \Combodo\iTop\Portal\Controller\AbstractController
*/
public function SetTemplatePath(string $sTemplateId, string $sTileTemplatePath): AbstractController
{
static::GetTemplatesProviderService()->OverrideInstanceTemplatePath($this, $sTemplateId, $sTileTemplatePath);
return $this;
}
}

View File

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

View File

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

View File

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

View File

@@ -21,8 +21,6 @@
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;
@@ -35,15 +33,6 @@ 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
@@ -83,7 +72,7 @@ class DefaultController extends AbstractController
}
// Home page template
$sTemplatePath = $this->GetTemplatePath('home');
$sTemplatePath = $this->getParameter('combodo.portal.instance.conf')['properties']['templates']['home'];
return $this->render($sTemplatePath, $aData);
}

View File

@@ -40,8 +40,6 @@ 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;
@@ -72,20 +70,8 @@ use utils;
*/
class ManageBrickController extends BrickController
{
/**
* @var string EXCEL_EXPORT_TEMPLATE_PATH
* @deprecated since 3.2.1
*/
/** @var string EXCEL_EXPORT_TEMPLATE_PATH */
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
@@ -135,7 +121,7 @@ class ManageBrickController extends BrickController
$sDisplayMode = $oBrick->GetDefaultDisplayMode();
}
$aData = $this->GetData($oRequest, $sBrickId, $sGroupingTab, $oBrick->IsDetailsNeeded($sDisplayMode));
$aData = $this->GetData($oRequest, $sBrickId, $sGroupingTab, $oBrick::AreDetailsNeededForDisplayMode($sDisplayMode));
$aExportFields = $oBrick->GetExportFields();
$aData = $aData + array(
@@ -149,7 +135,7 @@ class ManageBrickController extends BrickController
}
else
{
$sLayoutTemplate = $oBrick->GetPageTemplate($sDisplayMode);
$sLayoutTemplate = $oBrick::GetPageTemplateFromDisplayMode($sDisplayMode);
$oResponse = $this->render($sLayoutTemplate, $aData);
}
@@ -181,7 +167,7 @@ class ManageBrickController extends BrickController
$aData = array();
}
return $this->render($oBrick->GetTileTemplate(), $aData);
return $this->render($oBrick->GetTileTemplatePath(), $aData);
}
/**
@@ -295,7 +281,7 @@ class ManageBrickController extends BrickController
'sWikiUrl' => 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Auser%3Alists#excel_export',
);
return $this->render($this->GetTemplatePath('modal_export_excel'), $aData);
return $this->render(static::EXCEL_EXPORT_TEMPLATE_PATH, $aData);
}
/**

View File

@@ -37,8 +37,6 @@ 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;
@@ -76,24 +74,6 @@ 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_edit', static::TEMPLATES_BASE_PATH.'bricks/object/mode_edit.html.twig', true, 'edit'),
TemplateDefinitionDto::Create('mode_search_hierarchy', static::TEMPLATES_BASE_PATH.'bricks/object/mode_search_hierarchy.html.twig', true, 'search_hierarchy'),
TemplateDefinitionDto::Create('mode_search_regular', static::TEMPLATES_BASE_PATH.'bricks/object/mode_search_regular.html.twig', true, 'search_regular'),
TemplateDefinitionDto::Create('mode_view', static::TEMPLATES_BASE_PATH.'bricks/object/mode_view.html.twig', true, 'view'),
TemplateDefinitionDto::Create('mode_apply_stimulus', static::TEMPLATES_BASE_PATH.'bricks/object/mode_apply_stimulus.html.twig', true, 'apply_stimulus'),
TemplateDefinitionDto::Create('mode_loader', static::TEMPLATES_BASE_PATH.'modal/mode_loader.html.twig'),
TemplateDefinitionDto::Create('plugins_buttons', static::TEMPLATES_BASE_PATH.'bricks/object/plugins_buttons.html.twig'),
);
}
/**
* @param \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper
* @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidatorHelper
@@ -102,11 +82,8 @@ class ObjectController extends BrickController
* @param \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection
* @param \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandlerHelper
* @param \Combodo\iTop\Portal\Helper\NavigationRuleHelper $oNavigationRuleHelper
* @param \Combodo\iTop\Portal\Helper\ContextManipulatorHelper $oContextManipulatorHelper
* @param array $aCombodoPortalInstanceConf
*
* @since 3.2.0 N°6933
* @since 3.2.1 Added $aCombodoPortalInstanceConf parameter
*/
public function __construct(
protected SecurityHelper $oSecurityHelper,
@@ -116,11 +93,10 @@ class ObjectController extends BrickController
protected BrickCollection $oBrickCollection,
protected ObjectFormHandlerHelper $oObjectFormHandlerHelper,
protected NavigationRuleHelper $oNavigationRuleHelper,
protected ContextManipulatorHelper $oContextManipulatorHelper,
protected array $aCombodoPortalInstanceConf = []
protected ContextManipulatorHelper $oContextManipulatorHelper
)
{
}
/**
@@ -256,7 +232,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($this->GetTemplatePath('modal'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
} else {
$oResponse = new JsonResponse($aData);
}
@@ -270,7 +246,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
return $oResponse;
@@ -331,7 +307,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($this->GetTemplatePath('modal'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
else
{
@@ -351,7 +327,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
return $oResponse;
@@ -558,11 +534,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($this->GetTemplatePath('modal'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
elseif ($sOperation === 'redirect')
{
$oResponse = $this->render($this->GetTemplatePath('mode_loader'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/modal/mode_loader.html.twig', $aData);
}
else
{
@@ -571,7 +547,7 @@ class ObjectController extends BrickController
}
else
{
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
return $oResponse;
@@ -1035,12 +1011,12 @@ class ObjectController extends BrickController
if ($oRequest->isXmlHttpRequest())
{
$oResponse = $this->render($this->GetTemplatePath('modal'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
else
{
//throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
}
else
@@ -1621,7 +1597,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($this->GetTemplatePath('modal'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
else
{
@@ -1641,7 +1617,7 @@ class ObjectController extends BrickController
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render($this->GetTemplatePath('page'), $aData);
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
return $oResponse;
@@ -1675,7 +1651,7 @@ class ObjectController extends BrickController
if (!empty($sBrickId))
{
$oBrick = $this->oBrickCollection->GetBrickById($sBrickId);
$sTemplatePath = $oBrick->GetTemplatePath('page');
$sTemplatePath = $oBrick->GetPageTemplatePath();
$aData['sBrickId'] = $sBrickId;
$aData['oBrick'] = $oBrick;

View File

@@ -152,7 +152,7 @@ class UserProfileBrickController extends BrickController
// Preparing forms
$aData['forms']['contact'] = $this->ObjectFormHandlerHelper->HandleForm($oRequest, $sFormMode, $sCurContactClass,
$sCurContactId,
);
$oBrick->GetForm());
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
// - If user can change password, we display the form
$aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null;
@@ -166,7 +166,7 @@ class UserProfileBrickController extends BrickController
$this->ManageUserProfileBrickExtensibility($sTab, $aData);
$oResponse = $this->render($oBrick->GetTemplatePath('page'), $aData);
$oResponse = $this->render($oBrick->GetPageTemplatePath(), $aData);
}
return $oResponse;
@@ -428,7 +428,7 @@ class UserProfileBrickController extends BrickController
'sObjectField' => $sPictureAttCode,
'cache' => 86400,
's' => $oOrmDoc->GetSignature(),
]);
]);
$aFormData['validation'] = array(
'valid' => true,
'messages' => array(),

View File

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

View File

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

View File

@@ -100,9 +100,9 @@ class ApplicationHelper
try
{
// Allowed profiles
if (utils::IsNotNullOrEmptyString($oBrick->GetAllowedProfilesOql()))
if ($oBrick->GetAllowedProfilesOql() !== null && $oBrick->GetAllowedProfilesOql() !== '')
{
$oSearch = DBObjectSearch::FromOQL_AllData($oBrick->GetAllowedProfilesOql());
$oSearch = DBObjectSearch::FromOQL($oBrick->GetAllowedProfilesOql());
$oSet = new DBObjectSet($oSearch);
while ($oProfile = $oSet->Fetch())
{
@@ -111,9 +111,9 @@ class ApplicationHelper
}
// Denied profiles
if (utils::IsNotNullOrEmptyString($oBrick->GetDeniedProfilesOql()))
if ($oBrick->GetDeniedProfilesOql() !== null && $oBrick->GetDeniedProfilesOql() !== '')
{
$oSearch = DBObjectSearch::FromOQL_AllData($oBrick->GetDeniedProfilesOql());
$oSearch = DBObjectSearch::FromOQL($oBrick->GetDeniedProfilesOql());
$oSet = new DBObjectSet($oSearch);
while ($oProfile = $oSet->Fetch())
{

View File

@@ -124,15 +124,17 @@ class ObjectFormHandlerHelper
* @throws \OQLException
* @throws \Exception
*/
public function HandleForm(Request $oRequest, $sMode, $sObjectClass, $sObjectId = null, array $aFormProperties = null)
public function HandleForm(Request $oRequest, $sMode, $sObjectClass, $sObjectId = null, $aFormProperties = null)
{
$aFormData = array();
$sOperation = $this->oRequestManipulator->ReadParam('operation', '');
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
// - Retrieve form properties
$aFormProperties = $aFormProperties ?? ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
if ($aFormProperties === null)
{
$aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
}
// - Create and
if (empty($sOperation))
{
@@ -279,8 +281,7 @@ class ObjectFormHandlerHelper
->SetActionRulesToken($sActionRulesToken)
->SetRenderer($oFormRenderer)
->SetFormProperties($aFormProperties);
$oFormManager->PrepareFormAndHTMLDocument();
$oFormManager->PrepareFields();
$oFormManager->Build();
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
// Check the number of editable fields

View File

@@ -1,145 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Portal\Service\TemplatesProvider;
/**
* Template definition DTO.
*
* Describes a template.
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
class TemplateDefinitionDto
{
/**
* Create a new template definition instance.
*
* @param string $sTemplateId template identifier
* @param string $sPath template path
* @param bool $isOverridable flag set when the template is overridable
* @param string|null $sAlias template alias
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto
*/
public static function Create(string $sTemplateId, string $sPath, bool $isOverridable = true, ?string $sAlias = null): TemplateDefinitionDto
{
return new TemplateDefinitionDto($sTemplateId, $sPath, $isOverridable, $sAlias);
}
/** @var bool $bIsOverridden flag set when overriding a template */
private bool $bIsOverridden = false;
/** @var string|null $sInitialValue Initial template value */
private ?string $sInitialValue;
/**
* Constructor.
*
* @param string $sId template identifier
* @param string|null $sPath template path
* @param bool|null $bIsOverridable flag set when the template is overridable
* @param string|null $sAlias template alias
*/
private function __construct(
private readonly string $sId,
private ?string $sPath = null,
private readonly ?bool $bIsOverridable = false,
private readonly ?string $sAlias = null,
)
{
// save overridable values
$this->sInitialValue = $sPath;
}
/**
* Return the template identifier.
*
* @return string
*/
public function GetId(): string
{
return $this->sId !== null ? $this->sId : '';
}
/**
* Return the template path.
*
* @param bool $bInitialValue Return the initial value instead of the overridden one.
*
* @return string
*/
public function GetPath(bool $bInitialValue = false): string
{
if($bInitialValue){
return $this->sInitialValue !== null ? $this->sInitialValue : '';
}
return $this->sPath !== null ? $this->sPath : '';
}
/**
* Return the overridable state.
*
* @return bool
*/
public function IsOverridable(): bool
{
return $this->bIsOverridable !== null ? $this->bIsOverridable : false;
}
/**
* Return the template alias.
*
* @return string
*/
public function GetAlias(): string
{
return $this->sAlias !== null ? $this->sAlias : '';
}
/**
* Override a template.
*
* @param string $sPath template path
*
* @return $this
*/
public function OverrideTemplatePath(string $sPath): TemplateDefinitionDto
{
if ($this->IsOverridable() && $sPath !== $this->sPath) {
$this->sPath = $sPath;
$this->bIsOverridden = true;
}
return $this;
}
/**
* Return the overridden flag.
*
* @noinspection PhpUnused
*/
public function IsOverridden(): bool
{
return $this->bIsOverridden;
}
}

View File

@@ -1,39 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Portal\Service\TemplatesProvider;
/**
* Template provider interface.
*
* This interface is used to register templates in the templates provider service.
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
interface TemplatesProviderInterface
{
/**
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister $oTemplatesRegister
*
* @return void
*/
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void;
}

View File

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

View File

@@ -1,133 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Portal\Service\TemplatesProvider;
/**
* Template register.
*
*
* @package Combodo\iTop\Portal\Service\TemplatesProvider
* @since 3.2.1
*/
class TemplatesRegister
{
/** @var array Templates definitions (possibly altered by portal configuration) */
private array $aTemplatesDefinitions = [];
public function __construct(private string $sTemplateUIVersion = 'unset')
{
}
/**
* @return string
*/
public function GetUIVersion(): string
{
return $this->sTemplateUIVersion;
}
public function IsProviderExists(string $sProviderId): bool
{
return array_key_exists($sProviderId, $this->aTemplatesDefinitions);
}
public function IsTemplateExists(string $sProviderId, string $sTemplateId): bool
{
return array_key_exists($sProviderId, $this->aTemplatesDefinitions) && array_key_exists($sTemplateId, $this->aTemplatesDefinitions[$sProviderId]);
}
/**
* Register templates.
*
* @param string $sProviderId the templates provider id
* @param \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto ...$aTemplatesDefinitions
*
* @return \Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister
*/
public function RegisterTemplates(string $sProviderId, TemplateDefinitionDto...$aTemplatesDefinitions): TemplatesRegister
{
// prevent child classes to erase parent templates
if (array_key_exists($sProviderId, $this->aTemplatesDefinitions)) {
return $this;
}
// register templates...
$this->aTemplatesDefinitions[$sProviderId] = [];
foreach ($aTemplatesDefinitions as $oTemplateDefinition) {
$this->aTemplatesDefinitions[$sProviderId][$oTemplateDefinition->GetId()] = $oTemplateDefinition;
}
return $this;
}
/**
* Get a template definition.
*
* @param string $sProviderId the templates provider id
* @param string $sTemplateId the template id
*
* @return TemplateDefinitionDto|null
*/
public function GetTemplateDefinition(string $sProviderId, string $sTemplateId): ?TemplateDefinitionDto
{
// retrieve template path
if (array_key_exists($sProviderId, $this->aTemplatesDefinitions)) {
// search in template definitions
if (array_key_exists($sTemplateId, $this->aTemplatesDefinitions[$sProviderId])) {
return $this->aTemplatesDefinitions[$sProviderId][$sTemplateId];
}
// search in aliases
foreach ($this->aTemplatesDefinitions[$sProviderId] as $item) {
/** @var \Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto $item */
if ($item->GetAlias() === $sTemplateId) {
return $item;
}
}
}
return null;
}
/**
* @param string $sProviderId
*
* @return array
*/
public function GetProviderTemplatesIds(string $sProviderId): array
{
return array_map(fn($oTemplateDefinition) => $oTemplateDefinition->GetId(), $this->aTemplatesDefinitions[$sProviderId] ?? ['tile', 'page']);
}
/**
* Return templates definitions.
*
* @return array
*/
public function GetTemplatesDefinitions(): array
{
return $this->aTemplatesDefinitions;
}
}

View File

@@ -1,81 +0,0 @@
<?php
/**
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Portal\Twig;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesProviderService;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* New Twig function useful for extending or including template handled by templates provider service.
*
* @package Combodo\iTop\Portal\Twig
* @since 3.2.1
*/
class TemplatesTwigExtension extends AbstractExtension
{
const DEFAULT_PROVIDER_CLASS = 'Combodo\\iTop\\Portal\\Controller\\AbstractController';
public function __construct(private readonly TemplatesProviderService $oTemplatesService)
{
}
/** @inheritdoc */
public function getFunctions(): array
{
return [
new TwigFunction('template', [$this, 'GetTemplate'], ['id' => null, 'provider' => null, 'provider_instance' => null]),
new TwigFunction('template_initial', [$this, 'GetInitialTemplate'], ['id' => null, 'provider' => null]),
];
}
/**
* Retrieve the path of the desired template (maybe overridden by configuration or by instance).
*
* @param string $sId template identifier
* @param string $sProviderClass provider class FQN
* @param object|null $oProviderInstance the provider instance
*
* @return string the template path
* @throws \ReflectionException
*/
public function GetTemplate(string $sId, string $sProviderClass = self::DEFAULT_PROVIDER_CLASS, object $oProviderInstance = null): string
{
if ($oProviderInstance === null) {
return $this->oTemplatesService->GetTemplatePath($sProviderClass, $sId);
} else {
return $this->oTemplatesService->GetProviderInstanceTemplatePath($oProviderInstance, $sId);
}
}
/**
* Retrieve the initial path of the desired template (hardcoded).
*
* @param string $sId template identifier
* @param string $sProviderClass provider class FQN
*
* @return string the template path
* @throws \ReflectionException
*/
public function GetInitialTemplate(string $sId, string $sProviderClass = self::DEFAULT_PROVIDER_CLASS): string
{
return $this->oTemplatesService->GetTemplatePath($sProviderClass, $sId, true);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick', oBrick) %}
{# itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig #}
{# Browse brick tree mode layout #}
{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %}
{#
Documentation :
@@ -34,7 +36,7 @@
</div>
<div id="brick_tree_overlay">
<div class="overlay_content">
{% include template('loader') %}
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
</div>
</div>
{% endblock %}

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\ManageBrick', oBrick) %}
{# 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' %}
{% block pPageBodyClass %}{{ parent() }} page_manage_brick{% endblock %}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# 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' %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}

View File

@@ -30,7 +30,7 @@
{# Misc. buttons #}
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
<div class="form_btn_misc">
{% include template('plugins_buttons', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') with {'aButtons': form.buttons} %}
{% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
</div>
{% endif %}
{# Transition buttons #}

View File

@@ -1,3 +1,5 @@
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# 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' %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}

View File

@@ -1,4 +1,6 @@
{% extends template('mode_create', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') %}
{# 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' %}
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}
@@ -6,7 +8,7 @@
{# Misc. buttons #}
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
<div class="form_btn_misc">
{% include template('plugins_buttons', 'Combodo\\iTop\\Portal\\Controller\\ObjectController') with {'aButtons': form.buttons} %}
{% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
</div>
{% endif %}

View File

@@ -1,4 +1,6 @@
{% extends template('page', 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick') %}
{# 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' %}
{% if sTab == "" %}
{% set sTab = "user-info" %}
@@ -55,7 +57,7 @@
{% set oContactForm = forms.contact %}
{% set oPreferencesForm = forms.preferences %}
{% set oPasswordForm = forms.password %}
{% include template('user_info', 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick') %}
{% include 'itop-portal-base/portal/templates/bricks/user-profile/user_info.html.twig' %}
{% else %}
<div id="user-profile-wrapper">
<div class="row">
@@ -80,7 +82,7 @@
{% set oContactForm = forms.contact %}
{% set oPreferencesForm = forms.preferences %}
{% set oPasswordForm = forms.password %}
{% include template('user_info_ready_js', 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick') %}
{% include 'itop-portal-base/portal/templates/bricks/user-profile/user_info.ready.js.twig' %}
{% endif %}
{% endblock %}

View File

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

View File

@@ -1,5 +1,5 @@
<div id="session-messages">
{% for aSessionMessage in app['session_message_helper'] %}
{% include template('session_message') with {aSessionMessage: aSessionMessage} %}
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_message.html.twig' with {aSessionMessage: aSessionMessage} %}
{% endfor %}
</div>

View File

@@ -11,7 +11,7 @@
<div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
<section class="row">
<div class="col-xs-12">
{% include template('session_messages') %}
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_messages.html.twig' %}
</div>
</section>
<section class="row tiles_wrapper">

View File

@@ -335,7 +335,7 @@
<div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
<section class="row">
<div class="col-xs-12">
{% include template('session_messages') %}
{% include 'itop-portal-base/portal/templates/helpers/session_messages/session_messages.html.twig' %}
</div>
</section>
<section class="row" id="main-header">
@@ -367,7 +367,7 @@
<div class="modal fade" id="modal-for-all" role="dialog" tabindex="-1">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
{% include template('loader') %}
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
</div>
</div>
</div>
@@ -395,7 +395,7 @@
{% block pPageOverlay %}
<div id="page_overlay" class="global_overlay">
<div class="overlay_content">
{% include template('loader') %}
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
</div>
</div>
{% endblock %}

View File

@@ -3,7 +3,7 @@
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalContent %}
{% include template('loader') %}
{% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% if redirection is defined and redirection.url is defined %}
<script type="text/javascript">

View File

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

View File

@@ -27,7 +27,6 @@ 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,10 +59,6 @@ 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',
@@ -71,7 +66,6 @@ return array(
'Combodo\\iTop\\Portal\\Twig\\CurrentUserAccessor' => $baseDir . '/src/Twig/CurrentUserAccessor.php',
'Combodo\\iTop\\Portal\\Twig\\PortalBlockExtension' => $baseDir . '/src/Twig/PortalBlockExtension.php',
'Combodo\\iTop\\Portal\\Twig\\PortalTwigContext' => $baseDir . '/src/Twig/PortalTwigContext.php',
'Combodo\\iTop\\Portal\\Twig\\TemplatesTwigExtension' => $baseDir . '/src/Twig/TemplatesTwigExtension.php',
'Combodo\\iTop\\Portal\\UrlMaker\\AbstractPortalUrlMaker' => $baseDir . '/src/UrlMaker/AbstractPortalUrlMaker.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractStringVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractStringVariableAccessor.php',
'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractVariableAccessor.php',

View File

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

View File

@@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInitd751713988987e9331980363e24189ce
class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b
{
public static $prefixLengthsPsr4 = array (
'C' =>
@@ -47,7 +47,6 @@ class ComposerStaticInitd751713988987e9331980363e24189ce
'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,10 +79,6 @@ class ComposerStaticInitd751713988987e9331980363e24189ce
'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',
@@ -91,7 +86,6 @@ class ComposerStaticInitd751713988987e9331980363e24189ce
'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',
@@ -103,9 +97,9 @@ class ComposerStaticInitd751713988987e9331980363e24189ce
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitd751713988987e9331980363e24189ce::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd751713988987e9331980363e24189ce::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd751713988987e9331980363e24189ce::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$classMap;
}, null, ClassLoader::class);
}

View File

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

View File

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

View File

@@ -183,103 +183,8 @@
<class id="SynchroLog"/>
</classes>
</group>
<group id="AdminSysReadOnly" _delta="define">
<classes>
<class id="ItopFenceLogin"/>
</classes>
</group>
<group id="AdminSys" _delta="define">
<classes>
<class id="ResourceDesignerConnectorMenu"/>
<class id="ResourceSystemMenu"/>
<class id="RessourceHybridAuthMenu"/>
</classes>
</group>
</groups>
<profiles>
<profile id="117" _delta="define">
<name>SuperUser</name>
<description>This profil allows all actions which are not Administrator restricted.</description>
<groups>
<group id="AdminTools">
<actions>
<action id="action:read">allow</action>
<action id="action:write">allow</action>
<action id="action:delete">allow</action>
<action id="action:bulk read">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:bulk delete">allow</action>
</actions>
</group>
<group id="AdminSysReadOnly">
<actions>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
<group id="AdminSys">
<actions>
<action id="action:read">allow</action>
<action id="action:write">allow</action>
</actions>
</group>
<group id="History">
<actions>
<action id="action:bulk read">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
<action id="action:bulk delete">allow</action>
</actions>
</group>
<group id="UserRequest">
<actions>
<action id="stimulus:ev_approve">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_close">allow</action>
<action id="stimulus:ev_dispatch">allow</action>
<action id="stimulus:ev_pending">allow</action>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_reject">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_resolve">allow</action>
</actions>
</group>
<group id="Incident">
<actions>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_resolve">allow</action>
<action id="stimulus:ev_close">allow</action>
<action id="stimulus:ev_pending">allow</action>
</actions>
</group>
<group id="Change">
<actions>
<action id="stimulus:ev_approve">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_finish">allow</action>
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_reject">allow</action>
<action id="stimulus:ev_reopen">allow</action>
</actions>
</group>
<group id="Problem">
<actions>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_resolve">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
</groups>
</profile>
<profile id="3" _delta="define">
<name>Configuration Manager</name>
<description>Person in charge of the documentation of the managed CIs</description>

View File

@@ -74,7 +74,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:Organization/Attribute:deliverymodel_id' => '交付模式',
'Class:Organization/Attribute:deliverymodel_id+' => 'This is required for Tickets handling.
The delivery model specifies the teams to which tickets can be assigned.~~',
The delivery model specifies the teams to which tickets can be assigned.~~',
'Class:Organization/Attribute:deliverymodel_name' => '交付模式名称',
]);

View File

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

View File

@@ -31,7 +31,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
// Create
'UI:Links:Create:Button' => '创建',
'UI:Links:Create:Button+' => '创建一个 %4$s',
'UI:Links:Create:Modal:Title' => '创建一个 %4$s 至 %2$s',
'UI:Links:Create:Modal:Title' => '创建一个 %4$s 至% 2$s',
// Add
'UI:Links:Add:Button' => '添加',

View File

@@ -1,20 +1,28 @@
<?php
/**
* Localized data
* Copyright (C) 2013-2024 Combodo SAS
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
*/
/**
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
// Quick create
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:Component:QuickCreate:HistoryDisabled' => '历史记录已禁用',
'UI:Component:QuickCreate:Input:Placeholder' => '请选择对象类型...',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => '打开快速创建',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => '您尚未创建任何对象',
'UI:Component:QuickCreate:MostPopular:Title' => '最常用',
'UI:Component:QuickCreate:Recents:Title' => '最近',
'UI:Component:QuickCreate:Tooltip' => '快速创建任意类型的对象',
]);
'UI:Component:QuickCreate:Input:Placeholder' => '请选择对象类型...',
'UI:Component:QuickCreate:Recents:Title' => '最近',
'UI:Component:QuickCreate:LastClasses:NoClass:Placeholder' => '您尚未创建任何对象',
'UI:Component:QuickCreate:HistoryDisabled' => '历史记录已禁用',
'UI:Component:QuickCreate:KeyboardShortcut:OpenDrawer' => '打开快速创建',
]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3
node_modules/.package-lock.json generated vendored
View File

@@ -71,8 +71,7 @@
},
"node_modules/ckeditor5-itop-build": {
"version": "3.2.0",
"resolved": "git+ssh://git@github.com/Combodo/ckeditor5-itop-build.git#deab849bf05e66948c5e66a57079ad1baea66b94",
"license": "SEE LICENSE IN LICENSE.md"
"resolved": "git+ssh://git@github.com/Combodo/ckeditor5-itop-build.git#7366299c6c4b659736b958c5e9496e98c22a3c36"
},
"node_modules/clipboard": {
"version": "2.0.11",

Some files were not shown because too many files have changed in this diff Show More