From 54032197462fb31d4ee898301384c90ffea10677 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 18 Sep 2018 10:28:39 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B0931:=20Tags=20admin=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/tagsetfield.class.inc.php | 64 +++++++- .../datamodel.itop-config-mgmt.xml | 7 + .../2.x/itop-tickets/en.dict.itop-tickets.php | 2 +- dictionaries/en.dictionary.itop.core.php | 2 + dictionaries/en.dictionary.itop.ui.php | 7 +- dictionaries/fr.dictionary.itop.core.php | 2 + dictionaries/fr.dictionary.itop.ui.php | 6 + pages/tagadmin.php | 152 ++++++++++++++++++ setup/compiler.class.inc.php | 2 +- 9 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 pages/tagadmin.php diff --git a/core/tagsetfield.class.inc.php b/core/tagsetfield.class.inc.php index d856f0565..89cd1daa3 100644 --- a/core/tagsetfield.class.inc.php +++ b/core/tagsetfield.class.inc.php @@ -96,12 +96,27 @@ abstract class TagSetFieldData extends cmdbAbstractObject public function ComputeValues() { $sClassName = get_class($this); + $aRes = static::ExtractTagFieldName($sClassName); + $this->_Set('tag_class', $aRes['tag_class']); + $this->_Set('tag_attcode', $aRes['tag_attcode']); + } + + /** + * Extract Tag class and attcode from the TagFieldData class name + * @param $sClassName + * + * @return string[] + */ + public static function ExtractTagFieldName($sClassName) + { + $aRes = array(); // Extract class and attcode from class name using pattern TagSetFieldDataFor__>; if (preg_match('@^TagSetFieldDataFor_(?\w+)_(?\w+)$@', $sClassName, $aMatches)) { - $this->_Set('tag_class', $aMatches['class']); - $this->_Set('tag_attcode', $aMatches['attcode']); + $aRes['tag_class'] = $aMatches['class']; + $aRes['tag_attcode'] = $aMatches['attcode']; } + return $aRes; } /** @@ -188,6 +203,51 @@ abstract class TagSetFieldData extends cmdbAbstractObject } } + /** + * Display Tag Usage + * + * @param \WebPage $oPage + * @param bool $bEditMode + * + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \MissingQueryArgument + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + * @throws \OQLException + */ + function DisplayBareRelations(WebPage $oPage, $bEditMode = false) + { + parent::DisplayBareRelations($oPage, $bEditMode); + if (!$bEditMode) + { + $sClass = $this->Get('tag_class'); + $sAttCode = $this->Get('tag_attcode'); + $sTagCode = $this->Get('tag_code'); + $oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'"); + $oSet = new DBObjectSet($oFilter); + $iCount = $oSet->Count(); + $oPage->SetCurrentTab(Dict::Format('Core:TagSetFieldData:WhereIsThisTagTab', $iCount)); + $aClassLabels = array(); + foreach(MetaModel::EnumChildClasses($sClass) as $sCurrentClass) + { + $aClassLabels[$sCurrentClass] = MetaModel::GetName($sCurrentClass); + } + + foreach($aClassLabels as $sClass => $sClassLabel) + { + $oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'"); + $oSet = new DBObjectSet($oFilter); + if ($oSet->CountExceeds(0)) + { + $oPage->add("

$sClassLabel

"); + $oResultBlock = new DisplayBlock($oFilter, 'list', false); + $oResultBlock->Display($oPage, 1); + } + } + } + } + /** * @param $sClass * @param $sAttCode diff --git a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml index 1c918b16c..f35b2abea 100755 --- a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml +++ b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml @@ -8423,5 +8423,12 @@ + + 100 + Catalogs + $pages/tagadmin.php + TagSetFieldData + UR_ACTION_MODIFY + diff --git a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php index 6d199fb99..7a2754a30 100755 --- a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php @@ -76,7 +76,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:Ticket/Attribute:private_log+' => '', 'Class:Ticket/Attribute:contacts_list' => 'Contacts', 'Class:Ticket/Attribute:contacts_list+' => 'All the contacts linked to this ticket', - 'Class:Ticket/Attribute:tagfield' => 'Tag', + 'Class:Ticket/Attribute:tagfield' => 'Tags', 'Class:Ticket/Attribute:functionalcis_list' => 'CIs', 'Class:Ticket/Attribute:functionalcis_list+' => 'All the configuration items impacted by this ticket. Items marked as "Computed" have been automatically marked as impacted. Items marked as "Not impacted" are excluded from the impact.', 'Class:Ticket/Attribute:workorders_list' => 'Work orders', diff --git a/dictionaries/en.dictionary.itop.core.php b/dictionaries/en.dictionary.itop.core.php index ea4b50088..15ed78cd8 100644 --- a/dictionaries/en.dictionary.itop.core.php +++ b/dictionaries/en.dictionary.itop.core.php @@ -922,4 +922,6 @@ Dict::Add('EN US', 'English', 'English', array( 'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'Tags code should contain between 3 and 20 alphanumeric characters', 'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'Tags label should not contain | nor be empty', 'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Tags code cannot be changed', + 'Core:TagSetFieldData:FieldDescription' => '%2$s of class %1$s', + 'Core:TagSetFieldData:WhereIsThisTagTab' => 'Tag usage (%1$d)', )); diff --git a/dictionaries/en.dictionary.itop.ui.php b/dictionaries/en.dictionary.itop.ui.php index 1e61d44fd..4ef495476 100644 --- a/dictionaries/en.dictionary.itop.ui.php +++ b/dictionaries/en.dictionary.itop.ui.php @@ -956,7 +956,12 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:NotificationsMenu:OnStateLeave' => 'When an object leaves a given state', 'UI:NotificationsMenu:Actions' => 'Actions', 'UI:NotificationsMenu:AvailableActions' => 'Available actions', - + + 'Menu:TagAdminMenu' => 'Tags configuration', + 'Menu:TagAdminMenu+' => 'Tags values management', + 'UI:TagAdminMenu:Title' => 'Tags configuration', + 'UI:TagSetFieldData:Error' => 'Error: %1$s', + 'Menu:AuditCategories' => 'Audit Categories', // Duplicated into itop-welcome-itil (will be removed from here...) 'Menu:AuditCategories+' => 'Audit Categories', // Duplicated into itop-welcome-itil (will be removed from here...) 'Menu:Notifications:Title' => 'Audit Categories', // Duplicated into itop-welcome-itil (will be removed from here...) diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index c62ce845f..a00f78d3f 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -770,4 +770,6 @@ Opérateurs :
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'Le code de l\'étiquette doit contenir entre 3 et 20 caractères alphanumériques.', 'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'Le nom de l\'étiquette ne doit pas être vide ni contenir le caractère \'|\'', 'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Le code de l\'étiquette ne peut pas être changé', + 'Core:TagSetFieldData:FieldDescription' => '%2$s pour la classe %1$s', + 'Core:TagSetFieldData:WhereIsThisTagTab' => 'Utilisation (%1$d)', )); diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 82159bc78..d9cc4d077 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -1343,6 +1343,12 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:Search:Criteria:Raw:Filtered' => 'Filtré', 'UI:Search:Criteria:Raw:FilteredOn' => 'Filtré sur %1$s', + + // - Tags admin + 'Menu:TagAdminMenu' => 'Etiquettes', + 'Menu:TagAdminMenu+' => 'Gestion des étiquettes', + 'UI:TagAdminMenu:Title' => 'Gestion des étiquettes', + 'UI:TagSetFieldData:Error' => 'Erreur: %1$s', )); diff --git a/pages/tagadmin.php b/pages/tagadmin.php new file mode 100644 index 000000000..fc4c0bd46 --- /dev/null +++ b/pages/tagadmin.php @@ -0,0 +1,152 @@ + + + +/** + * Page to configuration the tag sets + * + * @copyright Copyright (C) 2010-2018 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +require_once('../approot.inc.php'); +require_once(APPROOT.'application/application.inc.php'); +require_once(APPROOT.'application/itopwebpage.class.inc.php'); +require_once(APPROOT.'application/startup.inc.php'); +require_once(APPROOT.'application/loginwebpage.class.inc.php'); + +try +{ + LoginWebPage::DoLogin(); + // Check user rights and prompt if needed + ApplicationMenu::CheckMenuIdEnabled("TagAdminMenu"); + + $oAppContext = new ApplicationContext(); + + // Main program + // + $oP = new iTopWebPage(Dict::S('Menu:TagAdminMenu+')); + $oP->add_linked_script("../js/json.js"); + $oP->add_linked_script("../js/forms-json-utils.js"); + $oP->add_linked_script("../js/wizardhelper.js"); + $oP->add_linked_script("../js/wizard.utils.js"); + $oP->add_linked_script("../js/linkswidget.js"); + $oP->add_linked_script("../js/extkeywidget.js"); + $oP->add_linked_script("../js/jquery.blockUI.js"); + + $sBaseClass = 'TagSetFieldData'; + $sClass = utils::ReadParam('class', '', false, 'class'); + $sOQLClause = utils::ReadParam('oql_clause', '', false, 'raw_data'); + $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); + $sOperation = utils::ReadParam('operation', ''); + + $oP->add(''); + + $oP->SetBreadCrumbEntry('ui-tool-tag-admin', Dict::S('Menu:TagAdminMenu'), Dict::S('Menu:TagAdminMenu+')); + + $sSearchHeaderForceDropdown = '\n"; + + try + { + if ($sOperation == 'search_form') + { + $sOQL = "SELECT $sClass $sOQLClause"; + $oFilter = DBObjectSearch::FromOQL($sOQL); + } + else + { + // Second part: advanced search form: + if (!empty($sFilter)) + { + $oFilter = DBSearch::unserialize($sFilter); + } + else if (!empty($sClass)) + { + $oFilter = new DBObjectSearch($sClass); + } + } + } + catch (CoreException $e) + { + $oFilter = new DBObjectSearch($sClass); + $oP->P("".Dict::Format('UI:TagSetFieldData:Error', $e->getHtmlDesc()).""); + } + + if ($oFilter != null) + { + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'search', false); + $aExtraParams = $oAppContext->GetAsHash(); + $aExtraParams['open'] = true; + $aExtraParams['class'] = $sClass; + $aExtraParams['action'] = utils::GetAbsoluteUrlAppRoot().'pages/tagadmin.php'; + $aExtraParams['table_id'] = '1'; + $aExtraParams['search_header_force_dropdown'] = $sSearchHeaderForceDropdown; + $oBlock->Display($oP, 0, $aExtraParams); + + // Search results + $oResultBlock = new DisplayBlock($oFilter, 'list', false); + $oResultBlock->Display($oP, 1); + + // Breadcrumb + //$iCount = $oBlock->GetDisplayedCount(); + $sPageId = "ui-search-".$oFilter->GetClass(); + $sLabel = MetaModel::GetName($oFilter->GetClass()); + $oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', '../images/breadcrumb-search.png'); + + // Menu node + $sFilter = $oFilter->ToOQL(); + $oP->add("\n\n"); + } + $oP->add("\n"); + + $oP->output(); +} +catch (Exception $e) +{ + require_once(APPROOT.'setup/setuppage.class.inc.php'); + + $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); + //$oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); + $oP->output(); + + IssueLog::Error($e->getMessage()); +} \ No newline at end of file diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index e45518fbd..c42277369 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -2671,7 +2671,7 @@ EOF; * @param bool $bIsAbstractClass * @param string $sMethods * - * @param string $aRequiredFiles + * @param array $aRequiredFiles * @param string $sCodeComment * * @return string php code for the class