mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Implemented a new (optional) UI for managing 1:n linksets.
SVN:trunk[2290]
This commit is contained in:
@@ -113,7 +113,7 @@ class ajax_page extends WebPage
|
||||
}
|
||||
if (count($this->m_aTabs) > 0)
|
||||
{
|
||||
$this->add_ready_script(
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
// The "tab widgets" to handle.
|
||||
var tabs = $('div[id^=tabbedContent]');
|
||||
@@ -183,7 +183,33 @@ EOF
|
||||
$this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content);
|
||||
$container_index++;
|
||||
}
|
||||
|
||||
|
||||
// Additional UI widgets to be activated inside the ajax fragment ??
|
||||
if ($this->sContentType == 'text/html')
|
||||
{
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||
|
||||
@@ -36,6 +36,7 @@ require_once(APPROOT.'/application/applicationextension.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
|
||||
require_once(APPROOT.'/application/ui.linkswidget.class.inc.php');
|
||||
require_once(APPROOT.'/application/ui.linksdirectwidget.class.inc.php');
|
||||
require_once(APPROOT.'/application/ui.passwordwidget.class.inc.php');
|
||||
require_once(APPROOT.'/application/ui.extkeywidget.class.inc.php');
|
||||
require_once(APPROOT.'/application/ui.htmleditorwidget.class.inc.php');
|
||||
@@ -266,58 +267,25 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
if ($bEditMode && (!$bReadOnly))
|
||||
{
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
if (get_class($oAttDef) == 'AttributeLinkedSet')
|
||||
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
// 1:n links
|
||||
$sTargetClass = $oAttDef->GetLinkedClass();
|
||||
if ($this->IsNew())
|
||||
{
|
||||
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription());
|
||||
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition($oAttDef->GetExtKeyToMe(), $this->GetKey(),'=');
|
||||
|
||||
$aDefaults = array($oAttDef->GetExtKeyToMe() => $this->GetKey());
|
||||
$oAppContext = new ApplicationContext();
|
||||
foreach($oAppContext->GetNames() as $sKey)
|
||||
{
|
||||
// The linked object inherits the parent's value for the context
|
||||
if (MetaModel::IsValidAttCode($sClass, $sKey))
|
||||
{
|
||||
$aDefaults[$sKey] = $this->Get($sKey);
|
||||
}
|
||||
}
|
||||
$aParams = array(
|
||||
'target_attr' => $oAttDef->GetExtKeyToMe(),
|
||||
'object_id' => $this->GetKey(),
|
||||
'menu' => true,
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $sClass.'_'.$sAttCode,
|
||||
);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oPage, $sInputId, $aParams);
|
||||
}
|
||||
}
|
||||
else // get_class($oAttDef) == 'AttributeLinkedSetIndirect'
|
||||
{
|
||||
// n:n links
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
$oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription().'<span id="busy_'.$sInputId.'"></span>');
|
||||
|
||||
$sValue = $this->Get($sAttCode);
|
||||
$sDisplayValue = ''; // not used
|
||||
$aArgs = array('this' => $this);
|
||||
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
$oPage->add($sHTMLValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTargetClass = $sLinkedClass;
|
||||
}
|
||||
$oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription().'<span id="busy_'.$sInputId.'"></span>');
|
||||
|
||||
$oValue = $this->Get($sAttCode);
|
||||
$sDisplayValue = ''; // not used
|
||||
$aArgs = array('this' => $this);
|
||||
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
$oPage->add($sHTMLValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -414,6 +382,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$iInputId = 0;
|
||||
$aFieldsMap = array();
|
||||
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
|
||||
$aExtraFlags = (isset($aExtraParams['fieldsFlags'])) ? $aExtraParams['fieldsFlags'] : array();
|
||||
$bFieldComments = (count($aFieldsComments) > 0);
|
||||
|
||||
foreach($aDetailsStruct as $sTab => $aCols )
|
||||
@@ -474,6 +443,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
|
||||
}
|
||||
if (array_key_exists($sAttCode, $aExtraFlags))
|
||||
{
|
||||
// the caller may override some flags if needed
|
||||
$iFlags = $iFlags | $aExtraFlags[$sAttCode];
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
|
||||
{
|
||||
@@ -1608,12 +1582,19 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
break;
|
||||
|
||||
case 'LinkedSet':
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed(), $aArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix, $aArgs);
|
||||
}
|
||||
$aEventsList[] ='validate';
|
||||
$aEventsList[] ='change';
|
||||
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed(), $aArgs);
|
||||
$oObj = isset($aArgs['this']) ? $aArgs['this'] : null;
|
||||
$sHTMLValue = $oWidget->Display($oPage, $value, array(), $sFormPrefix, $oObj);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'Document':
|
||||
$aEventsList[] ='validate';
|
||||
@@ -2434,6 +2415,50 @@ EOF
|
||||
$this->Set($sAttCode, $iValue);
|
||||
}
|
||||
}
|
||||
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() && ($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE))
|
||||
{
|
||||
$oLinkset = $this->Get($sAttCode);
|
||||
$sLinkedClass = $oLinkset->GetClass();
|
||||
$aObjSet = array();
|
||||
$oLinkset->Rewind();
|
||||
$bModified = false;
|
||||
while($oLink = $oLinkset->Fetch())
|
||||
{
|
||||
if (in_array($oLink->GetKey(), $value['to_be_deleted']))
|
||||
{
|
||||
// The link is to be deleted, don't copy it in the array
|
||||
$bModified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aObjSet[] = $oLink;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('to_be_created', $value) && (count($value['to_be_created']) > 0))
|
||||
{
|
||||
// Now handle the lniks to be created
|
||||
foreach($value['to_be_created'] as $aData)
|
||||
{
|
||||
$sSubClass = $aData['class'];
|
||||
if ( ($sLinkedClass == $sSubClass) || (is_subclass_of($sSubClass, $sLinkedClass)) )
|
||||
{
|
||||
$aObjData = $aData['data'];
|
||||
|
||||
$oLink = new $sSubClass;
|
||||
$oLink->UpdateObjectFromArray($aObjData);
|
||||
$aObjSet[] = $oLink;
|
||||
$bModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($bModified)
|
||||
{
|
||||
$oNewSet = DBObjectSet::FromArray($oLinkset->GetClass(), $aObjSet);
|
||||
$this->Set($sAttCode, $oNewSet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_null($value))
|
||||
@@ -2466,6 +2491,28 @@ EOF
|
||||
{
|
||||
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
|
||||
}
|
||||
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() && ($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE))
|
||||
{
|
||||
$aRawToBeCreated = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbc", '{}', 'raw_data'), true);
|
||||
$aToBeCreated = array();
|
||||
foreach($aRawToBeCreated as $aData)
|
||||
{
|
||||
$sSubFormPrefix = $aData['formPrefix'];
|
||||
$sObjClass = $aData['class'];
|
||||
$aObjData = array();
|
||||
foreach($aData as $sKey => $value)
|
||||
{
|
||||
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
|
||||
{
|
||||
$aObjData[$aMatches[1]] = $value;
|
||||
}
|
||||
}
|
||||
$aToBeCreated[] = array('class' => $sObjClass, 'data' => $aObjData);
|
||||
}
|
||||
|
||||
$value = array('to_be_created' => $aToBeCreated,
|
||||
'to_be_deleted' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbd", '[]', 'raw_data'), true) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
|
||||
|
||||
@@ -568,11 +568,13 @@ class DisplayBlock
|
||||
{
|
||||
if ((UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$sLinkTarget = '';
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
// 1:n links, populate the target object as a default value when creating a new linked object
|
||||
if (isset($aExtraParams['target_attr']))
|
||||
{
|
||||
$sLinkTarget = ' target="_blank" ';
|
||||
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
|
||||
}
|
||||
$sDefault = '';
|
||||
@@ -584,7 +586,7 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml .= $oPage->GetP("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."</a>\n");
|
||||
$sHtml .= $oPage->GetP("<a{$sLinkTarget} href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."</a>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ EOF
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
@@ -290,23 +290,6 @@ EOF
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
// Restore the persisted sortable order, for all sortable lists... if any
|
||||
$('.sortable').each(function()
|
||||
{
|
||||
var sTemp = GetUserPreference(this.id+'_order', undefined);
|
||||
if (sTemp != undefined)
|
||||
{
|
||||
var aSerialized = sTemp.split(',');
|
||||
var sortable = $(this);
|
||||
$.each(aSerialized, function(i,v) {
|
||||
var item = $('#menu_'+v);
|
||||
if (item.length > 0) // Check that the menu exists
|
||||
{
|
||||
sortable.append(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Make sortable, everything that claims to be sortable
|
||||
$('.sortable').sortable( {axis: 'y', cursor: 'move', handle: '.drag_handle', stop: function()
|
||||
|
||||
238
application/ui.linksdirectwidget.class.inc.php
Normal file
238
application/ui.linksdirectwidget.class.inc.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
// Copyright (C) 2012 Combodo SARL
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; version 3 of the License.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
class UILinksWidgetDirect
|
||||
{
|
||||
protected $sClass;
|
||||
protected $sAttCode;
|
||||
protected $sInputid;
|
||||
protected $sNameSuffix;
|
||||
protected $sLinkedClass;
|
||||
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
|
||||
{
|
||||
$this->sClass = $sClass;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sInputid = $sInputId;
|
||||
$this->sNameSuffix = $sNameSuffix;
|
||||
$this->aZlist = array();
|
||||
$this->sLinkedClass = '';
|
||||
|
||||
// Compute the list of attributes visible from the given objet:
|
||||
// All the attributes from the "list" Zlist of the Link class except
|
||||
// the ExternalKey that points to the current object and its related external fields
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$this->sLinkedClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
|
||||
foreach($aZList as $sLinkedAttCode)
|
||||
{
|
||||
if ($sLinkedAttCode != $sExtKeyToMe)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
|
||||
|
||||
if (!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe) )
|
||||
{
|
||||
$this->aZlist[] = $sLinkedAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
{
|
||||
case LINKSET_EDITMODE_NONE: // The linkset is read-only
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
|
||||
if ($oCurrentObj && !$oCurrentObj->IsNew())
|
||||
{
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
|
||||
}
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
|
||||
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
|
||||
break;
|
||||
|
||||
case LINKSET_EDITMODE_ACTIONS:
|
||||
default:
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
|
||||
{
|
||||
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
|
||||
|
||||
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
|
||||
$oAppContext = new ApplicationContext();
|
||||
foreach($oAppContext->GetNames() as $sKey)
|
||||
{
|
||||
// The linked object inherits the parent's value for the context
|
||||
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
|
||||
{
|
||||
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
|
||||
}
|
||||
}
|
||||
$aParams = array(
|
||||
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
|
||||
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
|
||||
'menu' => $bDisplayMenu,
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $this->sClass.'_'.$this->sAttCode,
|
||||
);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oPage, $this->sInputid, $aParams);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
|
||||
$oValue->Rewind();
|
||||
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
|
||||
|
||||
$aData = array();
|
||||
while($oLinkObj = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oPage->table($aAttribs, $aData);
|
||||
$oPage->add('</td></tr></table>'); //listcontainer
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName' });");
|
||||
}
|
||||
|
||||
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
|
||||
{
|
||||
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||
// and that the current user is allowed to create objects of this class
|
||||
$sRealClass = '';
|
||||
$oPage->add('<div class="wizContainer" style="vertical-align:top;"><div>');
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sLinkedClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||
$aPossibleClasses = array();
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
if ($sCandidateClass == $sProposedRealClass)
|
||||
{
|
||||
$sRealClass = $sProposedRealClass;
|
||||
}
|
||||
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
|
||||
}
|
||||
}
|
||||
// Only one of the subclasses can be instantiated...
|
||||
if (count($aPossibleClasses) == 1)
|
||||
{
|
||||
$aKeys = array_keys($aPossibleClasses);
|
||||
$sRealClass = $aKeys[0];
|
||||
}
|
||||
|
||||
if ($sRealClass != '')
|
||||
{
|
||||
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($this->sLinkedClass);
|
||||
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
|
||||
$oPage->add('<nobr><select name="class">');
|
||||
asort($aPossibleClasses);
|
||||
foreach($aPossibleClasses as $sClassName => $sClassLabel)
|
||||
{
|
||||
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
|
||||
}
|
||||
$oPage->add('</select>');
|
||||
$oPage->add(' <button type="button" onclick="$(\'#'.$this->sInputid.'\').directlinks(\'subclassSelected\');">'.Dict::S('UI:Button:Apply').'</button><span class="indicator" style="display:inline-block;width:16px"></span></nobr></p>');
|
||||
}
|
||||
$oPage->add('</div></div>');
|
||||
}
|
||||
|
||||
public function GetObjectModificationDlg()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function GetTableConfig()
|
||||
{
|
||||
$aAttribs = array();
|
||||
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
|
||||
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
if ($sRealClass == '')
|
||||
{
|
||||
$sRealClass = $this->sLinkedClass;
|
||||
}
|
||||
$oLinkObj = new $sRealClass();
|
||||
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
|
||||
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.(-$iTempId).'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
return $oPage->GetTableRow($aRow, $aAttribs);
|
||||
}
|
||||
|
||||
public function UpdateFromArray($oObj, $aData)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,10 @@ define('LINKSET_TRACKING_LIST', 1); // Do track added/removed items
|
||||
define('LINKSET_TRACKING_DETAILS', 2); // Do track modified items
|
||||
define('LINKSET_TRACKING_ALL', 3); // Do track added/removed/modified items
|
||||
|
||||
define('LINKSET_EDITMODE_NONE', 0); // The linkset cannot be edited at all from inside this object
|
||||
define('LINKSET_EDITMODE_ADDONLY', 1); // The only possible action is to open a new window to create a new object
|
||||
define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
|
||||
|
||||
|
||||
/**
|
||||
@@ -563,7 +567,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max"));
|
||||
}
|
||||
|
||||
public function GetEditClass() {return "List";}
|
||||
public function GetEditClass() {return "LinkedSet";}
|
||||
|
||||
public function IsWritable() {return true;}
|
||||
public function IsLinkSet() {return true;}
|
||||
@@ -593,6 +597,11 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $this->GetOptional('tracking_level', LINKSET_TRACKING_LIST);
|
||||
}
|
||||
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
public function GetLinkedClass() {return $this->Get('linked_class');}
|
||||
public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');}
|
||||
|
||||
|
||||
249
js/linksdirectwidget.js
Normal file
249
js/linksdirectwidget.js
Normal file
@@ -0,0 +1,249 @@
|
||||
// jQuery UI style "widget" for managing 1:n links "in-place"
|
||||
$(function()
|
||||
{
|
||||
// the widget definition, where "itop" is the namespace,
|
||||
// "directlinks" the widget name
|
||||
$.widget( "itop.directlinks",
|
||||
{
|
||||
// default options
|
||||
options:
|
||||
{
|
||||
input_name: '',
|
||||
class_name: '',
|
||||
att_code: '',
|
||||
submit_to: GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
submit_parameters: {},
|
||||
labels: { 'delete': 'Delete',
|
||||
modify: 'Modify...' ,
|
||||
creation_title: 'Creation of a new object...' ,
|
||||
create: 'Create...'
|
||||
}
|
||||
},
|
||||
|
||||
// the constructor
|
||||
_create: function()
|
||||
{
|
||||
var me = this;
|
||||
this.id = this.element.attr('id');
|
||||
|
||||
this.element
|
||||
.addClass('itop-directlinks');
|
||||
|
||||
this.datatable = this.element.find('table.listResults');
|
||||
|
||||
this.deleteBtn = $('<button type="button">' + this.options.labels['delete'] + '</button>');
|
||||
this.modifyBtn = $('<button type="button">' + this.options.labels['modify'] + '</button>');
|
||||
this.createBtn = $('<button type="button">' + this.options.labels['create'] + '</button>');
|
||||
this.indicator = $('<span></span>');
|
||||
this.inputToBeCreated = $('<input type="hidden" name="'+this.options.input_name+'_tbc" value="{}">');
|
||||
this.toBeCreated = {};
|
||||
this.inputToBeDeleted = $('<input type="hidden" name="'+this.options.input_name+'_tbd" value="[]">');
|
||||
this.toBeDeleted = [];
|
||||
|
||||
|
||||
this.element
|
||||
.after(this.inputToBeCreated)
|
||||
.after(this.inputToBeDeleted)
|
||||
.after('<span style="float:left"> <img src="../images/tv-item-last.gif"> ')
|
||||
.after(this.indicator).after(this.createBtn).after('  ')
|
||||
.after(this.modifyBtn).after('  ')
|
||||
.after(this.deleteBtn);
|
||||
|
||||
this.element.find('.selectList'+this.id).bind('change', function() { me._updateButtons(); });
|
||||
this.deleteBtn.click(function() {
|
||||
$('.selectList'+me.id+':checked', me.element).each( function() { me._deleteRow($(this)); });
|
||||
});
|
||||
this.createBtn.click(function() {
|
||||
me._createRow();
|
||||
});
|
||||
|
||||
this.modifyBtn.hide(); //hidden for now since it's not yet implemented
|
||||
|
||||
this._updateButtons();
|
||||
},
|
||||
|
||||
// called when created, and later when changing options
|
||||
_refresh: function()
|
||||
{
|
||||
this._updateButtons();
|
||||
},
|
||||
// events bound via _bind are removed automatically
|
||||
// revert other modifications here
|
||||
destroy: function()
|
||||
{
|
||||
this.element
|
||||
.removeClass('itop-directlinks');
|
||||
|
||||
// call the original destroy method since we overwrote it
|
||||
$.Widget.prototype.destroy.call( this );
|
||||
},
|
||||
// _setOptions is called with a hash of all options that are changing
|
||||
_setOptions: function()
|
||||
{
|
||||
// in 1.9 would use _superApply
|
||||
$.Widget.prototype._setOptions.apply( this, arguments );
|
||||
},
|
||||
// _setOption is called for each individual option that is changing
|
||||
_setOption: function( key, value )
|
||||
{
|
||||
// in 1.9 would use _super
|
||||
$.Widget.prototype._setOption.call( this, key, value );
|
||||
|
||||
if (key == 'fields') this._refresh();
|
||||
},
|
||||
_updateButtons: function()
|
||||
{
|
||||
var oChecked = $('.selectList'+this.id+':checked', this.element);
|
||||
switch(oChecked.length)
|
||||
{
|
||||
case 0:
|
||||
this.deleteBtn.attr('disabled', 'disabled');
|
||||
this.modifyBtn.attr('disabled', 'disabled');
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this.deleteBtn.removeAttr('disabled');
|
||||
this.modifyBtn.removeAttr('disabled');
|
||||
break;
|
||||
|
||||
default:
|
||||
this.deleteBtn.removeAttr('disabled');
|
||||
this.modifyBtn.attr('disabled', 'disabled');
|
||||
break;
|
||||
}
|
||||
},
|
||||
_updateTable: function()
|
||||
{
|
||||
var me = this;
|
||||
this.datatable.trigger("update").trigger("applyWidgets");
|
||||
this.datatable.tableHover();
|
||||
this.datatable.find('.selectList'+this.id).bind('change', function() { me._updateButtons(); });
|
||||
},
|
||||
_updateDlgSize: function()
|
||||
{
|
||||
this.oDlg.dialog('option', { position: { my: "center", at: "center", of: window }});
|
||||
},
|
||||
_createRow: function()
|
||||
{
|
||||
this.createBtn.attr('disabled', 'disabled');
|
||||
this.indicator.html('<img src="../images/indicator.gif">');
|
||||
oParams = this.options.submit_parameters;
|
||||
oParams.operation = 'createObject';
|
||||
oParams['class'] = this.options.class_name;
|
||||
oParams.real_class = '';
|
||||
oParams.att_code = this.options.att_code;
|
||||
oParams.iInputId = this.id;
|
||||
var me = this;
|
||||
$.post(this.options.submit_to, oParams, function(data){
|
||||
me.oDlg = $('<div></div>');
|
||||
$('body').append(me.oDlg);
|
||||
me.oDlg.html(data);
|
||||
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onCreateRow(); return false; } );
|
||||
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
|
||||
|
||||
me.oDlg.dialog({
|
||||
title: me.options.labels['creation_title'],
|
||||
modal: true,
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
position: { my: "center", at: "center", of: window },
|
||||
close: function() { me._onDlgClose(); }
|
||||
});
|
||||
me.indicator.html('');
|
||||
me.createBtn.removeAttr('disabled');
|
||||
me._updateDlgSize();
|
||||
});
|
||||
},
|
||||
subclassSelected: function()
|
||||
{
|
||||
var sRealClass = this.oDlg.find('select[name="class"]').val();
|
||||
oParams = this.options.submit_parameters;
|
||||
oParams.operation = 'createObject';
|
||||
oParams['class'] = this.options.class_name;
|
||||
oParams.real_class = sRealClass;
|
||||
oParams.att_code = this.options.att_code;
|
||||
oParams.iInputId = this.id;
|
||||
var me = this;
|
||||
me.oDlg.find('button').attr('disabled', 'disabled');
|
||||
me.oDlg.find('span.indicator').html('<img src="../images/indicator.gif">');
|
||||
$.post(this.options.submit_to, oParams, function(data){
|
||||
me.oDlg.html(data);
|
||||
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onCreateRow(); return false; } );
|
||||
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
|
||||
me._updateDlgSize();
|
||||
});
|
||||
},
|
||||
_onCreateRow: function()
|
||||
{
|
||||
// Validate the form
|
||||
var sFormId = this.oDlg.find('form').attr('id');
|
||||
if (CheckFields(sFormId, true))
|
||||
{
|
||||
// Gather the values from the form
|
||||
oParams = this.options.submit_parameters;
|
||||
var oValues = {};
|
||||
this.oDlg.find(':input').each( function() {
|
||||
if (this.name != '')
|
||||
{
|
||||
oParams[this.name] = this.value;
|
||||
oValues[this.name] = this.value;
|
||||
}
|
||||
});
|
||||
var nextIdx = 0;
|
||||
for(k in this.toBeCreated)
|
||||
{
|
||||
nextIdx++;
|
||||
}
|
||||
nextIdx++;
|
||||
this.toBeCreated[nextIdx] = oValues;
|
||||
this.inputToBeCreated.val(JSON.stringify(this.toBeCreated));
|
||||
this.oDlg.dialog('close');
|
||||
|
||||
oParams = this.options.submit_parameters;
|
||||
oParams.operation = 'getLinksetRow';
|
||||
oParams['class'] = this.options.class_name;
|
||||
oParams.att_code = this.options.att_code;
|
||||
oParams.iInputId = this.id;
|
||||
oParams.tempId = nextIdx;
|
||||
var me = this;
|
||||
|
||||
this.createBtn.attr('disabled', 'disabled');
|
||||
this.indicator.html('<img src="../images/indicator.gif">');
|
||||
|
||||
$.post(this.options.submit_to, oParams, function(data){
|
||||
me.datatable.find('tbody').append(data);
|
||||
me._updateTable();
|
||||
me.indicator.html('');
|
||||
me.createBtn.removeAttr('disabled');
|
||||
});
|
||||
}
|
||||
},
|
||||
_onDlgClose: function()
|
||||
{
|
||||
this.oDlg.remove();
|
||||
this.oDlg = null;
|
||||
},
|
||||
_deleteRow: function(oCheckbox)
|
||||
{
|
||||
var iObjKey = parseInt(oCheckbox.val(), 10); // Number in base 10
|
||||
|
||||
if (iObjKey > 0)
|
||||
{
|
||||
// Existing objet: add it to the "to be deleted" list
|
||||
this.toBeDeleted.push(iObjKey);
|
||||
this.inputToBeDeleted.val(JSON.stringify(this.toBeDeleted));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Object to be created, just remove it from the "to be created" list
|
||||
this.toBeCreated[-iObjKey] = undefined;
|
||||
this.inputToBeCreated.val(JSON.stringify(this.toBeCreated));
|
||||
}
|
||||
// Now remove the row from the table
|
||||
oRow = oCheckbox.closest('tr');
|
||||
oRow.remove();
|
||||
this._updateButtons();
|
||||
this._updateTable();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -532,6 +532,7 @@ try
|
||||
$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/linksdirectwidget.js");
|
||||
$oP->add_linked_script("../js/extkeywidget.js");
|
||||
$oP->add_linked_script("../js/jquery.blockUI.js");
|
||||
break;
|
||||
|
||||
@@ -238,6 +238,30 @@ try
|
||||
$oWidget->SearchObjectsToAdd($oPage, $sRemoteClass, $aAlreadyLinked);
|
||||
break;
|
||||
|
||||
//ui.linksdirectwidget
|
||||
case 'createObject':
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$sRealClass = utils::ReadParam('real_class', '', false, 'class');
|
||||
$sAttCode = utils::ReadParam('att_code', '');
|
||||
$iInputId = utils::ReadParam('iInputId', '');
|
||||
$oPage->SetContentType('text/html');
|
||||
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId);
|
||||
$oWidget->GetObjectCreationDlg($oPage, $sRealClass);
|
||||
break;
|
||||
|
||||
// ui.linksdirectwidget
|
||||
case 'getLinksetRow':
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$sRealClass = utils::ReadParam('real_class', '', false, 'class');
|
||||
$sAttCode = utils::ReadParam('att_code', '');
|
||||
$iInputId = utils::ReadParam('iInputId', '');
|
||||
$iTempId = utils::ReadParam('tempId', '');
|
||||
$aValues = utils::ReadParam('values', array(), false, 'raw_data');
|
||||
$oPage->SetContentType('text/html');
|
||||
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId);
|
||||
$oPage->add($oWidget->GetRow($oPage, $sRealClass, $aValues, $iTempId));
|
||||
break;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// ui.extkeywidget
|
||||
|
||||
@@ -307,7 +307,28 @@ EOF;
|
||||
return $aXmlToPHP[$sTrackingLevel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to format the edit-mode for direct linkset
|
||||
* @param string $sEditMode Value set from within the XML
|
||||
* Returns string PHP flag
|
||||
*/
|
||||
protected function EditModeToPHP($sEditMode)
|
||||
{
|
||||
static $aXmlToPHP = array(
|
||||
'none' => 'LINKSET_EDITMODE_NONE',
|
||||
'add_only' => 'LINKSET_EDITMODE_ADDONLY',
|
||||
'actions' => 'LINKSET_EDITMODE_ACTIONS',
|
||||
'in_place' => 'LINKSET_EDITMODE_INPLACE',
|
||||
);
|
||||
|
||||
if (!array_key_exists($sEditMode, $aXmlToPHP))
|
||||
{
|
||||
throw new exception("Edit mode: unknown value '$sTrackingLevel'");
|
||||
}
|
||||
return $aXmlToPHP[$sEditMode];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format a path (file or url) as an absolute path or relative to the module or the app
|
||||
*/
|
||||
@@ -584,6 +605,11 @@ EOF;
|
||||
{
|
||||
$aParameters['tracking_level'] = $this->TrackingLevelToPHP($sTrackingLevel);
|
||||
}
|
||||
$sEditMode = $oField->GetChildText('edit_mode');
|
||||
if (!is_null($sEditMode))
|
||||
{
|
||||
$aParameters['edit_mode'] = $this->EditModeToPHP($sEditMode);
|
||||
}
|
||||
$aParameters['depends_on'] = $sDependencies;
|
||||
}
|
||||
elseif ($sAttType == 'AttributeExternalKey')
|
||||
|
||||
Reference in New Issue
Block a user