ui.extkeywidget.class.inc.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * Class UIExtKeyWidget
  18. * UI wdiget for displaying and editing external keys when
  19. * A simple drop-down list is not enough...
  20. *
  21. * The layout is the following
  22. *
  23. * +-- #label_<id> (input)-------+ +-----------+
  24. * | | | Browse... |
  25. * +-----------------------------+ +-----------+
  26. *
  27. * And the popup dialog has the following layout:
  28. *
  29. * +------------------- ac_dlg_<id> (div)-----------+
  30. * + +--- ds_<id> (div)---------------------------+ |
  31. * | | +------------- fs_<id> (form)------------+ | |
  32. * | | | +--------+---+ | | |
  33. * | | | | Class | V | | | |
  34. * | | | +--------+---+ | | |
  35. * | | | | | |
  36. * | | | S e a r c h F o r m | | |
  37. * | | | +--------+ | | |
  38. * | | | | Search | | | |
  39. * | | | +--------+ | | |
  40. * | | +----------------------------------------+ | |
  41. * | +--------------+-dh_<id>-+--------------------+ |
  42. * | \ Search / |
  43. * | +------+ |
  44. * | +--- fr_<id> (form)--------------------------+ |
  45. * | | +------------ dr_<id> (div)--------------+ | |
  46. * | | | | | |
  47. * | | | S e a r c h R e s u l t s | | |
  48. * | | | | | |
  49. * | | +----------------------------------------+ | |
  50. * | | +--------+ +-----+ | |
  51. * | | | Cancel | | Add | | |
  52. * | | +--------+ +-----+ | |
  53. * | +--------------------------------------------+ |
  54. * +------------------------------------------------+
  55. * @author Erwan Taloc <erwan.taloc@combodo.com>
  56. * @author Romain Quetiez <romain.quetiez@combodo.com>
  57. * @author Denis Flaven <denis.flaven@combodo.com>
  58. * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
  59. */
  60. require_once(APPROOT.'/application/webpage.class.inc.php');
  61. require_once(APPROOT.'/application/displayblock.class.inc.php');
  62. class UIExtKeyWidget
  63. {
  64. protected $iId;
  65. protected $sTargetClass;
  66. //public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
  67. static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
  68. {
  69. $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
  70. $sTargetClass = $oAttDef->GetTargetClass();
  71. $iMaxComboLength = $oAttDef->GetMaximumComboLength();
  72. $bAllowTargetCreation = $oAttDef->AllowTargetCreation();
  73. $sDisplayStyle = $oAttDef->GetDisplayStyle();
  74. $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId);
  75. return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, $bSearchMode, $sDisplayStyle);
  76. }
  77. public function __construct($sTargetClass, $iInputId)
  78. {
  79. $this->sTargetClass = $sTargetClass;
  80. $this->iId = $iInputId;
  81. }
  82. /**
  83. * Get the HTML fragment corresponding to the linkset editing widget
  84. * @param WebPage $oP The web page used for all the output
  85. * @param Hash $aArgs Extra context arguments
  86. * @return string The HTML fragment to be inserted into the page
  87. */
  88. public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = false, $sDisplayStyle = 'select')
  89. {
  90. $sTitle = addslashes($sTitle);
  91. $oPage->add_linked_script('../js/extkeywidget.js');
  92. $oPage->add_linked_script('../js/forms-json-utils.js');
  93. $bCreate = (!$bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
  94. $bExtensions = true;
  95. $sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
  96. $sAttrFieldPrefix = ($bSearchMode) ? '' : 'attr_';
  97. $sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
  98. $sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
  99. if($bSearchMode)
  100. {
  101. $sWizHelper = 'null';
  102. $sWizHelperJSON = "''";
  103. }
  104. else
  105. {
  106. $sWizHelper = 'oWizardHelper'.$sFormPrefix;
  107. $sWizHelperJSON = $sWizHelper.'.ToJSON()';
  108. }
  109. if (is_null($oAllowedValues))
  110. {
  111. throw new Exception('Implementation: null value for allowed values definition');
  112. }
  113. elseif ($oAllowedValues->Count() < $iMaxComboLength)
  114. {
  115. // Discrete list of values, use a SELECT or RADIO buttons depending on the config
  116. switch($sDisplayStyle)
  117. {
  118. case 'radio':
  119. case 'radio_horizontal':
  120. case 'radio_vertical':
  121. $sHTMLValue = '';
  122. $bVertical = ($sDisplayStyle != 'radio_horizontal');
  123. $bExtensions = false;
  124. $oAllowedValues->Rewind();
  125. $aAllowedValues = array();
  126. while($oObj = $oAllowedValues->Fetch())
  127. {
  128. $aAllowedValues[$oObj->GetKey()] = $oObj->Get('friendlyname');
  129. }
  130. $sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, '' /* TODO: manage validation field */);
  131. $aEventsList[] ='change';
  132. break;
  133. case 'select':
  134. default:
  135. $sSelectMode = 'true';
  136. $sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
  137. $sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
  138. if ($bSearchMode)
  139. {
  140. $sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
  141. $sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
  142. }
  143. else
  144. {
  145. $sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
  146. }
  147. $oAllowedValues->Rewind();
  148. while($oObj = $oAllowedValues->Fetch())
  149. {
  150. $key = $oObj->GetKey();
  151. $display_value = $oObj->Get('friendlyname');
  152. if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
  153. {
  154. // When there is only once choice, select it by default
  155. $sSelected = ' selected';
  156. }
  157. else
  158. {
  159. $sSelected = ($value == $key) ? ' selected' : '';
  160. }
  161. $sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
  162. }
  163. $sHTMLValue .= "</select>\n";
  164. $oPage->add_ready_script(
  165. <<<EOF
  166. oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper);
  167. oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
  168. $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
  169. $('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
  170. EOF
  171. );
  172. } // Switch
  173. }
  174. else
  175. {
  176. // Too many choices, use an autocomplete
  177. $sSelectMode = 'false';
  178. if (is_null($value) || ($value == 0)) // Null values are displayed as ''
  179. {
  180. $sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
  181. }
  182. else
  183. {
  184. $sDisplayValue = $this->GetObjectName($value);
  185. }
  186. $iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
  187. $iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize();
  188. // the input for the auto-complete
  189. $sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
  190. $sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/>&nbsp;";
  191. // another hidden input to store & pass the object's Id
  192. $sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
  193. // Scripts to start the autocomplete and bind some events to it
  194. $oPage->add_ready_script(
  195. <<<EOF
  196. oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper);
  197. oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
  198. $('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter', json: function() { return $sWizHelperJSON; } }});
  199. $('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
  200. $('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
  201. $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
  202. if ($('#ac_dlg_{$this->iId}').length == 0)
  203. {
  204. $('body').append('<div id="ac_dlg_{$this->iId}"></div>');
  205. }
  206. EOF
  207. );
  208. }
  209. if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
  210. {
  211. $sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/>&nbsp;";
  212. $oPage->add_ready_script(
  213. <<<EOF
  214. if ($('#ac_tree_{$this->iId}').length == 0)
  215. {
  216. $('body').append('<div id="ac_tree_{$this->iId}"></div>');
  217. }
  218. EOF
  219. );
  220. }
  221. if ($bCreate && $bExtensions)
  222. {
  223. $sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/>&nbsp;";
  224. $oPage->add_ready_script(
  225. <<<EOF
  226. if ($('#ajax_{$this->iId}').length == 0)
  227. {
  228. $('body').append('<div id="ajax_{$this->iId}"></div>');
  229. }
  230. EOF
  231. );
  232. }
  233. $sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
  234. $sHTMLValue .= "</span>"; // end of no wrap
  235. return $sHTMLValue;
  236. }
  237. public function GetSearchDialog(WebPage $oPage, $sTitle)
  238. {
  239. $sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
  240. $oFilter = new DBObjectSearch($this->sTargetClass);
  241. $oSet = new CMDBObjectSet($oFilter);
  242. $oBlock = new DisplayBlock($oFilter, 'search', false);
  243. $sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId));
  244. $sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n";
  245. $sHTML .= "<div id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
  246. $sHTML .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
  247. $sHTML .= "</div>\n";
  248. $sHTML .= "<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_dlg_{$this->iId}').dialog('close');\">&nbsp;&nbsp;";
  249. $sHTML .= "<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoOk();\">";
  250. $sHTML .= "<input type=\"hidden\" id=\"count_{$this->iId}\" value=\"0\">";
  251. $sHTML .= "</form>\n";
  252. $sHTML .= '</div></div>';
  253. $sDialogTitle = addslashes($sTitle);
  254. $oPage->add_ready_script(
  255. <<<EOF
  256. $('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
  257. $('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
  258. $('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
  259. EOF
  260. );
  261. $oPage->add($sHTML);
  262. }
  263. /**
  264. * Search for objects to be selected
  265. * @param WebPage $oP The page used for the output (usually an AjaxWebPage)
  266. * @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
  267. * @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
  268. */
  269. public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
  270. {
  271. if (is_null($sFilter))
  272. {
  273. throw new Exception('Implementation: null value for allowed values definition');
  274. }
  275. try
  276. {
  277. $oFilter = DBObjectSearch::FromOQL($sFilter);
  278. $oBlock = new DisplayBlock($oFilter, 'list', false);
  279. $oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
  280. }
  281. catch(MissingQueryArgument $e)
  282. {
  283. // When used in a search form the $this parameter may be missing, in this case return all possible values...
  284. // TODO check if we can improve this behavior...
  285. $sOQL = 'SELECT '.$sRemoteClass;
  286. $oFilter = DBObjectSearch::FromOQL($sOQL);
  287. $oBlock = new DisplayBlock($oFilter, 'list', false);
  288. $oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
  289. }
  290. }
  291. /**
  292. * Search for objects to be selected
  293. * @param WebPage $oP The page used for the output (usually an AjaxWebPage)
  294. * @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
  295. * @param DBObject $oObj The current object for the OQL context
  296. * @param string $sContains The text of the autocomplete to filter the results
  297. */
  298. public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains)
  299. {
  300. if (is_null($sFilter))
  301. {
  302. throw new Exception('Implementation: null value for allowed values definition');
  303. }
  304. $oValuesSet = new ValueSetObjects($sFilter);
  305. $aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
  306. foreach($aValues as $sKey => $sFriendlyName)
  307. {
  308. $oP->add(trim(htmlentities($sFriendlyName, ENT_COMPAT, 'UTF-8'))."|".$sKey."\n");
  309. }
  310. }
  311. /**
  312. * Get the display name of the selected object, to fill back the autocomplete
  313. */
  314. public function GetObjectName($iObjId)
  315. {
  316. $oObj = MetaModel::GetObject($this->sTargetClass, $iObjId);
  317. return $oObj->GetName();
  318. }
  319. /**
  320. * Get the form to create a new object of the 'target' class
  321. */
  322. public function GetObjectCreationForm(WebPage $oPage)
  323. {
  324. // Set all the default values in an object and clone this "default" object
  325. $oNewObj = MetaModel::NewObject($this->sTargetClass);
  326. // 1st - set context values
  327. $oAppContext = new ApplicationContext();
  328. $oAppContext->InitObjectFromContext($oNewObj);
  329. // 2nd - set values from the page argument 'default'
  330. $oNewObj->UpdateObjectFromArg('default');
  331. $sDialogTitle = addslashes($this->sTitle);
  332. $oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
  333. $oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
  334. cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
  335. $oPage->add('</div></div></div>');
  336. // $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
  337. $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
  338. $oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
  339. $oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);");
  340. }
  341. /**
  342. * Display the hierarchy of the 'target' class
  343. */
  344. public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
  345. {
  346. $sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
  347. $oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="overflow:auto;background:#fff;margin-bottom:5px;" id="tree_'.$this->iId.'">');
  348. $oPage->add('<table style="width:100%"><tr><td>');
  349. if (is_null($sFilter))
  350. {
  351. throw new Exception('Implementation: null value for allowed values definition');
  352. }
  353. try
  354. {
  355. $oFilter = DBObjectSearch::FromOQL($sFilter);
  356. $oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
  357. }
  358. catch(MissingQueryArgument $e)
  359. {
  360. // When used in a search form the $this parameter may be missing, in this case return all possible values...
  361. // TODO check if we can improve this behavior...
  362. $sOQL = 'SELECT '.$this->m_sTargetClass;
  363. $oFilter = DBObjectSearch::FromOQL($sOQL);
  364. $oSet = new DBObjectSet($oFilter);
  365. }
  366. $sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
  367. $this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
  368. $oPage->add('</td></tr></table>');
  369. $oPage->add('</div>');
  370. $oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\">&nbsp;&nbsp;");
  371. $oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
  372. $oPage->add('</div></div>');
  373. $oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
  374. $oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
  375. }
  376. /**
  377. * Get the form to create a new object of the 'target' class
  378. */
  379. public function DoCreateObject($oPage)
  380. {
  381. $oObj = MetaModel::NewObject($this->sTargetClass);
  382. $aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
  383. if (count($aErrors) == 0)
  384. {
  385. $oMyChange = MetaModel::NewObject("CMDBChange");
  386. $oMyChange->Set("date", time());
  387. $sUserString = CMDBChange::GetCurrentUserName();
  388. $oMyChange->Set("userinfo", $sUserString);
  389. $iChangeId = $oMyChange->DBInsert();
  390. $oObj->DBInsertTracked($oMyChange);
  391. return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
  392. }
  393. else
  394. {
  395. return array('name' => implode(' ', $aErrors), 'id' => 0);
  396. }
  397. }
  398. function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
  399. {
  400. $aTree = array();
  401. $aNodes = array();
  402. while($oObj = $oSet->Fetch())
  403. {
  404. $iParentId = $oObj->Get($sParentAttCode);
  405. if (!isset($aTree[$iParentId]))
  406. {
  407. $aTree[$iParentId] = array();
  408. }
  409. $aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
  410. $aNodes[$oObj->GetKey()] = $oObj;
  411. }
  412. $aParents = array_keys($aTree);
  413. $aRoots = array();
  414. foreach($aParents as $id)
  415. {
  416. if (!array_key_exists($id, $aNodes))
  417. {
  418. $aRoots[] = $id;
  419. }
  420. }
  421. foreach($aRoots as $iRootId)
  422. {
  423. $this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
  424. }
  425. }
  426. function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
  427. {
  428. $bSelect = true;
  429. $bMultiple = false;
  430. $sSelect = '';
  431. if (array_key_exists($iRootId, $aTree))
  432. {
  433. $aSortedRoots = $aTree[$iRootId];
  434. asort($aSortedRoots);
  435. $oP->add("<ul>\n");
  436. foreach($aSortedRoots as $id => $sName)
  437. {
  438. if ($bSelect)
  439. {
  440. $sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
  441. if ($bMultiple)
  442. {
  443. $sSelect = '<input type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
  444. }
  445. else
  446. {
  447. $sSelect = '<input type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
  448. }
  449. }
  450. $oP->add('<li>'.$sSelect.$aNodes[$id]->GetHyperlink());
  451. $this->DumpNodes($oP, $id, $aTree, $aNodes, $currValue);
  452. $oP->add("</li>\n");
  453. }
  454. $oP->add("</ul>\n");
  455. }
  456. }
  457. }
  458. ?>