diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index d0ece5dc1..75683b21d 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -988,6 +988,7 @@ HTML; $oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']); $oField->SetMandatory(); + $oField->AddCSSClass("ibo-queryoql"); $oForm->AddField($oField); $oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']); @@ -1024,6 +1025,7 @@ HTML; $oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL); $oField->SetMandatory(); + $oField->AddCSSClass("ibo-queryoql"); $oForm->AddField($oField); $oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']); @@ -1373,10 +1375,10 @@ abstract class DashletGroupBy extends Dashlet $oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']); $oField->SetMandatory(); + $oField->AddCSSClass("ibo-queryoql"); $oForm->AddField($oField); - try - { + try { // Group by field: build the list of possible values (attribute codes + ...) $aGroupBy = $this->GetGroupByOptions($this->aProperties['query']); @@ -1630,16 +1632,14 @@ abstract class DashletGroupBy extends Dashlet $oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL); $oField->SetMandatory(); + $oField->AddCSSClass("ibo-queryoql"); $oForm->AddField($oField); - if (!is_null($sOQL)) - { + if (!is_null($sOQL)) { $oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null); $aGroupBy = $this->GetGroupByOptions($sOQL); $oField->SetAllowedValues($aGroupBy); - } - else - { + } else { // Creating a form for reading parameters! $oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null); } @@ -2183,6 +2183,7 @@ class DashletHeaderDynamic extends Dashlet $oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']); $oField->SetMandatory(); + $oField->AddCSSClass("ibo-queryoql"); $oForm->AddField($oField); try diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 680896a29..cb376a810 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -151,12 +151,6 @@ class DisplayBlock 'order_direction', /** string order direction 'asc' or 'desc' */ 'display_limit', - 'surround_with_panel', - /**bool true if list may be render in panel block*/ - 'panel_title', - /**string title of panel block*/ - 'panel_class', - /**string class for panel block style*/ ], 'csv' => [], 'join' => array_merge([ @@ -179,12 +173,6 @@ class DisplayBlock /**positive or negative*/ 'max_height', /** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */ - 'surround_with_panel', - /**bool true if list may be render in panel block*/ - 'panel_title', - /**string title of panel block*/ - 'panel_class', - /**string class for panel block style*/ ], DataTableUIBlockFactory::GetAllowedParams()), 'list_search' => array_merge([ 'update_history', @@ -271,6 +259,12 @@ class DisplayBlock 'withJSRefreshCallBack', /** true if dashboard page */ 'from_dashboard_page', + /**bool true if list may be render in panel block*/ + 'surround_with_panel', + /**string title of panel block*/ + 'panel_title', + /**string class for panel block style*/ + 'panel_class', ]; if (isset($aAllowedParams[$sStyle])) { @@ -1040,10 +1034,12 @@ JS $sHyperlink = $aCount['link']; $sCountLabel = $aCount['label']; $oPill = PillFactory::MakeForState($sClass, $sStateValue) - ->SetUrl($sHyperlink) ->SetTooltip($sStateLabel) ->AddHtml("$sCountLabel") ->AddHtml("$sStateLabel"); + if ($sHyperlink != '-') { + $oPill->SetUrl($sHyperlink); + } $oBlock->AddSubBlock($oPill); } $aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams(); @@ -1541,6 +1537,13 @@ JS $oBlock->sUrl = $sUrl; + if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) { + $oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]); + $oPanel->AddSubBlock($oBlock); + + return $oPanel; + } + return $oBlock; } @@ -1622,6 +1625,13 @@ JS $oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl); break; } + if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) { + $oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]); + $oPanel->AddSubBlock($oBlock); + + return $oPanel; + } + return $oBlock; } diff --git a/application/forms.class.inc.php b/application/forms.class.inc.php index ca50712b2..906882d22 100644 --- a/application/forms.class.inc.php +++ b/application/forms.class.inc.php @@ -203,51 +203,41 @@ class DesignerForm $sActionUrl = addslashes($this->sSubmitTo); $sJSSubmitParams = json_encode($this->aSubmitParams); $sFormId = $this->GetFormId(); - if ($this->oParentForm == null) - { + if ($this->oParentForm == null) { $sReturn = '
'; $sReturn .= ''; - $sReturn .= ''; + $sReturn .= ''; } $sHiddenFields = ''; - foreach($this->aFieldSets as $sLabel => $aFields) - { + foreach ($this->aFieldSets as $sLabel => $aFields) { $aDetails = array(); - if ($sLabel != '') - { + if ($sLabel != '') { $sReturn .= $this->StartRow().''.$this->EndRow(); } - foreach($aFields as $oField) - { + foreach ($aFields as $oField) { $aRow = $oField->Render($oP, $sFormId, 'property'); - if ($oField->IsVisible()) - { + if ($oField->IsVisible()) { $sFieldId = $this->GetFieldId($oField->GetCode()); - $sValidation = $this->GetValidationArea($sFieldId, ''); - $sValidationFields = ''.$this->EndRow(); - - $sPath = $this->GetHierarchyPath().'/'.$oField->GetCode(); - - if (is_null($aRow['label'])) - { + $sValidation = $this->GetValidationArea($sFieldId, ''); + $sValidationFields = '' + .$this->EndRow(); + + if (is_null($aRow['label'])) { $sReturn .= $this->StartRow($sFieldId).''; + + if ($sRenderMode == 'property') { + $sHtml .= ''; } - foreach($this->aSubForms as $sKey => $aFormData) - { + foreach ($this->aSubForms as $sKey => $aFormData) { $sId = $this->oForm->GetFieldId($this->sCode); $sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"'; $oSubForm = $aFormData['form']; $oSubForm->SetParentForm($this->oForm); $oSubForm->CopySubmitParams($this->oForm); $oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_'); - - if ($sRenderMode == 'property') - { + + if ($sRenderMode == 'property') { // Note: Managing the visibility of nested subforms had several implications // 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection // 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs). diff --git a/css/backoffice/components/_form.scss b/css/backoffice/components/_form.scss index c4bd7eb2b..69821acf7 100644 --- a/css/backoffice/components/_form.scss +++ b/css/backoffice/components/_form.scss @@ -2,3 +2,7 @@ * copyright Copyright (C) 2010-2021 Combodo SARL * license http://opensource.org/licenses/AGPL-3.0 */ +.ibo-prop-header { + @extend %ibo-font-size-150; + padding-bottom: 14px; +} \ No newline at end of file diff --git a/css/backoffice/layout/dashboard/_dashboard-editor.scss b/css/backoffice/layout/dashboard/_dashboard-editor.scss index ef92b7451..e6fa28fb4 100644 --- a/css/backoffice/layout/dashboard/_dashboard-editor.scss +++ b/css/backoffice/layout/dashboard/_dashboard-editor.scss @@ -44,15 +44,17 @@ $ibo-dashboard-editor--delete-dashlet-icon--z-index: 21 !default; flex-direction: column; padding-bottom: 20px; table{ - width: 100%; - text-align: left; - td{ - margin-bottom: 14px; - .ibo-field{ - @extend %ibo-font-size-100; - } - } - } + width: 100%; + text-align: left; + + td, th { + margin-bottom: 14px; + + .ibo-field { + @extend %ibo-font-size-100; + } + } + } } .ibo-dashboard-editor--properties-title{ padding-bottom: $ibo-dashboard-editor--properties-title--padding-bottom; diff --git a/dictionaries/en.dictionary.itop.ui.php b/dictionaries/en.dictionary.itop.ui.php index 912112289..e67547aeb 100644 --- a/dictionaries/en.dictionary.itop.ui.php +++ b/dictionaries/en.dictionary.itop.ui.php @@ -1232,6 +1232,8 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:DashboardEdit:AutoReload' => 'Automatic refresh', 'UI:DashboardEdit:AutoReloadSec' => 'Automatic refresh interval (seconds)', 'UI:DashboardEdit:AutoReloadSec+' => 'The minimum allowed is %1$d seconds', + 'UI:DashboardEdit:Revert' => 'Revert', + 'UI:DashboardEdit:Apply' => 'Apply', 'UI:DashboardEdit:Layout' => 'Layout', 'UI:DashboardEdit:Properties' => 'Dashboard Properties', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 694443cf2..a3afc8702 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -1219,6 +1219,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:DashboardEdit:Properties' => 'Propriétés du tableau de bord', 'UI:DashboardEdit:Dashlets' => 'Indicateurs', 'UI:DashboardEdit:DashletProperties' => 'Propriétés de l\'Indicateur', + 'UI:DashboardEdit:Revert' => 'Revenir à la valeur précédente', + 'UI:DashboardEdit:Apply' => 'Appliquer', 'UI:Form:Property' => 'Propriété', 'UI:Form:Value' => 'Valeur',
'.Dict::S('UI:Form:Property').''.Dict::S('UI:Form:Value').' 
'.Dict::S('UI:Form:Property').''.Dict::S('UI:Form:Value').' 
'.$sLabel.''.$sValidation.''.$sValidation.''.$aRow['value']; - } - else - { + } else { $sReturn .= $this->StartRow($sFieldId).''.$aRow['label'].''.$aRow['value']; } - if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) - { + if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) { $sReturn .= $sValidationFields; } $sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'"; $sAutoApply = $oField->IsAutoApply() ? 'true' : 'false'; $sHandlerEquals = $oField->GetHandlerEquals(); $sHandlerGetValue = $oField->GetHandlerGetValue(); - + $sWidgetClass = $oField->GetWidgetClass(); $sJSExtraParams = ''; if (count($oField->GetWidgetExtraParams()) > 0) @@ -1423,28 +1413,32 @@ class DesignerIconSelectionField extends DesignerFormField $sId = $this->oForm->GetFieldId($this->sCode); $sName = $this->oForm->GetFieldName($this->sCode); $idx = 0; - foreach($this->aAllowedValues as $index => $aValue) - { - if ($aValue['value'] == $this->defaultValue) - { + $idxFallback = 0; + foreach ($this->aAllowedValues as $index => $aValue) { + if ($aValue['value'] == $this->defaultValue) { $idx = $index; break; } + //fallback if url of default value contains ../ + //for contact, icon is http://localhost/env-production/itop-structure/../../images/icons/icons8-customer.svg => not found http://localhost/images/icons/icons8-customer.svg + if (basename($aValue['value']) == basename($this->defaultValue)) { + $idxFallback = $index; + } + } + if ($idx == 0) { + $idx = $idxFallback; } $sJSItems = json_encode($this->aAllowedValues); $sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'"; - if (!$this->IsReadOnly()) - { + if (!$this->IsReadOnly()) { $sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value']; $sValue = ""; $oP->add_ready_script( -<<aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').''; } $sReadOnly = $this->IsReadOnly() ? 'disabled' : ''; @@ -1459,18 +1453,21 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField public function __construct($sCode, $sLabel = '', $defaultValue = '') { parent::__construct($sCode, $sLabel, $defaultValue); + $aFolderList = [ + APPROOT.'env-'.utils::GetCurrentEnvironment() => utils::GetAbsoluteUrlModulesRoot(), + APPROOT.'images/icons' => utils::GetAbsoluteUrlAppRoot().'images/icons', + ]; + if (count(self::$aAllIcons) == 0) { + foreach ($aFolderList as $sFolderPath => $sUrlPrefix) { + $aIcons = self::FindIconsOnDisk($sFolderPath); + ksort($aIcons); - if (count(self::$aAllIcons) == 0) - { - self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment()); - ksort(self::$aAllIcons); + foreach ($aIcons as $sFilePath) { + self::$aAllIcons[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => $sUrlPrefix.$sFilePath); + } + } } - $aValues = array(); - foreach(self::$aAllIcons as $sFilePath) - { - $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath); - } - $this->SetAllowedValues($aValues); + $this->SetAllowedValues(self::$aAllIcons); } static protected function FindIconsOnDisk($sBaseDir, $sDir = '') @@ -1501,26 +1498,29 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField SetupUtils::builddir(dirname($sCacheFile)); file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX); } + return $aFiles; } - static protected function _FindIconsOnDisk($sBaseDir, $sDir = '') + static protected function _FindIconsOnDisk($sBaseDir, $sDir = ' ', &$aFilesCaract = []) { - $aResult = array(); + $aResult = []; // Populate automatically the list of icon files - if ($hDir = @opendir($sBaseDir.'/'.$sDir)) - { - while (($sFile = readdir($hDir)) !== false) - { + if ($hDir = @opendir($sBaseDir.'/'.$sDir)) { + while (($sFile = readdir($hDir)) !== false) { $aMatches = array(); - if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) - { + if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) { $sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile; - $aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath)); + $aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath, $aFilesCaract)); } - if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid + $sSize = filesize($sBaseDir.'/'.$sDir.'/'.$sFile); + if (isset($aFilesCaract[$sFile]) && $aFilesCaract[$sFile] == $sSize) { + continue; + } + if (preg_match("/\.(png|jpg|jpeg|gif|svg)$/i", $sFile, $aMatches)) // png, jp(e)g, gif and svg are considered valid { $aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile; + $aFilesCaract[$sFile] = $sSize; } } closedir($hDir); @@ -1645,27 +1645,23 @@ class DesignerFormSelectorField extends DesignerFormField { $sId = $this->oForm->GetFieldId($this->sCode); $sName = $this->oForm->GetFieldName($this->sCode); - $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : ''; + $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : ''; $this->aCSSClasses[] = 'formSelector'; - + $sCSSClasses = ''; - if (count($this->aCSSClasses) > 0) - { + if (count($this->aCSSClasses) > 0) { $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"'; } - if ($this->IsSorted()) - { + if ($this->IsSorted()) { uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel')); } - - if ($this->IsReadOnly()) - { + + if ($this->IsReadOnly()) { $sDisplayValue = ''; $sHiddenValue = ''; - foreach($this->aSubForms as $iKey => $aFormData) - { + foreach ($this->aSubForms as $iKey => $aFormData) { if ($iKey == $this->defaultValue) // Default value is actually the index { $sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8'); @@ -1674,12 +1670,9 @@ class DesignerFormSelectorField extends DesignerFormField } } $sHtml = "".$sDisplayValue.$sHiddenValue.""; - } - else - { + } else { $sHtml = ""; } - - if ($sRenderMode == 'property') - { - $sHtml .= '