forms.class.inc.php 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. <?php
  2. // Copyright (C) 2010-2012 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Helper class to build interactive forms to be used either in stand-alone
  20. * modal dialog or in "property-sheet" panes.
  21. *
  22. * @copyright Copyright (C) 2010-2012 Combodo SARL
  23. * @license http://opensource.org/licenses/AGPL-3.0
  24. */
  25. class DesignerForm
  26. {
  27. protected $aFieldSets;
  28. protected $sCurrentFieldSet;
  29. protected $sScript;
  30. protected $sReadyScript;
  31. protected $sFormId;
  32. protected $sFormPrefix;
  33. protected $sParamsContainer;
  34. protected $oParentForm;
  35. protected $aSubmitParams;
  36. protected $sSubmitTo;
  37. protected $bReadOnly;
  38. protected $sSelectorClass;
  39. public function __construct()
  40. {
  41. $this->aFieldSets = array();
  42. $this->sCurrentFieldSet = '';
  43. $this->sScript = '';
  44. $this->sReadyScript = '';
  45. $this->sFormPrefix = '';
  46. $this->sParamsContainer = '';
  47. $this->sFormId = 'form_'.rand();
  48. $this->oParentForm = null;
  49. $this->bReadOnly = false;
  50. $this->sSelectorClass = '';
  51. $this->StartFieldSet($this->sCurrentFieldSet);
  52. }
  53. public function AddField(DesignerFormField $oField)
  54. {
  55. if (!is_array($this->aFieldSets[$this->sCurrentFieldSet]))
  56. {
  57. $this->aFieldSets[$this->sCurrentFieldSet] = array();
  58. }
  59. $this->aFieldSets[$this->sCurrentFieldSet][] = $oField;
  60. $oField->SetForm($this);
  61. }
  62. public function StartFieldSet($sLabel)
  63. {
  64. $this->sCurrentFieldSet = $sLabel;
  65. if (!array_key_exists($this->sCurrentFieldSet, $this->aFieldSets))
  66. {
  67. $this->aFieldSets[$this->sCurrentFieldSet] = array();
  68. }
  69. }
  70. public function Render($oP, $bReturnHTML = false)
  71. {
  72. if ($this->oParentForm == null)
  73. {
  74. $sFormId = $this->sFormId;
  75. $sReturn = '<form id="'.$sFormId.'">';
  76. }
  77. else
  78. {
  79. $sReturn = '';
  80. $sFormId = $this->oParentForm->sFormId;
  81. }
  82. $sHiddenFields = '';
  83. foreach($this->aFieldSets as $sLabel => $aFields)
  84. {
  85. $aDetails = array();
  86. if ($sLabel != '')
  87. {
  88. $sReturn .= '<fieldset>';
  89. $sReturn .= '<legend>'.$sLabel.'</legend>';
  90. }
  91. foreach($aFields as $oField)
  92. {
  93. $aRow = $oField->Render($oP, $sFormId);
  94. if ($oField->IsVisible())
  95. {
  96. $sValidation = '&nbsp;<span class="prop_apply">'.$this->GetValidationArea($oField->GetCode()).'</span>';
  97. $sField = $aRow['value'].$sValidation;
  98. $aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
  99. }
  100. else
  101. {
  102. $sHiddenFields .= $aRow['value'];
  103. }
  104. }
  105. $sReturn .= $oP->GetDetails($aDetails);
  106. if ($sLabel != '')
  107. {
  108. $sReturn .= '</fieldset>';
  109. }
  110. }
  111. $sReturn .= $sHiddenFields;
  112. if ($this->oParentForm == null)
  113. {
  114. $sReturn .= '</form>';
  115. }
  116. if($this->sScript != '')
  117. {
  118. $oP->add_script($this->sScript);
  119. }
  120. if($this->sReadyScript != '')
  121. {
  122. $oP->add_ready_script($this->sReadyScript);
  123. }
  124. if ($bReturnHTML)
  125. {
  126. return $sReturn;
  127. }
  128. else
  129. {
  130. $oP->add($sReturn);
  131. }
  132. }
  133. public function SetSubmitParams($sSubmitToUrl, $aSubmitParams)
  134. {
  135. $this->sSubmitTo = $sSubmitToUrl;
  136. $this->aSubmitParams = $aSubmitParams;
  137. }
  138. public function CopySubmitParams($oParentForm)
  139. {
  140. $this->sSubmitTo = $oParentForm->sSubmitTo;
  141. $this->aSubmitParams = $oParentForm->aSubmitParams;
  142. }
  143. public function SetSelectorClass($sSelectorClass)
  144. {
  145. $this->sSelectorClass = $sSelectorClass;
  146. }
  147. public function GetSelectorClass()
  148. {
  149. return $this->sSelectorClass;
  150. }
  151. public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
  152. {
  153. $sReturn = '';
  154. $sActionUrl = addslashes($this->sSubmitTo);
  155. $sJSSubmitParams = json_encode($this->aSubmitParams);
  156. if ($this->oParentForm == null)
  157. {
  158. $sFormId = $this->sFormId;
  159. $sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
  160. $sReturn .= '<table class="prop_table">';
  161. $sReturn .= '<thead><tr><th class="prop_header">'.Dict::S('UI:Form:Property').'</th><th class="prop_header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="prop_header">&nbsp;</th></tr></thead><tbody>';
  162. }
  163. else
  164. {
  165. $sFormId = $this->oParentForm->sFormId;
  166. }
  167. $sHiddenFields = '';
  168. foreach($this->aFieldSets as $sLabel => $aFields)
  169. {
  170. $aDetails = array();
  171. if ($sLabel != '')
  172. {
  173. $sReturn .= '<tr><th colspan="4">'.$sLabel.'</th></tr>';
  174. }
  175. foreach($aFields as $oField)
  176. {
  177. $aRow = $oField->Render($oP, $sFormId, 'property');
  178. if ($oField->IsVisible())
  179. {
  180. $sFieldId = $this->GetFieldId($oField->GetCode());
  181. $sValidation = $this->GetValidationArea($oField->GetCode(), '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
  182. $sValidationFields = '</td><td class="prop_icon prop_apply">'.$sValidation.'</td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
  183. $sReturn .= '<tr id="row_'.$sFieldId.'"><td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
  184. if (!($oField instanceof DesignerFormSelectorField))
  185. {
  186. $sReturn .= $sValidationFields;
  187. }
  188. $sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
  189. $sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
  190. $this->AddReadyScript(
  191. <<<EOF
  192. $('#row_$sFieldId').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
  193. EOF
  194. );
  195. }
  196. else
  197. {
  198. $sHiddenFields .= $aRow['value'];
  199. }
  200. }
  201. }
  202. if ($this->oParentForm == null)
  203. {
  204. $sFormId = $this->sFormId;
  205. $sReturn .= '</tbody>';
  206. $sReturn .= '</table>';
  207. $sReturn .= $sHiddenFields;
  208. $sReturn .= '</form>';
  209. $sReturn .= '<div id="prop_submit_result"/>'; // for the return of the submit operation
  210. }
  211. else
  212. {
  213. $sReturn .= $sHiddenFields;
  214. }
  215. $this->AddReadyScript(
  216. <<<EOF
  217. $('.prop_table').tableHover();
  218. var idx = 0;
  219. $('.prop_table tbody tr').each(function() {
  220. if ((idx % 2) == 0)
  221. {
  222. $(this).addClass('even');
  223. }
  224. else
  225. {
  226. $(this).addClass('odd');
  227. }
  228. idx++;
  229. });
  230. EOF
  231. );
  232. if($this->sScript != '')
  233. {
  234. $oP->add_script($this->sScript);
  235. }
  236. if($this->sReadyScript != '')
  237. {
  238. $oP->add_ready_script($this->sReadyScript);
  239. }
  240. if ($bReturnHTML)
  241. {
  242. return $sReturn;
  243. }
  244. else
  245. {
  246. $oP->add($sReturn);
  247. }
  248. }
  249. public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
  250. {
  251. $sDialogTitle = addslashes($sDialogTitle);
  252. $sOkButtonLabel = addslashes($sOkButtonLabel);
  253. $sCancelButtonLabel = Dict::S('UI:Button:Cancel');
  254. $oPage->add("<div id=\"$sDialogId\">");
  255. if ($sIntroduction != null)
  256. {
  257. $oPage->add('<div class="ui-dialog-header">'.$sIntroduction.'</div>');
  258. }
  259. $this->Render($oPage);
  260. $oPage->add('</div>');
  261. $oPage->add_ready_script(
  262. <<<EOF
  263. $('#$sDialogId').dialog({
  264. height: 'auto',
  265. width: 500,
  266. modal: true,
  267. title: '$sDialogTitle',
  268. buttons: [
  269. { text: "$sOkButtonLabel", click: function() {
  270. var oForm = $(this).closest('.ui-dialog').find('form');
  271. oForm.submit();
  272. } },
  273. { text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
  274. ],
  275. close: function() { KillAllMenus(); $(this).remove(); }
  276. });
  277. var oForm = $('#$sDialogId form');
  278. var sFormId = oForm.attr('id');
  279. ValidateForm(sFormId, true);
  280. EOF
  281. );
  282. }
  283. public function ReadParams(&$aValues = array())
  284. {
  285. foreach($this->aFieldSets as $sLabel => $aFields)
  286. {
  287. foreach($aFields as $oField)
  288. {
  289. $oField->ReadParam($aValues);
  290. }
  291. }
  292. return $aValues;
  293. }
  294. public function SetPrefix($sPrefix)
  295. {
  296. $this->sFormPrefix = $sPrefix;
  297. }
  298. public function GetPrefix()
  299. {
  300. return $this->sFormPrefix;
  301. }
  302. public function SetReadOnly($bReadOnly = true)
  303. {
  304. $this->bReadOnly = $bReadOnly;
  305. }
  306. public function IsReadOnly()
  307. {
  308. if ($this->oParentForm == null)
  309. {
  310. return $this->bReadOnly;
  311. }
  312. else
  313. {
  314. return $this->oParentForm->IsReadOnly();
  315. }
  316. }
  317. public function SetParamsContainer($sParamsContainer)
  318. {
  319. $this->sParamsContainer = $sParamsContainer;
  320. }
  321. public function GetParamsContainer()
  322. {
  323. if ($this->oParentForm == null)
  324. {
  325. return $this->sParamsContainer;
  326. }
  327. else
  328. {
  329. return $this->oParentForm->GetParamsContainer();
  330. }
  331. }
  332. public function SetParentForm($oParentForm)
  333. {
  334. $this->oParentForm = $oParentForm;
  335. }
  336. public function GetParentForm()
  337. {
  338. return $this->oParentForm;
  339. }
  340. public function AddScript($sScript)
  341. {
  342. $this->sScript .= $sScript;
  343. }
  344. public function AddReadyScript($sScript)
  345. {
  346. $this->sReadyScript .= $sScript;
  347. }
  348. public function GetFieldId($sCode)
  349. {
  350. return $this->sFormPrefix.'attr_'.$sCode;
  351. }
  352. public function GetFieldName($sCode)
  353. {
  354. return 'attr_'.$sCode;
  355. }
  356. public function GetParamName($sCode)
  357. {
  358. return 'attr_'.$sCode;
  359. }
  360. public function GetValidationArea($sCode, $sContent = '')
  361. {
  362. return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$this->sFormPrefix}attr_$sCode\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
  363. }
  364. public function GetAsyncActionClass()
  365. {
  366. return $this->sAsyncActionClass;
  367. }
  368. }
  369. class DesignerTabularForm extends DesignerForm
  370. {
  371. protected $aTable;
  372. public function __construct()
  373. {
  374. parent::__construct();
  375. $this->aTable = array();
  376. }
  377. public function AddRow($aRow)
  378. {
  379. $this->aTable[] = $aRow;
  380. }
  381. public function Render($oP, $bReturnHTML = false)
  382. {
  383. $sReturn = '';
  384. if ($this->oParentForm == null)
  385. {
  386. $sFormId = $this->sFormId;
  387. $sReturn = '<form id="'.$sFormId.'">';
  388. }
  389. else
  390. {
  391. $sFormId = $this->oParentForm->sFormId;
  392. }
  393. $sHiddenFields = '';
  394. $sReturn .= '<table style="width:100%">';
  395. foreach($this->aTable as $aRow)
  396. {
  397. $sReturn .= '<tr>';
  398. foreach($aRow as $field)
  399. {
  400. if (!is_object($field))
  401. {
  402. // Shortcut: pass a string for a cell containing just a label
  403. $sReturn .= '<td>'.$field.'</td>';
  404. }
  405. else
  406. {
  407. $field->SetForm($this);
  408. $aFieldData = $field->Render($oP, $sFormId);
  409. if ($field->IsVisible())
  410. {
  411. // put the label and value separated by a non-breaking space if needed
  412. $aData = array();
  413. foreach(array('label', 'value') as $sCode )
  414. {
  415. if ($aFieldData[$sCode] != '')
  416. {
  417. $aData[] = $aFieldData[$sCode];
  418. }
  419. }
  420. $sReturn .= '<td>'.implode('&nbsp;', $aData).'</td>';
  421. }
  422. else
  423. {
  424. $sHiddenFields .= $aRow['value'];
  425. }
  426. }
  427. }
  428. $sReturn .= '</tr>';
  429. }
  430. $sReturn .= '</table>';
  431. $sReturn .= $sHiddenFields;
  432. if($this->sScript != '')
  433. {
  434. $oP->add_script($this->sScript);
  435. }
  436. if($this->sReadyScript != '')
  437. {
  438. $oP->add_ready_script($this->sReadyScript);
  439. }
  440. if ($bReturnHTML)
  441. {
  442. return $sReturn;
  443. }
  444. else
  445. {
  446. $oP->add($sReturn);
  447. }
  448. }
  449. public function ReadParams(&$aValues = array())
  450. {
  451. foreach($this->aTable as $aRow)
  452. {
  453. foreach($aRow as $field)
  454. {
  455. if (is_object($field))
  456. {
  457. $field->SetForm($this);
  458. $field->ReadParam($aValues);
  459. }
  460. }
  461. }
  462. return $aValues;
  463. }
  464. }
  465. class DesignerFormField
  466. {
  467. protected $sLabel;
  468. protected $sCode;
  469. protected $defaultValue;
  470. protected $oForm;
  471. protected $bMandatory;
  472. protected $bReadOnly;
  473. protected $bAutoApply;
  474. protected $aCSSClasses;
  475. public function __construct($sCode, $sLabel, $defaultValue)
  476. {
  477. $this->sLabel = $sLabel;
  478. $this->sCode = $sCode;
  479. $this->defaultValue = $defaultValue;
  480. $this->bMandatory = false;
  481. $this->bReadOnly = false;
  482. $this->bAutoApply = false;
  483. $this->aCSSClasses = array();
  484. }
  485. public function GetCode()
  486. {
  487. return $this->sCode;
  488. }
  489. public function SetForm($oForm)
  490. {
  491. $this->oForm = $oForm;
  492. }
  493. public function SetMandatory($bMandatory = true)
  494. {
  495. $this->bMandatory = $bMandatory;
  496. }
  497. public function SetReadOnly($bReadOnly = true)
  498. {
  499. $this->bReadOnly = $bReadOnly;
  500. }
  501. public function IsReadOnly()
  502. {
  503. return ($this->oForm->IsReadOnly() || $this->bReadOnly);
  504. }
  505. public function SetAutoApply($bAutoApply)
  506. {
  507. $this->bAutoApply = $bAutoApply;
  508. }
  509. public function IsAutoApply()
  510. {
  511. return $this->bAutoApply;
  512. }
  513. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  514. {
  515. $sId = $this->oForm->GetFieldId($this->sCode);
  516. $sName = $this->oForm->GetFieldName($this->sCode);
  517. return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  518. }
  519. public function ReadParam(&$aValues)
  520. {
  521. if ($this->IsReadOnly())
  522. {
  523. $aValues[$this->sCode] = $this->defaultValue;
  524. }
  525. else
  526. {
  527. if ($this->oForm->GetParamsContainer() != '')
  528. {
  529. $aParams = utils::ReadParam($this->oForm->GetParamsContainer(), array(), false, 'raw_data');
  530. if (array_key_exists($this->oForm->GetParamName($this->sCode), $aParams))
  531. {
  532. $aValues[$this->sCode] = $aParams[$this->oForm->GetParamName($this->sCode)];
  533. }
  534. else
  535. {
  536. $aValues[$this->sCode] = $this->defaultValue;
  537. }
  538. }
  539. else
  540. {
  541. $aValues[$this->sCode] = utils::ReadParam($this->oForm->GetParamName($this->sCode), $this->defaultValue, false, 'raw_data');
  542. }
  543. }
  544. }
  545. public function IsVisible()
  546. {
  547. return true;
  548. }
  549. public function AddCSSClass($sCSSClass)
  550. {
  551. $this->aCSSClasses[] = $sCSSClass;
  552. }
  553. }
  554. class DesignerLabelField extends DesignerFormField
  555. {
  556. protected $sDescription;
  557. public function __construct($sLabel, $sDescription)
  558. {
  559. parent::__construct('', $sLabel, '');
  560. $this->sDescription = $sDescription;
  561. }
  562. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  563. {
  564. $sId = $this->oForm->GetFieldId($this->sCode);
  565. $sName = $this->oForm->GetFieldName($this->sCode);
  566. return array('label' => $this->sLabel, 'value' => $sDescription);
  567. }
  568. public function ReadParam(&$aValues)
  569. {
  570. }
  571. public function IsVisible()
  572. {
  573. return true;
  574. }
  575. }
  576. class DesignerTextField extends DesignerFormField
  577. {
  578. protected $sValidationPattern;
  579. protected $aForbiddenValues;
  580. protected $sExplainForbiddenValues;
  581. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  582. {
  583. parent::__construct($sCode, $sLabel, $defaultValue);
  584. $this->sValidationPattern = '';
  585. $this->aForbiddenValues = null;
  586. $this->sExplainForbiddenValues = null;
  587. }
  588. public function SetValidationPattern($sValidationPattern)
  589. {
  590. $this->sValidationPattern = $sValidationPattern;
  591. }
  592. public function SetForbiddenValues($aValues, $sExplain)
  593. {
  594. $this->aForbiddenValues = $aValues;
  595. $iDefaultKey = array_search($this->defaultValue, $this->aForbiddenValues);
  596. if ($iDefaultKey !== false)
  597. {
  598. // The default (current) value is always allowed...
  599. unset($this->aForbiddenValues[$iDefaultKey]);
  600. }
  601. $this->sExplainForbiddenValues = $sExplain;
  602. }
  603. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  604. {
  605. $sId = $this->oForm->GetFieldId($this->sCode);
  606. $sName = $this->oForm->GetFieldName($this->sCode);
  607. $sPattern = addslashes($this->sValidationPattern);
  608. if (is_array($this->aForbiddenValues))
  609. {
  610. $sForbiddenValues = json_encode($this->aForbiddenValues);
  611. $sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
  612. }
  613. else
  614. {
  615. $sForbiddenValues = 'null';
  616. $sExplainForbiddenValues = 'null';
  617. }
  618. $sMandatory = $this->bMandatory ? 'true' : 'false';
  619. $sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
  620. $oP->add_ready_script(
  621. <<<EOF
  622. $('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
  623. {
  624. var myTimer = null;
  625. $('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
  626. }
  627. EOF
  628. );
  629. $sCSSClasses = '';
  630. if (count($this->aCSSClasses) > 0)
  631. {
  632. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  633. }
  634. return array('label' => $this->sLabel, 'value' => "<input type=\"text\" $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  635. }
  636. public function ReadParam(&$aValues)
  637. {
  638. parent::ReadParam($aValues);
  639. if (($this->sValidationPattern != '') &&(!preg_match('/'.$this->sValidationPattern.'/', $aValues[$this->sCode])) )
  640. {
  641. $aValues[$this->sCode] = $this->defaultValue;
  642. }
  643. else if(($this->aForbiddenValues != null) && in_array($aValues[$this->sCode], $this->aForbiddenValues))
  644. {
  645. // Reject the value...
  646. $aValues[$this->sCode] = $this->defaultValue;
  647. }
  648. }
  649. }
  650. class DesignerLongTextField extends DesignerTextField
  651. {
  652. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  653. {
  654. $sId = $this->oForm->GetFieldId($this->sCode);
  655. $sName = $this->oForm->GetFieldName($this->sCode);
  656. $sPattern = addslashes($this->sValidationPattern);
  657. if (is_array($this->aForbiddenValues))
  658. {
  659. $sForbiddenValues = json_encode($this->aForbiddenValues);
  660. $sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
  661. }
  662. else
  663. {
  664. $sForbiddenValues = 'null';
  665. $sExplainForbiddenValues = 'null';
  666. }
  667. $sMandatory = $this->bMandatory ? 'true' : 'false';
  668. $sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
  669. $oP->add_ready_script(
  670. <<<EOF
  671. $('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
  672. {
  673. var myTimer = null;
  674. $('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
  675. }
  676. EOF
  677. );
  678. $sCSSClasses = '';
  679. if (count($this->aCSSClasses) > 0)
  680. {
  681. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  682. }
  683. return array('label' => $this->sLabel, 'value' => "<textarea $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
  684. }
  685. }
  686. class DesignerComboField extends DesignerFormField
  687. {
  688. protected $aAllowedValues;
  689. protected $bMultipleSelection;
  690. protected $bOtherChoices;
  691. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  692. {
  693. parent::__construct($sCode, $sLabel, $defaultValue);
  694. $this->aAllowedValues = array();
  695. $this->bMultipleSelection = false;
  696. $this->bOtherChoices = false;
  697. $this->bAutoApply = true;
  698. }
  699. public function SetAllowedValues($aAllowedValues)
  700. {
  701. $this->aAllowedValues = $aAllowedValues;
  702. }
  703. public function MultipleSelection($bMultipleSelection = true)
  704. {
  705. $this->bMultipleSelection = $bMultipleSelection;
  706. }
  707. public function OtherChoices($bOtherChoices = true)
  708. {
  709. $this->bOtherChoices = $bOtherChoices;
  710. }
  711. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  712. {
  713. $sId = $this->oForm->GetFieldId($this->sCode);
  714. $sName = $this->oForm->GetFieldName($this->sCode);
  715. $sChecked = $this->defaultValue ? 'checked' : '';
  716. $sMandatory = $this->bMandatory ? 'true' : 'false';
  717. $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
  718. $sCSSClasses = '';
  719. if (count($this->aCSSClasses) > 0)
  720. {
  721. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  722. }
  723. if ($this->bMultipleSelection)
  724. {
  725. $sHtml = "<select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\" $sReadOnly>";
  726. }
  727. else
  728. {
  729. $sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
  730. $sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
  731. }
  732. foreach($this->aAllowedValues as $sKey => $sDisplayValue)
  733. {
  734. if ($this->bMultipleSelection)
  735. {
  736. $sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
  737. }
  738. else
  739. {
  740. $sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
  741. }
  742. // Quick and dirty: display the menu parents as a tree
  743. $sHtmlValue = str_replace(' ', '&nbsp;', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
  744. $sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
  745. }
  746. $sHtml .= "</select>";
  747. if ($this->bOtherChoices)
  748. {
  749. $sHtml .= '<br/><input type="checkbox" id="other_chk_'.$sId.'"><label for="other_chk_'.$sId.'">&nbsp;Other:</label>&nbsp;<input type="text" id="other_'.$sId.'" name="other_'.$sName.'" size="30"/>';
  750. }
  751. $oP->add_ready_script(
  752. <<<EOF
  753. $('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
  754. EOF
  755. );
  756. return array('label' => $this->sLabel, 'value' => $sHtml);
  757. }
  758. public function ReadParam(&$aValues)
  759. {
  760. parent::ReadParam($aValues);
  761. if ($aValues[$this->sCode] == 'null')
  762. {
  763. $aValues[$this->sCode] = array();
  764. }
  765. }
  766. }
  767. class DesignerBooleanField extends DesignerFormField
  768. {
  769. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  770. {
  771. parent::__construct($sCode, $sLabel, $defaultValue);
  772. $this->bAutoApply = true;
  773. }
  774. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  775. {
  776. $sId = $this->oForm->GetFieldId($this->sCode);
  777. $sName = $this->oForm->GetFieldName($this->sCode);
  778. $sChecked = $this->defaultValue ? 'checked' : '';
  779. $sReadOnly = $this->IsReadOnly() ? 'disabled' : ''; // readonly does not work as expected on checkboxes:
  780. // readonly prevents the user from changing the input's value not its state (checked/unchecked)
  781. $sCSSClasses = '';
  782. if (count($this->aCSSClasses) > 0)
  783. {
  784. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  785. }
  786. return array('label' => $this->sLabel, 'value' => "<input $sCSSClasses type=\"checkbox\" $sChecked $sReadOnly id=\"$sId\" name=\"$sName\" value=\"true\">");
  787. }
  788. public function ReadParam(&$aValues)
  789. {
  790. if ($this->IsReadOnly())
  791. {
  792. $aValues[$this->sCode] = $this->defaultValue;
  793. }
  794. else
  795. {
  796. $sParamsContainer = $this->oForm->GetParamsContainer();
  797. if ($sParamsContainer != '')
  798. {
  799. $aParams = utils::ReadParam($sParamsContainer, array(), false, 'raw_data');
  800. if (array_key_exists($this->oForm->GetParamName($this->sCode), $aParams))
  801. {
  802. $sValue = $aParams[$this->oForm->GetParamName($this->sCode)];
  803. }
  804. else
  805. {
  806. $sValue = 'false';
  807. }
  808. }
  809. else
  810. {
  811. $sValue = utils::ReadParam($this->oForm->GetParamName($this->sCode), 'false', false, 'raw_data');
  812. }
  813. }
  814. $aValues[$this->sCode] = ($sValue == 'true');
  815. }
  816. }
  817. class DesignerHiddenField extends DesignerFormField
  818. {
  819. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  820. {
  821. parent::__construct($sCode, $sLabel, $defaultValue);
  822. }
  823. public function IsVisible()
  824. {
  825. return false;
  826. }
  827. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  828. {
  829. $sId = $this->oForm->GetFieldId($this->sCode);
  830. $sName = $this->oForm->GetFieldName($this->sCode);
  831. $sChecked = $this->defaultValue ? 'checked' : '';
  832. return array('label' =>'', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  833. }
  834. }
  835. class DesignerIconSelectionField extends DesignerFormField
  836. {
  837. protected $sUploadUrl;
  838. protected $aAllowedValues;
  839. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  840. {
  841. parent::__construct($sCode, $sLabel, $defaultValue);
  842. $this->bAutoApply = true;
  843. $this->sUploadUrl = null;
  844. }
  845. public function SetAllowedValues($aAllowedValues)
  846. {
  847. $this->aAllowedValues = $aAllowedValues;
  848. }
  849. public function EnableUpload($sIconUploadUrl)
  850. {
  851. $this->sUploadUrl = $sIconUploadUrl;
  852. }
  853. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  854. {
  855. $sId = $this->oForm->GetFieldId($this->sCode);
  856. $sName = $this->oForm->GetFieldName($this->sCode);
  857. $idx = 0;
  858. foreach($this->aAllowedValues as $index => $aValue)
  859. {
  860. if ($aValue['value'] == $this->defaultValue)
  861. {
  862. $idx = $index;
  863. break;
  864. }
  865. }
  866. $sJSItems = json_encode($this->aAllowedValues);
  867. $sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
  868. if (!$this->IsReadOnly())
  869. {
  870. $oP->add_ready_script(
  871. <<<EOF
  872. $('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
  873. EOF
  874. );
  875. }
  876. $sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
  877. return array('label' =>$this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>");
  878. }
  879. }
  880. class RunTimeIconSelectionField extends DesignerIconSelectionField
  881. {
  882. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  883. {
  884. parent::__construct($sCode, $sLabel, $defaultValue);
  885. $aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
  886. ksort($aAllIcons);
  887. $aValues = array();
  888. foreach($aAllIcons as $sFilePath)
  889. {
  890. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  891. }
  892. $this->SetAllowedValues($aValues);
  893. }
  894. static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
  895. {
  896. $aResult = array();
  897. // Populate automatically the list of icon files
  898. if ($hDir = @opendir($sBaseDir.'/'.$sDir))
  899. {
  900. while (($sFile = readdir($hDir)) !== false)
  901. {
  902. $aMatches = array();
  903. if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
  904. {
  905. $sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
  906. $aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath));
  907. }
  908. if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
  909. {
  910. $aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
  911. }
  912. }
  913. closedir($hDir);
  914. }
  915. return $aResult;
  916. }
  917. public function ValueFromDOMNode($oDOMNode)
  918. {
  919. return $oDOMNode->textContent;
  920. }
  921. public function ValueToDOMNode($oDOMNode, $value)
  922. {
  923. $oTextNode = $oDOMNode->ownerDocument->createTextNode($value);
  924. $oDOMNode->appendChild($oTextNode);
  925. }
  926. public function MakeFileUrl($value)
  927. {
  928. return utils::GetAbsoluteUrlModulesRoot().$value;
  929. }
  930. public function GetDefaultValue($sClass = 'Contact')
  931. {
  932. $sIconPath = MetaModel::GetClassIcon($sClass, false);
  933. $sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
  934. return $sIcon;
  935. }
  936. }
  937. class DesignerSortableField extends DesignerFormField
  938. {
  939. protected $aAllowedValues;
  940. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  941. {
  942. parent::__construct($sCode, $sLabel, $defaultValue);
  943. $this->aAllowedValues = array();
  944. }
  945. public function SetAllowedValues($aAllowedValues)
  946. {
  947. $this->aAllowedValues = $aAllowedValues;
  948. }
  949. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  950. {
  951. $bOpen = false;
  952. $sId = $this->oForm->GetFieldId($this->sCode);
  953. $sName = $this->oForm->GetFieldName($this->sCode);
  954. $aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  955. $sJSFields = json_encode(array_keys($this->aAllowedValues));
  956. $oP->add_ready_script(
  957. "$('#$sId').sortable_field({aAvailableFields: $sJSFields});"
  958. );
  959. return $aResult;
  960. }
  961. }
  962. class DesignerFormSelectorField extends DesignerFormField
  963. {
  964. protected $aSubForms;
  965. protected $defaultRealValue; // What's stored as default value is actually the index
  966. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  967. {
  968. parent::__construct($sCode, $sLabel, 0);
  969. $this->defaultRealValue = $defaultValue;
  970. $this->aSubForms = array();
  971. }
  972. public function AddSubForm($oSubForm, $sLabel, $sValue)
  973. {
  974. $idx = count($this->aSubForms);
  975. $this->aSubForms[] = array('form' => $oSubForm, 'label' => $sLabel, 'value' => $sValue);
  976. if ($sValue == $this->defaultRealValue)
  977. {
  978. // Store the index of the selected/default form
  979. $this->defaultValue = count($this->aSubForms) - 1;
  980. }
  981. }
  982. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  983. {
  984. $sId = $this->oForm->GetFieldId($this->sCode);
  985. $sName = $this->oForm->GetFieldName($this->sCode);
  986. $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
  987. $sCSSClasses = '';
  988. if (count($this->aCSSClasses) > 0)
  989. {
  990. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  991. }
  992. $sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
  993. foreach($this->aSubForms as $sKey => $aFormData)
  994. {
  995. $sDisplayValue = $aFormData['label'];
  996. $sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
  997. $sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8')."</option>";
  998. }
  999. $sHtml .= "</select>";
  1000. if ($sRenderMode == 'property')
  1001. {
  1002. $sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
  1003. }
  1004. foreach($this->aSubForms as $sKey => $aFormData)
  1005. {
  1006. $sId = $this->oForm->GetFieldId($this->sCode);
  1007. $sStyle = ($sKey == $this->defaultValue) ? '' : 'style="display:none"';
  1008. $oSubForm = $aFormData['form'];
  1009. $oSubForm->SetParentForm($this->oForm);
  1010. $oSubForm->CopySubmitParams($this->oForm);
  1011. $oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
  1012. $oSubForm->SetSelectorClass("subform_{$sId} {$sId}_{$sKey}");
  1013. if ($sRenderMode == 'property')
  1014. {
  1015. $sHtml .= "</tbody><tbody class=\"subform_{$sId} {$sId}_{$sKey}\" $sStyle>";
  1016. $sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
  1017. $oParentForm = $this->oForm->GetParentForm();
  1018. if($oParentForm)
  1019. {
  1020. $sHtml .= "</tbody><tbody class=\"".$oParentForm->GetSelectorClass()."\">";
  1021. }
  1022. else
  1023. {
  1024. $sHtml .= "</tbody><tbody>";
  1025. }
  1026. }
  1027. else
  1028. {
  1029. $sHtml .= "<div class=\"subform_{$sId} {$sId}_{$sKey}\" $sStyle>";
  1030. $sHtml .= $oSubForm->Render($oP, true);
  1031. $sHtml .= "</div>";
  1032. }
  1033. }
  1034. $oP->add_ready_script(
  1035. <<<EOF
  1036. $('#$sId').bind('change reverted', function() { $('.subform_{$sId}').hide(); $('.{$sId}_'+this.value).show(); } );
  1037. EOF
  1038. );
  1039. return array('label' => $this->sLabel, 'value' => $sHtml);
  1040. }
  1041. public function ReadParam(&$aValues)
  1042. {
  1043. parent::ReadParam($aValues);
  1044. $sKey = $aValues[$this->sCode];
  1045. $aValues[$this->sCode] = $this->aSubForms[$sKey]['value'];
  1046. $this->aSubForms[$sKey]['form']->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
  1047. $this->aSubForms[$sKey]['form']->SetParentForm($this->oForm);
  1048. $this->aSubForms[$sKey]['form']->ReadParams($aValues);
  1049. }
  1050. }
  1051. class DesignerSubFormField extends DesignerFormField
  1052. {
  1053. protected $oSubForm;
  1054. public function __construct($sLabel, $oSubForm)
  1055. {
  1056. parent::__construct('', $sLabel, '');
  1057. $this->oSubForm = $oSubForm;
  1058. }
  1059. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1060. {
  1061. $this->oSubForm->SetParentForm($this->oForm);
  1062. $this->oSubForm->CopySubmitParams($this->oForm);
  1063. if ($sRenderMode == 'property')
  1064. {
  1065. $sHtml = $this->oSubForm->RenderAsPropertySheet($oP, true);
  1066. }
  1067. else
  1068. {
  1069. $sHtml = $this->oSubForm->Render($oP, true);
  1070. }
  1071. return array('label' => $this->sLabel, 'value' => $sHtml);
  1072. }
  1073. public function ReadParam(&$aValues)
  1074. {
  1075. $this->oSubForm->SetParentForm($this->oForm);
  1076. $this->oSubForm->ReadParams($aValues);
  1077. }
  1078. }
  1079. ?>