diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php index 1ef0af81c..7fadab3a1 100644 --- a/application/ajaxwebpage.class.inc.php +++ b/application/ajaxwebpage.class.inc.php @@ -113,7 +113,7 @@ class ajax_page extends WebPage } if (count($this->m_aTabs) > 0) { - $this->add_ready_script( + $this->add_ready_script( <<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( +<<sContentType == 'text/html') && ($this->sContentDisposition == 'inline')) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 959e497d2..2258e5321 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -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().''); - - $sValue = $this->Get($sAttCode); - $sDisplayValue = ''; // not used - $aArgs = array('this' => $this); - $sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).''; - $aFieldsMap[$sAttCode] = $sInputId; - $oPage->add($sHTMLValue); } + else + { + $sTargetClass = $sLinkedClass; + } + $oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription().''); + + $oValue = $this->Get($sAttCode); + $sDisplayValue = ''; // not used + $aArgs = array('this' => $this); + $sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).''; + $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'); diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 9e9920414..083c886b0 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -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("".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."\n"); + $sHtml .= $oPage->GetP("".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass))."\n"); } } } diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 63a3c718c..0634c6f23 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -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() diff --git a/application/ui.linksdirectwidget.class.inc.php b/application/ui.linksdirectwidget.class.inc.php new file mode 100644 index 000000000..3a2d00191 --- /dev/null +++ b/application/ui.linksdirectwidget.class.inc.php @@ -0,0 +1,238 @@ +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("".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."\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('
'); + + $aData = array(); + while($oLinkObj = $oValue->Fetch()) + { + $aRow = array(); + $aRow['form::select'] = ''; + foreach($this->aZlist as $sLinkedAttCode) + { + $aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode); + } + $aData[] = $aRow; + } + $oPage->table($aAttribs, $aData); + $oPage->add('
'); //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('
'); + $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("

".MetaModel::GetClassIcon($sRealClass)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."

\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('

'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel)); + $oPage->add(''); + $oPage->add(' 

'); + } + $oPage->add('
'); + } + + public function GetObjectModificationDlg() + { + + } + + protected function GetTableConfig() + { + $aAttribs = array(); + $aAttribs['form::select'] = array('label' => "sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\">", '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'] = ''; + foreach($this->aZlist as $sLinkedAttCode) + { + $aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode); + } + return $oPage->GetTableRow($aRow, $aAttribs); + } + + public function UpdateFromArray($oObj, $aData) + { + + } +} diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index ab6443ef0..f29b5d9ec 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -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');} diff --git a/js/linksdirectwidget.js b/js/linksdirectwidget.js new file mode 100644 index 000000000..c21f9cf86 --- /dev/null +++ b/js/linksdirectwidget.js @@ -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 = $(''); + this.modifyBtn = $(''); + this.createBtn = $(''); + this.indicator = $(''); + this.inputToBeCreated = $(''); + this.toBeCreated = {}; + this.inputToBeDeleted = $(''); + this.toBeDeleted = []; + + + this.element + .after(this.inputToBeCreated) + .after(this.inputToBeDeleted) + .after('      ') + .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(''); + 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 = $('
'); + $('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(''); + $.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(''); + + $.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(); + } + }); +}); \ No newline at end of file diff --git a/pages/UI.php b/pages/UI.php index 6d980b07b..232b972bc 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -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; diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 72494880d..2fa015432 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -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 diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 5d1ac171d..a801346e4 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -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')