N°931: Tags admin menu

This commit is contained in:
Eric
2018-09-18 10:28:39 +02:00
parent 1debf77ab4
commit 5403219746
9 changed files with 239 additions and 5 deletions

View File

@@ -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_<class>_<attcode>>;
if (preg_match('@^TagSetFieldDataFor_(?<class>\w+)_(?<attcode>\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("<h2>$sClassLabel</h2>");
$oResultBlock = new DisplayBlock($oFilter, 'list', false);
$oResultBlock->Display($oPage, 1);
}
}
}
}
/**
* @param $sClass
* @param $sAttCode

View File

@@ -8423,5 +8423,12 @@
</cells>
</definition>
</menu>
<menu id="TagAdminMenu" xsi:type="WebPageMenuNode" _delta="define">
<rank>100</rank>
<parent>Catalogs</parent>
<url>$pages/tagadmin.php</url>
<enable_class>TagSetFieldData</enable_class>
<enable_action>UR_ACTION_MODIFY</enable_action>
</menu>
</menus>
</itop_design>

View File

@@ -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',

View File

@@ -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)',
));

View File

@@ -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...)

View File

@@ -770,4 +770,6 @@ Opérateurs :<br/>
'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)',
));

View File

@@ -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',
));

152
pages/tagadmin.php Normal file
View File

@@ -0,0 +1,152 @@
<?php
// Copyright (C) 2010-2018 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
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* 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('<div class="page_header" style="padding:0.5em;">');
$oP->add('<h1>'.dict::S('UI:TagAdminMenu:Title').'</h1>');
$oP->add('</div>');
$oP->SetBreadCrumbEntry('ui-tool-tag-admin', Dict::S('Menu:TagAdminMenu'), Dict::S('Menu:TagAdminMenu+'));
$sSearchHeaderForceDropdown = '<select id="select_class" name="class" onChange="this.form.submit();">';
$aClassLabels = array();
foreach(MetaModel::EnumChildClasses($sBaseClass) as $sCurrentClass)
{
$aClassLabels[$sCurrentClass] = MetaModel::GetName($sCurrentClass);
}
asort($aClassLabels);
foreach($aClassLabels as $sCurrentClass => $sLabel)
{
if (empty($sClass))
{
$sClass = $sCurrentClass;
}
$aTagFieldInfo = TagSetFieldData::ExtractTagFieldName($sCurrentClass);
$sClassDesc = MetaModel::GetName($aTagFieldInfo['tag_class']);
$sAttDesc = MetaModel::GetAttributeDef($aTagFieldInfo['tag_class'], $aTagFieldInfo['tag_attcode'])->GetLabel();
$sDescription = Dict::Format('Core:TagSetFieldData:FieldDescription', $sClassDesc, $sAttDesc);
$sSelected = ($sCurrentClass == $sClass) ? " SELECTED" : "";
$sSearchHeaderForceDropdown .= "<option value=\"$sCurrentClass\" title=\"$sDescription\"$sSelected>$sDescription</option>";
}
$sSearchHeaderForceDropdown .= "</select>\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("<b>".Dict::Format('UI:TagSetFieldData:Error', $e->getHtmlDesc())."</b>");
}
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<!-- $sFilter -->\n");
}
$oP->add("</div>\n");
$oP->output();
}
catch (Exception $e)
{
require_once(APPROOT.'setup/setuppage.class.inc.php');
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
//$oP->error(Dict::Format('UI:Error_Details', $e->getMessage()));
$oP->output();
IssueLog::Error($e->getMessage());
}

View File

@@ -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