From e0b307ef488865517308c1d53f3fa9883c13b08c Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 26 Aug 2009 21:52:34 +0000 Subject: [PATCH] - New user interface to manage n-n links SVN:trunk[104] --- application/application.inc.php | 4 + application/cmdbabstract.class.inc.php | 73 ++++- application/displayblock.class.inc.php | 89 ++++-- application/template.class.inc.php | 19 +- application/ui.linkswidget.class.inc.php | 6 +- application/uilinkswizard.class.inc.php | 380 +++++++++++++++++++++++ business/templates/server.html | 4 +- business/templates/team.html | 2 +- business/templates/ticket.html | 2 +- pages/UI.php | 119 ++++++- pages/ajax.render.php | 34 ++ 11 files changed, 688 insertions(+), 44 deletions(-) create mode 100644 application/uilinkswizard.class.inc.php diff --git a/application/application.inc.php b/application/application.inc.php index 86f381396..d15464c19 100644 --- a/application/application.inc.php +++ b/application/application.inc.php @@ -9,4 +9,8 @@ require_once('../application/audit.category.class.inc.php'); require_once('../application/audit.rule.class.inc.php'); //require_once('../application/menunode.class.inc.php'); require_once('../application/utils.inc.php'); + +class ApplicationException extends CoreException +{ +} ?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index f95a03eab..59dd5a48b 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -51,6 +51,12 @@ abstract class cmdbAbstractObject extends CMDBObject $sHint = htmlentities("$sObjClass::$sObjKey"); return "GetForLink()."\" title=\"$sHint\">$sLabel"; } + + public function GetHyperlink() + { + $aAvailableFields[MetaModel::GetNameAttributeCode(get_class($this))] = $this->GetName(); + return $this->MakeHyperLink(get_class($this), $this->GetKey(), $aAvailableFields); + } public function GetDisplayValue($sAttCode) { @@ -213,13 +219,37 @@ abstract class cmdbAbstractObject extends CMDBObject // Comment by Rom: this helper may be used to display objects of class DBObject // -> I am using this to display the changes history - public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) + public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false, $iObjectId = 0) { - $oPage->add(self::GetDisplaySet($oPage, $oSet, $sLinkageAttribute, $bDisplayMenu)); + $oPage->add(self::GetDisplaySet($oPage, $oSet, array( 'link_attr' => $sLinkageAttribute, 'object_id' => $iObjectId, 'menu' => $bDisplayMenu, 'selection_mode' => $bSelectMode))); } - public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) + //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) + public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { + static $iListId = 0; + $iListId++; + + // Initialize and check the parameters + $sLinkageAttribute = isset($aExtraParams['link_attr']) ? $aExtraParams['link_attr'] : ''; + $iLinkedObjectId = isset($aExtraParams['object_id']) ? $aExtraParams['object_id'] : 0; + $sTargetAttr = isset($aExtraParams['target_attr']) ? $aExtraParams['target_attr'] : ''; + if (!empty($sLinkageAttribute)) + { + if($iLinkedObjectId == 0) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if($sTargetAttr == '') + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + } + $bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true; + $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; + $sHtml = ''; $oAppContext = new ApplicationContext(); $sClassName = $oSet->GetFilter()->GetClass(); @@ -260,6 +290,10 @@ abstract class cmdbAbstractObject extends CMDBObject } foreach($aList as $sAttCode) { + if ($bSelectMode) + { + $aAttribs['form::select'] = array('label' => "", 'description' => 'Select / Deselect All'); + } $aAttribs['key'] = array('label' => '', 'description' => 'Click to display'); $aAttribs[$sAttCode] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode)); } @@ -267,6 +301,11 @@ abstract class cmdbAbstractObject extends CMDBObject $oSet->Seek(0); while ($oObj = $oSet->Fetch()) { + $aRow['key'] = $oObj->GetKey(); + if ($bSelectMode) + { + $aRow['form::select'] = "GetKey()."\">"; + } $aRow['key'] = $oObj->GetKey(); foreach($aList as $sAttCode) { @@ -280,8 +319,14 @@ abstract class cmdbAbstractObject extends CMDBObject if ($bDisplayMenu) { $sColspan = 'colspan="2"'; + $aMenuExtraParams = array(); + if (!empty($sLinkageAttribute)) + { + //$aMenuExtraParams['linkage'] = $sLinkageAttribute; + $aMenuExtraParams = $aExtraParams; + } $sHtml .= ' '.$oSet->Count().' object(s)'; - $sHtml .= $oMenuBlock->GetRenderContent($oPage, $sLinkageAttribute); + $sHtml .= $oMenuBlock->GetRenderContent($oPage, $aMenuExtraParams); $sHtml .= ''; } $sHtml .= ""; @@ -400,7 +445,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "
\n"; $sHtml .= "

Search for ".MetaModel::GetName($sClassName)." Objects

\n"; $oUnlimitedFilter = new DBObjectSearch($sClassName); - $sHtml .= "
\n"; + $sHtml .= "\n"; $index = 0; $sHtml .= "\n"; $aFilterCriteria = $oSet->GetFilter()->GetCriteria(); @@ -488,7 +533,7 @@ abstract class cmdbAbstractObject extends CMDBObject // OQL query builder $sHtml .= "
\n"; $sHtml .= "

OQL Query Builder

\n"; - $sHtml .= "
\n"; + $sHtml .= "
\n"; $sHtml .= "\n"); + foreach($aConfig as $sName=>$void) + { + $oP->add("\n"); + } + $oP->add("\n"); + } + + public function DisplayAddForm(web_page $oP, UserContext $oContext) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + $oTargetObj = $oContext->GetObject($sTargetClass, $this->m_iObjectId); + $oP->add("
\n"); + $oP->add("
\n"); + $oP->add("

Add ".MetaModel::GetName($this->m_sLinkedClass)."s to ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetHyperlink()."

\n"); + $oP->add("
\n"); + + $oFilter = $oContext->NewFilter($this->m_sLinkedClass); + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'search', false); + $oBlock->Display($oP, 'SearchFormToAdd', array('open' => true)); + $oP->Add("
\n"); + $oP->Add("
\n"); + $oP->Add("

Use the search form above to search for objects to be added.

\n"); + $oP->Add("
\n"); + $oP->add("  "); + $oP->Add("
\n"); + $oP->Add("\n"); + $oP->add_ready_script("$('div#SearchFormToAdd form').attr('onSubmit', 'var the_form = this; return SearchObjectsToAdd(the_form.id);');"); + } + + public function SearchObjectsToAdd(web_page $oP, UserContext $oContext) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + + $oFilter = $oContext->NewFilter($this->m_sLinkedClass); + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'list', false); + $oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results + } + + public function DoAddObjects(web_page $oP, UserContext $oContext, $aLinkedObjectIds = array()) + { + //$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + //$sTargetClass = $oAttDef->GetTargetClass(); + //$oP->Add("\n"); // Just to make sure it's not empty + $aTable = array(); + foreach($aLinkedObjectIds as $iObjectId) + { + $oLinkedObj = $oContext->GetObject($this->m_sLinkedClass, $iObjectId); + if (is_object($oLinkedObj)) + { + $aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids + $this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId); + } + else + { + echo "Object: $sTargetClass - Id: $iObjectId not found
\n"; + } + } + //var_dump($aTable); + //$oP->Add("\n"); // Just to make sure it's not empty + } +} +?> diff --git a/business/templates/server.html b/business/templates/server.html index 8f72fd878..1989ac935 100644 --- a/business/templates/server.html +++ b/business/templates/server.html @@ -16,10 +16,10 @@ SELECT bizInterface WHERE device_id = $pkey$ - lnkContactRealObject: object_id = $pkey$ + lnkContactRealObject: object_id = $pkey$ - bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + lnkInfraTicket: infra_id = $pkey$ bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) diff --git a/business/templates/team.html b/business/templates/team.html index 12537bdd5..a9601ee03 100644 --- a/business/templates/team.html +++ b/business/templates/team.html @@ -7,7 +7,7 @@ $class$: pkey = $pkey$ - bizContact: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) + SELECT lnkContactRealObject WHERE contact_id=$pkey$ bizTeam: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) diff --git a/business/templates/ticket.html b/business/templates/ticket.html index efce97e2d..b1c0ae548 100644 --- a/business/templates/ticket.html +++ b/business/templates/ticket.html @@ -10,7 +10,7 @@ lnkInfraTicket: ticket_id = $pkey$ - lnkRelatedTicket: ticket_id = $pkey$ + lnkRelatedTicket: ticket_id = $pkey$ lnkContactTicket: ticket_id = $pkey$ diff --git a/pages/UI.php b/pages/UI.php index 45631dd69..338fdf406 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -335,7 +335,7 @@ switch($operation) } if (!is_object($oObj)) { - // new object or or that can't be retrieved (corrupted id or object not allowed to this user) + // new object or that can't be retrieved (corrupted id or object not allowed to this user) $id = ''; $oObj = MetaModel::NewObject($sClass); } @@ -701,6 +701,123 @@ switch($operation) } break; + case 'modify_links': + $sClass = utils::ReadParam('class', ''); + $sLinkAttr = utils::ReadParam('link_attr', ''); + $sTargetClass = utils::ReadParam('target_class', ''); + $id = utils::ReadParam('id', ''); + $bAddObjects = utils::ReadParam('addObjects', false); + if ( empty($sClass) || empty($id) || empty($sLinkAttr) || empty($sTargetClass)) // TO DO: check that the class name is valid ! + { + $oP->set_title("iTop - Error"); + $oP->add("

4 parameters are mandatory for this operation: class, id, target_class and link_attr.

\n"); + } + else + { + require_once('../application/uilinkswizard.class.inc.php'); + $oWizard = new UILinksWizard($sClass, $sLinkAttr, $id, $sTargetClass); + $oWizard->Display($oP, $oContext, array('StartWithAdd' => $bAddObjects)); + } + break; + + case 'do_modify_links': + $aLinks = utils::ReadParam('linkId', array(), 'post'); + $sLinksToRemove = trim(utils::ReadParam('linksToRemove', '', 'post')); + $aLinksToRemove = array(); + if (!empty($sLinksToRemove)) + { + $aLinksToRemove = explode(' ', trim($sLinksToRemove)); + } + $sClass = utils::ReadParam('class', '', 'post'); + $sLinkageAtt = utils::ReadParam('linkage', '', 'post'); + $iObjectId = utils::ReadParam('object_id', '', 'post'); + $sLinkingAttCode = utils::ReadParam('linking_attcode', '', 'post'); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + + // Delete links that are to be deleted + foreach($aLinksToRemove as $iLinkId) + { + if ($iLinkId > 0) // Negative IDs are objects that were not even created + { + $oLink = $oContext->GetObject($sClass, $iLinkId); + $oLink->DBDeleteTracked($oMyChange); + } + } + + $aEditableFields = array(); + $aData = array(); + foreach(MetaModel::GetAttributesList($sClass) as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField())) + { + $aEditableFields[] = $sAttCode; + $aData[$sAttCode] = utils::ReadParam('attr_'.$sAttCode, array(), 'post'); + } + } + + // Update existing links or create new links + foreach($aLinks as $iLinkId) + { + if ($iLinkId > 0) + { + // This is an existing link to be modified + $oLink = $oContext->GetObject($sClass, $iLinkId); + + // Update all the attributes of the link + foreach($aEditableFields as $sAttCode) + { + $value = $aData[$sAttCode][$iLinkId]; + $oLink->Set($sAttCode, $value); + } + if ($oLink->IsModified()) + { + $oLink->DBUpdateTracked($oMyChange); + } + //echo "Updated link:
\n"; + //var_dump($oLink); + } + else + { + // A new link must be created + $oLink = MetaModel::NewObject($sClass); + $oLinkedObjectId = -$iLinkId; + // Set all the attributes of the link + foreach($aEditableFields as $sAttCode) + { + $value = $aData[$sAttCode][$iLinkId]; + $oLink->Set($sAttCode, $value); + } + // And the two external keys + $oLink->Set($sLinkageAtt, $iObjectId); + $oLink->Set($sLinkingAttCode, $oLinkedObjectId); + // then save it + //echo "Created link:
\n"; + //var_dump($oLink); + $oLink->DBInsertTracked($oMyChange); + } + } + // Display again the details of the linked object + $oAttDef = MetaModel::GetAttributeDef($sClass, $sLinkageAtt); + $sTargetClass = $oAttDef->GetTargetClass(); + $oObj = $oContext->GetObject($sTargetClass, $iObjectId); + + $oSearch = $oContext->NewFilter(get_class($oObj)); + $oBlock = new DisplayBlock($oSearch, 'search', false); + $oBlock->Display($oP, 0); + $oObj->DisplayDetails($oP); + break; default: $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 35af6a858..a90059eeb 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -32,6 +32,40 @@ $sStyle = utils::ReadParam('style', 'list'); switch($operation) { + case 'addObjects': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->DisplayAddForm($oPage, $oContext); + break; + + case 'searchObjectsToAdd': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->SearchObjectsToAdd($oPage, $oContext); + break; + + case 'doAddObjects': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $aLinkedObjectIds = utils::ReadParam('selectObject', array(), 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->DoAddObjects($oPage, $oContext, $aLinkedObjectIds); + break; + case 'wizard_helper_preview': $sJson = utils::ReadParam('json_obj', '', 'post'); $oWizardHelper = WizardHelper::FromJSON($sJson);
"; + $sHTMLValue = ""; break; case 'Text': - $sHTMLValue = ""; + $sHTMLValue = ""; break; case 'List': - $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId); + $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sNameSuffix); $sHTMLValue = $oWidget->Display($oPage, $value); break; @@ -549,7 +594,7 @@ abstract class cmdbAbstractObject extends CMDBObject //Enum field or external key, display a combo if (count($aAllowedValues) == 0) { - $sHTMLValue = ""; + $sHTMLValue = ""; } else if (count($aAllowedValues) > 50) { @@ -557,13 +602,13 @@ abstract class cmdbAbstractObject extends CMDBObject // The input for the auto complete $sHTMLValue = ""; // another hidden input to store & pass the object's Id - $sHTMLValue .= "\n"; + $sHTMLValue .= "\n"; $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); } else { // Few choices, use a normal 'select' - $sHTMLValue = "\n"; foreach($aAllowedValues as $key => $display_value) { $sSelected = ($value == $key) ? ' selected' : ''; @@ -574,7 +619,7 @@ abstract class cmdbAbstractObject extends CMDBObject } else { - $sHTMLValue = ""; + $sHTMLValue = ""; } } } diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 33e639952..8eebeeae0 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -85,11 +85,15 @@ class DisplayBlock { $sEncoding = strtolower($aMatches[1]); } - if (preg_match('/ linkage="(.*)"/U',$sITopTag, $aMatches)) + if (preg_match('/ link_attr="(.*)"/U',$sITopTag, $aMatches)) { // The list to display is a list of links to the specified object - $sExtKey = strtolower($aMatches[1]); - $aParams['linkage'] = $sExtKey; // Name of the Ext. Key that make this linkage + $aParams['link_attr'] = $aMatches[1]; // Name of the Ext. Key that make this linkage + } + if (preg_match('/ object_id="(.*)"/U',$sITopTag, $aMatches)) + { + // The list to display is a list of links to the specified object + $aParams['object_id'] = $aMatches[1]; // Id of the object to be linked to } // Parameters contains a list of extra parameters for the block // the syntax is param_name1:value1;param_name2:value2;... @@ -106,6 +110,21 @@ class DisplayBlock } } } + if (!empty($aParams['link_attr'])) + { + // Check that all mandatory parameters are present: + if(empty($aParams['object_id'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if(empty($aParams['target_attr'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + + } switch($sEncoding) { case 'text/serialize': @@ -245,17 +264,16 @@ class DisplayBlock break; case 'list': - $bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) { - $sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; - $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $sLinkage, !$bDashboardMode /* bDisplayMenu */); + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); } else { $sHtml .= $oPage->GetP("No object to display."); $sClass = $this->m_oFilter->GetClass(); - if (!$bDashboardMode) + $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; + if ($bDisplayMenu) { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) { @@ -265,12 +283,37 @@ class DisplayBlock } break; + case 'links': + //$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; + //$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false; + if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) + { + //$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); + } + else + { + $sHtml .= $oPage->GetP("No object to display."); + $sClass = $this->m_oFilter->GetClass(); + $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; + if ($bDisplayMenu) + { + if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $this->m_aParams['target_attr']); + $sTargetClass = $oAttDef->GetTargetClass(); + $sHtml .= $oPage->GetP("Click here to add new ".Metamodel::GetName($sTargetClass)."s\n"); + } + } + } + break; + case 'details': if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) { while($oObj = $this->m_oSet->Fetch()) { - $sHtml .= $oObj->GetDetails($oPage); + $sHtml .= $oObj->GetDetails($oPage); // Still used ??? } } break; @@ -311,7 +354,7 @@ class DisplayBlock $oPage->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});"); $sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams); $sHtml .= "\n"; - $sHtml .= "
\n"; + $sHtml .= "
\n"; $sHtml .= "
Search
\n"; break; @@ -635,12 +678,15 @@ class MenuBlock extends DisplayBlock $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); // Just one object in the set, possible actions are "new / clone / modify and delete" - if (isset($aExtraParams['linkage'])) + if (isset($aExtraParams['link_attr'])) { - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone #...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } - if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } + $id = $aExtraParams['object_id']; + $sTargetAttr = $aExtraParams['target_attr']; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } + if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } } else { @@ -681,14 +727,17 @@ class MenuBlock extends DisplayBlock $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet); $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); - if (isset($aExtraParams['linkage'])) + if (isset($aExtraParams['link_attr'])) { + $id = $aExtraParams['object_id']; + $sTargetAttr = $aExtraParams['target_attr']; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr); + $sTargetClass = $oAttDef->GetTargetClass(); $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } - if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All #...', 'url' => "#"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } + //if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&linkage=".$aExtraParams['linkage']."&id=$id&addObjects=true&$sContext"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); } } else { diff --git a/application/template.class.inc.php b/application/template.class.inc.php index db768c3af..6436dfc52 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -134,7 +134,7 @@ class DisplayTemplate break; case 'itoptab': - $oPage->SetCurrentTab($aAttributes['name']); + $oPage->SetCurrentTab(str_replace('_', ' ', $aAttributes['name'])); $oTemplate = new DisplayTemplate($sContent); $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied //$oPage->p('iTop Tab Content:
'.htmlentities($sContent).'
'); @@ -153,9 +153,22 @@ class DisplayTemplate $sBlockClass = $aAttributes['blockclass']; $sBlockType = $aAttributes['type']; $aExtraParams = array(); - if (isset($aAttributes['linkage'])) + if (isset($aAttributes['link_attr'])) { - $aExtraParams['linkage'] = $aAttributes['linkage']; + $aExtraParams['link_attr'] = $aAttributes['link_attr']; + // Check that all mandatory parameters are present: + if(empty($aAttributes['object_id'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if(empty($aAttributes['target_attr'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + $aExtraParams['object_id'] = $aAttributes['object_id']; + $aExtraParams['target_attr'] = $aAttributes['target_attr']; } switch($aAttributes['encoding']) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 31be4bef4..1db8517e5 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -6,12 +6,14 @@ class UILinksWidget { protected $m_sClass; protected $m_sAttCode; + protected $m_sNameSuffix; protected $m_iInputId; - public function __construct($sClass, $sAttCode, $iInputId) + public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '') { $this->m_sClass = $sClass; $this->m_sAttCode = $sAttCode; + $this->m_sNameSuffix = $sNameSuffix; $this->m_iInputId = $iInputId; } @@ -76,7 +78,7 @@ class UILinksWidget $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; // another hidden input to store & pass the object's Id $sHTMLValue .= "m_iInputId}\"/>\n"; - $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}\" value=\"\"/>\n"; + $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"/>\n"; $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); } else diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php new file mode 100644 index 000000000..3a4cb450b --- /dev/null +++ b/application/uilinkswizard.class.inc.php @@ -0,0 +1,380 @@ +m_sClass = $sClass; + $this->m_sLinkageAttr = $sLinkageAttr; + $this->m_iObjectId = $iObjectId; + $this->m_sLinkedClass = $sLinkedClass; // Will try to guess below, if it's empty + $this->m_sLinkingAttCode = ''; // Will be filled once we've found the attribute corresponding to the linked class + + $this->m_aEditableFields = array(); + $this->m_aTableConfig = array(); + $this->m_aTableConfig['form::checkbox'] = array( 'label' => "", 'description' => "Select / Deselect All"); + foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + if ($oAttDef->IsExternalKey() && ($sAttCode != $this->m_sLinkageAttr)) + { + if (empty($this->m_sLinkedClass)) + { + // This is a class of objects we can manage ! + // Since nothing was specify, any class will do ! + $this->m_sLinkedClass = $oAttDef->GetTargetClass(); + $this->m_sLinkingAttCode = $sAttCode; + } + else if ($this->m_sLinkedClass == $oAttDef->GetTargetClass()) + { + // This is the class of objects we want to manage ! + $this->m_sLinkingAttCode = $sAttCode; + } + } + else if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField())) + { + $this->m_aEditableFields[] = $sAttCode; + $this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); + } + } + if (empty($this->m_sLinkedClass)) + { + throw( new Exception("Incorrect link definition: the class of objects to manage: '$sLinkedClass' was not found as an external key in the class '$sClass'")); + } + foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode); + $this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); + } + } + + public function Display(web_page $oP, UserContext $oContext, $aExtraParams = array()) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + $oTargetObj = $oContext->GetObject($sTargetClass, $this->m_iObjectId); + + $oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetName()); + $oP->add("
\n"); + $oP->add("\n"); + $oP->add("
\n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("m_sClass}\">\n"); + $oP->add("m_sLinkageAttr}\">\n"); + $oP->add("m_iObjectId}\">\n"); + $oP->add("m_sLinkingAttCode}\">\n"); + $oP->add("

Manage ".MetaModel::GetName($this->m_sLinkedClass)."s linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetHyperlink()."

\n"); + $oP->add("
\n"); + $oP->add("\n"); + $oP->add_ready_script("InitForm();"); + $oFilter = $oContext->NewFilter($this->m_sClass); + $oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '='); + $oSet = new DBObjectSet($oFilter); + $aForm = array(); + while($oCurrentLink = $oSet->Fetch()) + { + $aRow = array(); + $key = $oCurrentLink->GetKey(); + $oLinkedObj = $oContext->GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode)); + + $aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink); + } + //var_dump($aTableLabels); + //var_dump($aForm); + $this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm); + $oP->add("     m_sLinkedClass)."s \" onClick=\"RemoveSelected();\" >"); + $oP->add("   m_sLinkedClass)."s... \" onClick=\"AddObjects();\">\n"); + $oP->add(""); + $oP->add("   \n"); + $oP->add("

 

\n"); + $oP->add("
\n"); + $oP->add("\n"); + if (isset($aExtraParams['StartWithAdd']) && ($aExtraParams['StartWithAdd'])) + { + $oP->add_ready_script("AddObjects();"); + } + } + + protected function GetFormRow($oP, $oLinkedObj, $currentLink = null ) + { + $aRow = array(); + if(is_object($currentLink)) + { + $key = $currentLink->GetKey(); + $sNameSuffix = "[$key]"; // To make a tabular form + $aRow['form::checkbox'] = ""; + $aRow['form::checkbox'] .= ""; + foreach($this->m_aEditableFields as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode); + $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, $currentLink->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix); + } + } + else + { + // form for creating a new record + $sNameSuffix = "[$currentLink]"; // To make a tabular form + $aRow['form::checkbox'] = ""; + $aRow['form::checkbox'] .= ""; + foreach($this->m_aEditableFields as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode); + $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue */, '' /* DisplayValue */, '' /* id */, $sNameSuffix); + } + } + foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode) + { + $aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode); + } + return $aRow; + } + + protected function DisplayFormTable(web_page $oP, $aConfig, $aData) + { + $oP->add("\n"); + // Header + $oP->add("\n"); + $oP->add("\n"); + foreach($aConfig as $sName=>$aDef) + { + $oP->add("\n"); + } + $oP->add("\n"); + $oP->add("\n"); + + // Content + $oP->add("\n"); + if (count($aData) == 0) + { + $oP->add(""); + } + else + { + foreach($aData as $iRowId => $aRow) + { + $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId); + } + } + $oP->add("\n"); + + // Footer + $oP->add("
".$aDef['label']."
The list is empty, use 'Add...' to add elements.
\n"); + } + + protected function DisplayFormRow(web_page $oP, $aConfig, $aRow, $iRowId) + { + $oP->add("
".$aRow[$sName]."