forms.class.inc.php 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499
  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 $sHierarchyPath; // Needed to manage the visibility of nested subform
  39. protected $sHierarchyParent; // Needed to manage the visibility of nested subform
  40. protected $bDisplayed;
  41. public function __construct()
  42. {
  43. $this->aFieldSets = array();
  44. $this->sCurrentFieldSet = '';
  45. $this->sScript = '';
  46. $this->sReadyScript = '';
  47. $this->sFormPrefix = '';
  48. $this->sParamsContainer = '';
  49. $this->sFormId = 'form_'.rand();
  50. $this->oParentForm = null;
  51. $this->bReadOnly = false;
  52. $this->sHierarchyPath = '';
  53. $this->sHierarchyParent = '';
  54. $this->StartFieldSet($this->sCurrentFieldSet);
  55. $this->bDisplayed = true;
  56. }
  57. public function AddField(DesignerFormField $oField)
  58. {
  59. if (!is_array($this->aFieldSets[$this->sCurrentFieldSet]))
  60. {
  61. $this->aFieldSets[$this->sCurrentFieldSet] = array();
  62. }
  63. $this->aFieldSets[$this->sCurrentFieldSet][] = $oField;
  64. $oField->SetForm($this);
  65. }
  66. public function StartFieldSet($sLabel)
  67. {
  68. $this->sCurrentFieldSet = $sLabel;
  69. if (!array_key_exists($this->sCurrentFieldSet, $this->aFieldSets))
  70. {
  71. $this->aFieldSets[$this->sCurrentFieldSet] = array();
  72. }
  73. }
  74. public function Render($oP, $bReturnHTML = false)
  75. {
  76. if ($this->oParentForm == null)
  77. {
  78. $sFormId = $this->sFormId;
  79. $sReturn = '<form id="'.$sFormId.'">';
  80. }
  81. else
  82. {
  83. $sReturn = '';
  84. $sFormId = $this->oParentForm->sFormId;
  85. }
  86. $sHiddenFields = '';
  87. foreach($this->aFieldSets as $sLabel => $aFields)
  88. {
  89. $aDetails = array();
  90. if ($sLabel != '')
  91. {
  92. $sReturn .= '<fieldset>';
  93. $sReturn .= '<legend>'.$sLabel.'</legend>';
  94. }
  95. foreach($aFields as $oField)
  96. {
  97. $aRow = $oField->Render($oP, $sFormId);
  98. if ($oField->IsVisible())
  99. {
  100. $sValidation = '&nbsp;<span class="prop_apply">'.$this->GetValidationArea($oField->GetCode()).'</span>';
  101. $sField = $aRow['value'].$sValidation;
  102. $aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
  103. }
  104. else
  105. {
  106. $sHiddenFields .= $aRow['value'];
  107. }
  108. }
  109. $sReturn .= $oP->GetDetails($aDetails);
  110. if ($sLabel != '')
  111. {
  112. $sReturn .= '</fieldset>';
  113. }
  114. }
  115. $sReturn .= $sHiddenFields;
  116. if ($this->oParentForm == null)
  117. {
  118. $sReturn .= '</form>';
  119. }
  120. if($this->sScript != '')
  121. {
  122. $oP->add_script($this->sScript);
  123. }
  124. if($this->sReadyScript != '')
  125. {
  126. $oP->add_ready_script($this->sReadyScript);
  127. }
  128. if ($bReturnHTML)
  129. {
  130. return $sReturn;
  131. }
  132. else
  133. {
  134. $oP->add($sReturn);
  135. }
  136. }
  137. public function SetSubmitParams($sSubmitToUrl, $aSubmitParams)
  138. {
  139. $this->sSubmitTo = $sSubmitToUrl;
  140. $this->aSubmitParams = $aSubmitParams;
  141. }
  142. public function CopySubmitParams($oParentForm)
  143. {
  144. $this->sSubmitTo = $oParentForm->sSubmitTo;
  145. $this->aSubmitParams = $oParentForm->aSubmitParams;
  146. }
  147. /**
  148. * Helper to handle subforms hide/show
  149. */
  150. public function SetHierarchyPath($sHierarchy)
  151. {
  152. $this->sHierarchyPath = $sHierarchy;
  153. }
  154. /**
  155. * Helper to handle subforms hide/show
  156. */
  157. public function GetHierarchyPath()
  158. {
  159. return $this->sHierarchyPath;
  160. }
  161. /**
  162. * Helper to handle subforms hide/show
  163. */
  164. public function SetHierarchyParent($sHierarchy)
  165. {
  166. $this->sHierarchyParent = $sHierarchy;
  167. }
  168. /**
  169. * Helper to handle subforms hide/show
  170. */
  171. public function GetHierarchyParent()
  172. {
  173. return $this->sHierarchyParent;
  174. }
  175. public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
  176. {
  177. $sReturn = '';
  178. $sActionUrl = addslashes($this->sSubmitTo);
  179. $sJSSubmitParams = json_encode($this->aSubmitParams);
  180. if ($this->oParentForm == null)
  181. {
  182. $sFormId = $this->sFormId;
  183. $sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
  184. $sReturn .= '<table class="prop_table">';
  185. $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>';
  186. }
  187. else
  188. {
  189. $sFormId = $this->oParentForm->sFormId;
  190. }
  191. $sHiddenFields = '';
  192. foreach($this->aFieldSets as $sLabel => $aFields)
  193. {
  194. $aDetails = array();
  195. if ($sLabel != '')
  196. {
  197. $sReturn .= '<tr><th colspan="4">'.$sLabel.'</th></tr>';
  198. }
  199. foreach($aFields as $oField)
  200. {
  201. $aRow = $oField->Render($oP, $sFormId, 'property');
  202. if ($oField->IsVisible())
  203. {
  204. $sFieldId = $this->GetFieldId($oField->GetCode());
  205. $sValidation = $this->GetValidationArea($oField->GetCode(), '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
  206. $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>';
  207. if (is_null($aRow['label']))
  208. {
  209. $sReturn .= '<tr id="row_'.$sFieldId.'"><td class="prop_value" colspan="2">'.$aRow['value'];
  210. }
  211. else
  212. {
  213. $sReturn .= '<tr id="row_'.$sFieldId.'"><td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
  214. }
  215. if (!($oField instanceof DesignerFormSelectorField))
  216. {
  217. $sReturn .= $sValidationFields;
  218. }
  219. $sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
  220. $sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
  221. $sHandlerEquals = $oField->GetHandlerEquals();
  222. $sHandlerGetValue = $oField->GetHandlerGetValue();
  223. $this->AddReadyScript(
  224. <<<EOF
  225. $('#row_$sFieldId').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', equals: $sHandlerEquals, get_field_value: $sHandlerGetValue, auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
  226. EOF
  227. );
  228. }
  229. else
  230. {
  231. $sHiddenFields .= $aRow['value'];
  232. }
  233. }
  234. }
  235. if ($this->oParentForm == null)
  236. {
  237. $sFormId = $this->sFormId;
  238. $sReturn .= '</tbody>';
  239. $sReturn .= '</table>';
  240. $sReturn .= $sHiddenFields;
  241. $sReturn .= '</form>';
  242. $sReturn .= '<div id="prop_submit_result"></div>'; // for the return of the submit operation
  243. }
  244. else
  245. {
  246. $sReturn .= $sHiddenFields;
  247. }
  248. $this->AddReadyScript(
  249. <<<EOF
  250. $('.prop_table').tableHover();
  251. var idx = 0;
  252. $('.prop_table tbody tr').each(function() {
  253. if ((idx % 2) == 0)
  254. {
  255. $(this).addClass('even');
  256. }
  257. else
  258. {
  259. $(this).addClass('odd');
  260. }
  261. idx++;
  262. });
  263. EOF
  264. );
  265. if($this->sScript != '')
  266. {
  267. $oP->add_script($this->sScript);
  268. }
  269. if($this->sReadyScript != '')
  270. {
  271. $oP->add_ready_script($this->sReadyScript);
  272. }
  273. if ($bReturnHTML)
  274. {
  275. return $sReturn;
  276. }
  277. else
  278. {
  279. $oP->add($sReturn);
  280. }
  281. }
  282. public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
  283. {
  284. $sDialogTitle = addslashes($sDialogTitle);
  285. $sOkButtonLabel = addslashes($sOkButtonLabel);
  286. $sCancelButtonLabel = Dict::S('UI:Button:Cancel');
  287. $oPage->add("<div id=\"$sDialogId\">");
  288. if ($sIntroduction != null)
  289. {
  290. $oPage->add('<div class="ui-dialog-header">'.$sIntroduction.'</div>');
  291. }
  292. $this->Render($oPage);
  293. $oPage->add('</div>');
  294. $oPage->add_ready_script(
  295. <<<EOF
  296. $('#$sDialogId').dialog({
  297. height: 'auto',
  298. width: 500,
  299. modal: true,
  300. title: '$sDialogTitle',
  301. buttons: [
  302. { text: "$sOkButtonLabel", click: function() {
  303. var oForm = $(this).closest('.ui-dialog').find('form');
  304. oForm.submit();
  305. } },
  306. { text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
  307. ],
  308. close: function() { KillAllMenus(); $(this).remove(); }
  309. });
  310. var oForm = $('#$sDialogId form');
  311. var sFormId = oForm.attr('id');
  312. ValidateForm(sFormId, true);
  313. EOF
  314. );
  315. }
  316. public function ReadParams(&$aValues = array())
  317. {
  318. foreach($this->aFieldSets as $sLabel => $aFields)
  319. {
  320. foreach($aFields as $oField)
  321. {
  322. $oField->ReadParam($aValues);
  323. }
  324. }
  325. return $aValues;
  326. }
  327. public function SetPrefix($sPrefix)
  328. {
  329. $this->sFormPrefix = $sPrefix;
  330. }
  331. public function GetPrefix()
  332. {
  333. return $this->sFormPrefix;
  334. }
  335. public function SetReadOnly($bReadOnly = true)
  336. {
  337. $this->bReadOnly = $bReadOnly;
  338. }
  339. public function IsReadOnly()
  340. {
  341. if ($this->oParentForm == null)
  342. {
  343. return $this->bReadOnly;
  344. }
  345. else
  346. {
  347. return $this->oParentForm->IsReadOnly();
  348. }
  349. }
  350. public function SetParamsContainer($sParamsContainer)
  351. {
  352. $this->sParamsContainer = $sParamsContainer;
  353. }
  354. public function GetParamsContainer()
  355. {
  356. if ($this->oParentForm == null)
  357. {
  358. return $this->sParamsContainer;
  359. }
  360. else
  361. {
  362. return $this->oParentForm->GetParamsContainer();
  363. }
  364. }
  365. public function SetParentForm($oParentForm)
  366. {
  367. $this->oParentForm = $oParentForm;
  368. }
  369. public function GetParentForm()
  370. {
  371. return $this->oParentForm;
  372. }
  373. public function SetDisplayed($bDisplayed)
  374. {
  375. $this->bDisplayed = $bDisplayed;
  376. }
  377. public function IsDisplayed()
  378. {
  379. if ($this->oParentForm == null)
  380. {
  381. return $this->bDisplayed;
  382. }
  383. else
  384. {
  385. return ($this->bDisplayed && $this->oParentForm->IsDisplayed());
  386. }
  387. }
  388. public function AddScript($sScript)
  389. {
  390. $this->sScript .= $sScript;
  391. }
  392. public function AddReadyScript($sScript)
  393. {
  394. $this->sReadyScript .= $sScript;
  395. }
  396. public function GetFieldId($sCode)
  397. {
  398. return $this->sFormPrefix.'attr_'.$sCode;
  399. }
  400. public function GetFieldName($sCode)
  401. {
  402. return 'attr_'.$sCode;
  403. }
  404. public function GetParamName($sCode)
  405. {
  406. return 'attr_'.$sCode;
  407. }
  408. public function GetValidationArea($sCode, $sContent = '')
  409. {
  410. return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$this->sFormPrefix}attr_$sCode\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
  411. }
  412. public function GetAsyncActionClass()
  413. {
  414. return $this->sAsyncActionClass;
  415. }
  416. }
  417. class DesignerTabularForm extends DesignerForm
  418. {
  419. protected $aTable;
  420. public function __construct()
  421. {
  422. parent::__construct();
  423. $this->aTable = array();
  424. }
  425. public function AddRow($aRow)
  426. {
  427. $this->aTable[] = $aRow;
  428. }
  429. public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
  430. {
  431. return $this->Render($oP, $bReturnHTML);
  432. }
  433. public function Render($oP, $bReturnHTML = false)
  434. {
  435. $sReturn = '';
  436. if ($this->oParentForm == null)
  437. {
  438. $sFormId = $this->sFormId;
  439. $sReturn = '<form id="'.$sFormId.'">';
  440. }
  441. else
  442. {
  443. $sFormId = $this->oParentForm->sFormId;
  444. }
  445. $sHiddenFields = '';
  446. $sReturn .= '<table style="width:100%">';
  447. foreach($this->aTable as $aRow)
  448. {
  449. $sReturn .= '<tr>';
  450. foreach($aRow as $field)
  451. {
  452. if (!is_object($field))
  453. {
  454. // Shortcut: pass a string for a cell containing just a label
  455. $sReturn .= '<td>'.$field.'</td>';
  456. }
  457. else
  458. {
  459. $field->SetForm($this);
  460. $aFieldData = $field->Render($oP, $sFormId);
  461. if ($field->IsVisible())
  462. {
  463. // put the label and value separated by a non-breaking space if needed
  464. $aData = array();
  465. foreach(array('label', 'value') as $sCode )
  466. {
  467. if ($aFieldData[$sCode] != '')
  468. {
  469. $aData[] = $aFieldData[$sCode];
  470. }
  471. }
  472. $sReturn .= '<td>'.implode('&nbsp;', $aData).'</td>';
  473. }
  474. else
  475. {
  476. $sHiddenFields .= $aRow['value'];
  477. }
  478. }
  479. }
  480. $sReturn .= '</tr>';
  481. }
  482. $sReturn .= '</table>';
  483. $sReturn .= $sHiddenFields;
  484. if($this->sScript != '')
  485. {
  486. $oP->add_script($this->sScript);
  487. }
  488. if($this->sReadyScript != '')
  489. {
  490. $oP->add_ready_script($this->sReadyScript);
  491. }
  492. if ($bReturnHTML)
  493. {
  494. return $sReturn;
  495. }
  496. else
  497. {
  498. $oP->add($sReturn);
  499. }
  500. }
  501. public function ReadParams(&$aValues = array())
  502. {
  503. foreach($this->aTable as $aRow)
  504. {
  505. foreach($aRow as $field)
  506. {
  507. if (is_object($field))
  508. {
  509. $field->SetForm($this);
  510. $field->ReadParam($aValues);
  511. }
  512. }
  513. }
  514. return $aValues;
  515. }
  516. }
  517. class DesignerFormField
  518. {
  519. protected $sLabel;
  520. protected $sCode;
  521. protected $defaultValue;
  522. protected $oForm;
  523. protected $bMandatory;
  524. protected $bReadOnly;
  525. protected $bAutoApply;
  526. protected $aCSSClasses;
  527. protected $bDisplayed;
  528. public function __construct($sCode, $sLabel, $defaultValue)
  529. {
  530. $this->sLabel = $sLabel;
  531. $this->sCode = $sCode;
  532. $this->defaultValue = $defaultValue;
  533. $this->bMandatory = false;
  534. $this->bReadOnly = false;
  535. $this->bAutoApply = false;
  536. $this->aCSSClasses = array();
  537. $this->bDisplayed = true;
  538. }
  539. public function GetCode()
  540. {
  541. return $this->sCode;
  542. }
  543. public function SetForm($oForm)
  544. {
  545. $this->oForm = $oForm;
  546. }
  547. public function SetMandatory($bMandatory = true)
  548. {
  549. $this->bMandatory = $bMandatory;
  550. }
  551. public function SetReadOnly($bReadOnly = true)
  552. {
  553. $this->bReadOnly = $bReadOnly;
  554. }
  555. public function IsReadOnly()
  556. {
  557. return ($this->oForm->IsReadOnly() || $this->bReadOnly);
  558. }
  559. public function SetAutoApply($bAutoApply)
  560. {
  561. $this->bAutoApply = $bAutoApply;
  562. }
  563. public function IsAutoApply()
  564. {
  565. return $this->bAutoApply;
  566. }
  567. public function SetDisplayed($bDisplayed)
  568. {
  569. $this->bDisplayed = $bDisplayed;
  570. }
  571. public function IsDisplayed()
  572. {
  573. return $this->bDisplayed;
  574. }
  575. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  576. {
  577. $sId = $this->oForm->GetFieldId($this->sCode);
  578. $sName = $this->oForm->GetFieldName($this->sCode);
  579. return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  580. }
  581. public function ReadParam(&$aValues)
  582. {
  583. if ($this->IsReadOnly())
  584. {
  585. $aValues[$this->sCode] = $this->defaultValue;
  586. }
  587. else
  588. {
  589. if ($this->oForm->GetParamsContainer() != '')
  590. {
  591. $aParams = utils::ReadParam($this->oForm->GetParamsContainer(), array(), false, 'raw_data');
  592. if (array_key_exists($this->oForm->GetParamName($this->sCode), $aParams))
  593. {
  594. $aValues[$this->sCode] = $aParams[$this->oForm->GetParamName($this->sCode)];
  595. }
  596. else
  597. {
  598. $aValues[$this->sCode] = $this->defaultValue;
  599. }
  600. }
  601. else
  602. {
  603. $aValues[$this->sCode] = utils::ReadParam($this->oForm->GetParamName($this->sCode), $this->defaultValue, false, 'raw_data');
  604. }
  605. }
  606. }
  607. public function IsVisible()
  608. {
  609. return true;
  610. }
  611. public function AddCSSClass($sCSSClass)
  612. {
  613. $this->aCSSClasses[] = $sCSSClass;
  614. }
  615. public function GetHandlerEquals()
  616. {
  617. return 'null';
  618. }
  619. public function GetHandlerGetValue()
  620. {
  621. return 'null';
  622. }
  623. }
  624. class DesignerLabelField extends DesignerFormField
  625. {
  626. protected $sDescription;
  627. public function __construct($sLabel, $sDescription)
  628. {
  629. parent::__construct('', $sLabel, '');
  630. $this->sDescription = $sDescription;
  631. }
  632. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  633. {
  634. $sId = $this->oForm->GetFieldId($this->sCode);
  635. $sName = $this->oForm->GetFieldName($this->sCode);
  636. return array('label' => $this->sLabel, 'value' => $sDescription);
  637. }
  638. public function ReadParam(&$aValues)
  639. {
  640. }
  641. public function IsVisible()
  642. {
  643. return true;
  644. }
  645. }
  646. class DesignerTextField extends DesignerFormField
  647. {
  648. protected $sValidationPattern;
  649. protected $aForbiddenValues;
  650. protected $sExplainForbiddenValues;
  651. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  652. {
  653. parent::__construct($sCode, $sLabel, $defaultValue);
  654. $this->sValidationPattern = '';
  655. $this->aForbiddenValues = null;
  656. $this->sExplainForbiddenValues = null;
  657. }
  658. public function SetValidationPattern($sValidationPattern)
  659. {
  660. $this->sValidationPattern = $sValidationPattern;
  661. }
  662. public function SetForbiddenValues($aValues, $sExplain)
  663. {
  664. $this->aForbiddenValues = $aValues;
  665. $iDefaultKey = array_search($this->defaultValue, $this->aForbiddenValues);
  666. if ($iDefaultKey !== false)
  667. {
  668. // The default (current) value is always allowed...
  669. unset($this->aForbiddenValues[$iDefaultKey]);
  670. }
  671. $this->sExplainForbiddenValues = $sExplain;
  672. }
  673. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  674. {
  675. $sId = $this->oForm->GetFieldId($this->sCode);
  676. $sName = $this->oForm->GetFieldName($this->sCode);
  677. if ($this->IsReadOnly())
  678. {
  679. $sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
  680. }
  681. else
  682. {
  683. $sPattern = addslashes($this->sValidationPattern);
  684. if (is_array($this->aForbiddenValues))
  685. {
  686. $sForbiddenValues = json_encode($this->aForbiddenValues);
  687. $sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
  688. }
  689. else
  690. {
  691. $sForbiddenValues = 'null';
  692. $sExplainForbiddenValues = 'null';
  693. }
  694. $sMandatory = $this->bMandatory ? 'true' : 'false';
  695. $oP->add_ready_script(
  696. <<<EOF
  697. $('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
  698. {
  699. var myTimer = null;
  700. $('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
  701. }
  702. EOF
  703. );
  704. $sCSSClasses = '';
  705. if (count($this->aCSSClasses) > 0)
  706. {
  707. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  708. }
  709. $sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
  710. }
  711. return array('label' => $this->sLabel, 'value' => $sHtmlValue);
  712. }
  713. public function ReadParam(&$aValues)
  714. {
  715. parent::ReadParam($aValues);
  716. if (($this->sValidationPattern != '') &&(!preg_match('/'.$this->sValidationPattern.'/', $aValues[$this->sCode])) )
  717. {
  718. $aValues[$this->sCode] = $this->defaultValue;
  719. }
  720. else if(($this->aForbiddenValues != null) && in_array($aValues[$this->sCode], $this->aForbiddenValues))
  721. {
  722. // Reject the value...
  723. $aValues[$this->sCode] = $this->defaultValue;
  724. }
  725. }
  726. }
  727. class DesignerLongTextField extends DesignerTextField
  728. {
  729. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  730. {
  731. $sId = $this->oForm->GetFieldId($this->sCode);
  732. $sName = $this->oForm->GetFieldName($this->sCode);
  733. $sPattern = addslashes($this->sValidationPattern);
  734. if (is_array($this->aForbiddenValues))
  735. {
  736. $sForbiddenValues = json_encode($this->aForbiddenValues);
  737. $sExplainForbiddenValues = addslashes($this->sExplainForbiddenValues);
  738. }
  739. else
  740. {
  741. $sForbiddenValues = 'null';
  742. $sExplainForbiddenValues = 'null';
  743. }
  744. $sMandatory = $this->bMandatory ? 'true' : 'false';
  745. $sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
  746. $oP->add_ready_script(
  747. <<<EOF
  748. $('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
  749. {
  750. var myTimer = null;
  751. $('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
  752. }
  753. EOF
  754. );
  755. $sCSSClasses = '';
  756. if (count($this->aCSSClasses) > 0)
  757. {
  758. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  759. }
  760. return array('label' => $this->sLabel, 'value' => "<textarea $sCSSClasses id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>");
  761. }
  762. }
  763. class DesignerComboField extends DesignerFormField
  764. {
  765. protected $aAllowedValues;
  766. protected $bMultipleSelection;
  767. protected $bOtherChoices;
  768. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  769. {
  770. parent::__construct($sCode, $sLabel, $defaultValue);
  771. $this->aAllowedValues = array();
  772. $this->bMultipleSelection = false;
  773. $this->bOtherChoices = false;
  774. $this->bAutoApply = true;
  775. }
  776. public function SetAllowedValues($aAllowedValues)
  777. {
  778. $this->aAllowedValues = $aAllowedValues;
  779. }
  780. public function MultipleSelection($bMultipleSelection = true)
  781. {
  782. $this->bMultipleSelection = $bMultipleSelection;
  783. }
  784. public function OtherChoices($bOtherChoices = true)
  785. {
  786. $this->bOtherChoices = $bOtherChoices;
  787. }
  788. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  789. {
  790. $sId = $this->oForm->GetFieldId($this->sCode);
  791. $sName = $this->oForm->GetFieldName($this->sCode);
  792. $sChecked = $this->defaultValue ? 'checked' : '';
  793. $sMandatory = $this->bMandatory ? 'true' : 'false';
  794. $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
  795. $sCSSClasses = '';
  796. if (count($this->aCSSClasses) > 0)
  797. {
  798. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  799. }
  800. if ($this->IsReadOnly())
  801. {
  802. $aSelected = array();
  803. $aHiddenValues = array();
  804. foreach($this->aAllowedValues as $sKey => $sDisplayValue)
  805. {
  806. if ($this->bMultipleSelection)
  807. {
  808. if(in_array($sKey, $this->defaultValue))
  809. {
  810. $aSelected[] = $sDisplayValue;
  811. $aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
  812. }
  813. }
  814. else
  815. {
  816. if ($sKey == $this->defaultValue)
  817. {
  818. $aSelected[] = $sDisplayValue;
  819. $aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
  820. }
  821. }
  822. }
  823. $sHtml = "<span $sCSSClasses>".htmlentities(implode(', ', $aSelected), ENT_QUOTES, 'UTF-8').implode($aHiddenValues)."</span>";
  824. }
  825. else
  826. {
  827. if ($this->bMultipleSelection)
  828. {
  829. $sHtml = "<select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
  830. }
  831. else
  832. {
  833. $sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\">";
  834. $sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
  835. }
  836. foreach($this->aAllowedValues as $sKey => $sDisplayValue)
  837. {
  838. if ($this->bMultipleSelection)
  839. {
  840. $sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
  841. }
  842. else
  843. {
  844. $sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
  845. }
  846. // Quick and dirty: display the menu parents as a tree
  847. $sHtmlValue = str_replace(' ', '&nbsp;', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
  848. $sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
  849. }
  850. $sHtml .= "</select>";
  851. if ($this->bOtherChoices)
  852. {
  853. $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"/>';
  854. }
  855. $oP->add_ready_script(
  856. <<<EOF
  857. $('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
  858. EOF
  859. );
  860. }
  861. return array('label' => $this->sLabel, 'value' => $sHtml);
  862. }
  863. public function ReadParam(&$aValues)
  864. {
  865. parent::ReadParam($aValues);
  866. if ($aValues[$this->sCode] == 'null')
  867. {
  868. $aValues[$this->sCode] = array();
  869. }
  870. }
  871. }
  872. class DesignerBooleanField extends DesignerFormField
  873. {
  874. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  875. {
  876. parent::__construct($sCode, $sLabel, $defaultValue);
  877. $this->bAutoApply = true;
  878. }
  879. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  880. {
  881. $sId = $this->oForm->GetFieldId($this->sCode);
  882. $sName = $this->oForm->GetFieldName($this->sCode);
  883. $sChecked = $this->defaultValue ? 'checked' : '';
  884. if ($this->IsReadOnly())
  885. {
  886. $sLabel = $this->defaultValue ? Dict::S('UI:UserManagement:ActionAllowed:Yes') : Dict::S('UI:UserManagement:ActionAllowed:No'); //TODO use our own yes/no translations
  887. $sHtmlValue = "<span>".htmlentities($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
  888. }
  889. else
  890. {
  891. $sCSSClasses = '';
  892. if (count($this->aCSSClasses) > 0)
  893. {
  894. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  895. }
  896. $sHtmlValue = "<input $sCSSClasses type=\"checkbox\" $sChecked id=\"$sId\" name=\"$sName\" value=\"true\">";
  897. }
  898. return array('label' => $this->sLabel, 'value' => $sHtmlValue);
  899. }
  900. public function ReadParam(&$aValues)
  901. {
  902. if ($this->IsReadOnly())
  903. {
  904. $aValues[$this->sCode] = $this->defaultValue;
  905. }
  906. else
  907. {
  908. $sParamsContainer = $this->oForm->GetParamsContainer();
  909. if ($sParamsContainer != '')
  910. {
  911. $aParams = utils::ReadParam($sParamsContainer, array(), false, 'raw_data');
  912. if (array_key_exists($this->oForm->GetParamName($this->sCode), $aParams))
  913. {
  914. $sValue = $aParams[$this->oForm->GetParamName($this->sCode)];
  915. }
  916. else
  917. {
  918. $sValue = 'false';
  919. }
  920. }
  921. else
  922. {
  923. $sValue = utils::ReadParam($this->oForm->GetParamName($this->sCode), 'false', false, 'raw_data');
  924. }
  925. $aValues[$this->sCode] = ($sValue == 'true');
  926. }
  927. }
  928. }
  929. class DesignerHiddenField extends DesignerFormField
  930. {
  931. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  932. {
  933. parent::__construct($sCode, $sLabel, $defaultValue);
  934. }
  935. public function IsVisible()
  936. {
  937. return false;
  938. }
  939. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  940. {
  941. $sId = $this->oForm->GetFieldId($this->sCode);
  942. $sName = $this->oForm->GetFieldName($this->sCode);
  943. $sChecked = $this->defaultValue ? 'checked' : '';
  944. return array('label' =>'', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  945. }
  946. }
  947. class DesignerIconSelectionField extends DesignerFormField
  948. {
  949. protected $sUploadUrl;
  950. protected $aAllowedValues;
  951. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  952. {
  953. parent::__construct($sCode, $sLabel, $defaultValue);
  954. $this->bAutoApply = true;
  955. $this->sUploadUrl = null;
  956. }
  957. public function SetAllowedValues($aAllowedValues)
  958. {
  959. $this->aAllowedValues = $aAllowedValues;
  960. }
  961. public function EnableUpload($sIconUploadUrl)
  962. {
  963. $this->sUploadUrl = $sIconUploadUrl;
  964. }
  965. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  966. {
  967. $sId = $this->oForm->GetFieldId($this->sCode);
  968. $sName = $this->oForm->GetFieldName($this->sCode);
  969. $idx = 0;
  970. foreach($this->aAllowedValues as $index => $aValue)
  971. {
  972. if ($aValue['value'] == $this->defaultValue)
  973. {
  974. $idx = $index;
  975. break;
  976. }
  977. }
  978. $sJSItems = json_encode($this->aAllowedValues);
  979. $sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
  980. if (!$this->IsReadOnly())
  981. {
  982. $oP->add_ready_script(
  983. <<<EOF
  984. $('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
  985. EOF
  986. );
  987. }
  988. $sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
  989. return array('label' =>$this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>");
  990. }
  991. }
  992. class RunTimeIconSelectionField extends DesignerIconSelectionField
  993. {
  994. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  995. {
  996. parent::__construct($sCode, $sLabel, $defaultValue);
  997. $aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
  998. ksort($aAllIcons);
  999. $aValues = array();
  1000. foreach($aAllIcons as $sFilePath)
  1001. {
  1002. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  1003. }
  1004. $this->SetAllowedValues($aValues);
  1005. }
  1006. static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
  1007. {
  1008. $aResult = array();
  1009. // Populate automatically the list of icon files
  1010. if ($hDir = @opendir($sBaseDir.'/'.$sDir))
  1011. {
  1012. while (($sFile = readdir($hDir)) !== false)
  1013. {
  1014. $aMatches = array();
  1015. if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
  1016. {
  1017. $sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
  1018. $aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath));
  1019. }
  1020. if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
  1021. {
  1022. $aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
  1023. }
  1024. }
  1025. closedir($hDir);
  1026. }
  1027. return $aResult;
  1028. }
  1029. public function ValueFromDOMNode($oDOMNode)
  1030. {
  1031. return $oDOMNode->textContent;
  1032. }
  1033. public function ValueToDOMNode($oDOMNode, $value)
  1034. {
  1035. $oTextNode = $oDOMNode->ownerDocument->createTextNode($value);
  1036. $oDOMNode->appendChild($oTextNode);
  1037. }
  1038. public function MakeFileUrl($value)
  1039. {
  1040. return utils::GetAbsoluteUrlModulesRoot().$value;
  1041. }
  1042. public function GetDefaultValue($sClass = 'Contact')
  1043. {
  1044. $sIconPath = MetaModel::GetClassIcon($sClass, false);
  1045. $sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
  1046. return $sIcon;
  1047. }
  1048. }
  1049. class DesignerSortableField extends DesignerFormField
  1050. {
  1051. protected $aAllowedValues;
  1052. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  1053. {
  1054. parent::__construct($sCode, $sLabel, $defaultValue);
  1055. $this->aAllowedValues = array();
  1056. }
  1057. public function SetAllowedValues($aAllowedValues)
  1058. {
  1059. $this->aAllowedValues = $aAllowedValues;
  1060. }
  1061. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1062. {
  1063. $bOpen = false;
  1064. $sId = $this->oForm->GetFieldId($this->sCode);
  1065. $sName = $this->oForm->GetFieldName($this->sCode);
  1066. $sReadOnly = $this->IsReadOnly() ? 'readonly="readonly"' : '';
  1067. $aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
  1068. $sJSFields = json_encode(array_keys($this->aAllowedValues));
  1069. $oP->add_ready_script(
  1070. "$('#$sId').sortable_field({aAvailableFields: $sJSFields});"
  1071. );
  1072. return $aResult;
  1073. }
  1074. }
  1075. class DesignerFormSelectorField extends DesignerFormField
  1076. {
  1077. protected $aSubForms;
  1078. protected $defaultRealValue; // What's stored as default value is actually the index
  1079. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  1080. {
  1081. parent::__construct($sCode, $sLabel, 0);
  1082. $this->defaultRealValue = $defaultValue;
  1083. $this->aSubForms = array();
  1084. }
  1085. public function AddSubForm($oSubForm, $sLabel, $sValue)
  1086. {
  1087. $idx = count($this->aSubForms);
  1088. $this->aSubForms[] = array('form' => $oSubForm, 'label' => $sLabel, 'value' => $sValue);
  1089. if ($sValue == $this->defaultRealValue)
  1090. {
  1091. // Store the index of the selected/default form
  1092. $this->defaultValue = count($this->aSubForms) - 1;
  1093. }
  1094. }
  1095. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1096. {
  1097. $sId = $this->oForm->GetFieldId($this->sCode);
  1098. $sName = $this->oForm->GetFieldName($this->sCode);
  1099. $sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
  1100. $sCSSClasses = '';
  1101. if (count($this->aCSSClasses) > 0)
  1102. {
  1103. $sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
  1104. }
  1105. if ($this->IsReadOnly())
  1106. {
  1107. $aSelected = array();
  1108. $aHiddenValues = array();
  1109. $sDisplayValue = '';
  1110. $sHiddenValue = '';
  1111. foreach($this->aSubForms as $sKey => $aFormData)
  1112. {
  1113. if ($sKey == $this->defaultValue)
  1114. {
  1115. $sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
  1116. $sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
  1117. break;
  1118. }
  1119. }
  1120. $sHtml = "<span $sCSSClasses>".$sDisplayValue.$sHiddenValue."</span>";
  1121. }
  1122. else
  1123. {
  1124. $sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
  1125. foreach($this->aSubForms as $sKey => $aFormData)
  1126. {
  1127. $sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
  1128. $sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
  1129. $sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".$sDisplayValue."</option>";
  1130. }
  1131. $sHtml .= "</select>";
  1132. }
  1133. if ($sRenderMode == 'property')
  1134. {
  1135. $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>';
  1136. }
  1137. foreach($this->aSubForms as $sKey => $aFormData)
  1138. {
  1139. $sId = $this->oForm->GetFieldId($this->sCode);
  1140. $sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"';
  1141. $oSubForm = $aFormData['form'];
  1142. $oSubForm->SetParentForm($this->oForm);
  1143. $oSubForm->CopySubmitParams($this->oForm);
  1144. $oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
  1145. if ($sRenderMode == 'property')
  1146. {
  1147. // Note: Managing the visibility of nested subforms had several implications
  1148. // 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection
  1149. // 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs).
  1150. // The div hierarchy has been emulated by adding attributes to the tbody tags:
  1151. // - data-selector : uniquely identifies the DesignerFormSelectorField that has an impact on the visibility of the node
  1152. // - data-path : uniquely identifies the combination of users choices that must be made to show the node
  1153. // - data-state : records the state, depending on the user choice on the FormSelectorField just above the node, but indepentantly from the visibility in the page (can be visible in the form itself being in a hidden form)
  1154. // Then a series of actions are performed to hide and show the relevant nodes, depending on the user choice
  1155. $sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
  1156. $oSubForm->SetHierarchyParent($sSelector);
  1157. $sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.':'.$sKey;
  1158. $oSubForm->SetHierarchyPath($sPath);
  1159. $oSubForm->SetDisplayed($sKey == $this->defaultValue);
  1160. $sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
  1161. $sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
  1162. $sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
  1163. $sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
  1164. $sParentStyle = '';
  1165. if ($oParent = $this->oForm->GetParentForm())
  1166. {
  1167. $sParentStyle = ($oParent->IsDisplayed()) ? '' : 'style="display:none"';
  1168. $sParentSelector = $oParent->GetHierarchyParent();
  1169. $sParentPath = $oParent->GetHierarchyPath();
  1170. }
  1171. else
  1172. {
  1173. $sParentSelector = '';
  1174. $sParentPath = '';
  1175. }
  1176. $sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sParentStyle>";
  1177. }
  1178. else
  1179. {
  1180. $sHtml .= "<div class=\"subform_{$sId} {$sId}_{$sKey}\" $sStyle>";
  1181. $sHtml .= $oSubForm->Render($oP, true);
  1182. $sHtml .= "</div>";
  1183. }
  1184. }
  1185. if ($sRenderMode == 'property')
  1186. {
  1187. $sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
  1188. $oP->add_ready_script(
  1189. <<<EOF
  1190. $('#$sId').bind('change reverted', function() {
  1191. // Mark all the direct children as hidden
  1192. $('tbody[data-selector="$sSelector"]').attr('data-state', 'hidden');
  1193. // Mark the selected one as visible
  1194. var sSelectedHierarchy = '$sSelector:'+this.value;
  1195. $('tbody[data-path="'+sSelectedHierarchy+'"]').attr('data-state', 'visible');
  1196. // Show all items behind the current one
  1197. $('tbody[data-path^="$sSelector"]').show();
  1198. // Hide items behind the current one as soon as it is behind a hidden node (or itself is marked as hidden)
  1199. $('tbody[data-path^="$sSelector"][data-state="hidden"]').each(function(){
  1200. $(this).hide();
  1201. var sPath = $(this).attr('data-path');
  1202. $('tbody[data-path^="'+sPath+'/"]').hide();
  1203. });
  1204. });
  1205. EOF
  1206. );
  1207. }
  1208. else
  1209. {
  1210. $oP->add_ready_script(
  1211. <<<EOF
  1212. $('#$sId').bind('change reverted', function() { $('.subform_{$sId}').hide(); $('.{$sId}_'+this.value).show(); } );
  1213. EOF
  1214. );
  1215. }
  1216. return array('label' => $this->sLabel, 'value' => $sHtml);
  1217. }
  1218. public function ReadParam(&$aValues)
  1219. {
  1220. parent::ReadParam($aValues);
  1221. $sKey = $aValues[$this->sCode];
  1222. $aValues[$this->sCode] = $this->aSubForms[$sKey]['value'];
  1223. $this->aSubForms[$sKey]['form']->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
  1224. $this->aSubForms[$sKey]['form']->SetParentForm($this->oForm);
  1225. $this->aSubForms[$sKey]['form']->ReadParams($aValues);
  1226. }
  1227. }
  1228. class DesignerSubFormField extends DesignerFormField
  1229. {
  1230. protected $oSubForm;
  1231. public function __construct($sLabel, $oSubForm)
  1232. {
  1233. parent::__construct('', $sLabel, '');
  1234. $this->oSubForm = $oSubForm;
  1235. }
  1236. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1237. {
  1238. $this->oSubForm->SetParentForm($this->oForm);
  1239. $this->oSubForm->CopySubmitParams($this->oForm);
  1240. if ($sRenderMode == 'property')
  1241. {
  1242. $sHtml = $this->oSubForm->RenderAsPropertySheet($oP, true);
  1243. }
  1244. else
  1245. {
  1246. $sHtml = $this->oSubForm->Render($oP, true);
  1247. }
  1248. return array('label' => $this->sLabel, 'value' => $sHtml);
  1249. }
  1250. public function ReadParam(&$aValues)
  1251. {
  1252. $this->oSubForm->SetParentForm($this->oForm);
  1253. $this->oSubForm->ReadParams($aValues);
  1254. }
  1255. }
  1256. class DesignerStaticTextField extends DesignerFormField
  1257. {
  1258. public function __construct($sCode, $sLabel = '', $defaultValue = '')
  1259. {
  1260. parent::__construct($sCode, $sLabel, $defaultValue);
  1261. }
  1262. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1263. {
  1264. return array('label' => $this->sLabel, 'value' => $this->defaultValue);
  1265. }
  1266. }
  1267. class DesignerButtonsField extends DesignerFormField
  1268. {
  1269. protected $aButtons;
  1270. public function __construct($sCode, $sLabel = null)
  1271. {
  1272. parent::__construct($sCode, $sLabel, null);
  1273. $this->aButtons = array('left' => array(), 'center' => array(), 'right' => array());
  1274. }
  1275. public function AddButton($sPos, DesignerButtonsFieldCtrl $oButton)
  1276. {
  1277. $this->aButtons[$sPos][] = $oButton;
  1278. }
  1279. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1280. {
  1281. $sHtml = '<table style="width:100%"><tbody><tr>';
  1282. foreach($this->aButtons as $sPos => $aButtons)
  1283. {
  1284. $aBtns = array();
  1285. foreach($aButtons as $oButton)
  1286. {
  1287. $aBtns[] = $oButton->Render($oP, $sFormId, $sRenderMode);
  1288. }
  1289. $sHtml .= '<td style="text-align:'.$sPos.'; ">'.implode(' ', $aBtns)."</td>";
  1290. }
  1291. $sHtml .= '</tr></tbody></table>';
  1292. return array('label' => $this->sLabel, 'value' => $sHtml);
  1293. }
  1294. }
  1295. /**
  1296. * A button to add to a DesignerButtonsField
  1297. *
  1298. */
  1299. abstract class DesignerButtonsFieldCtrl
  1300. {
  1301. protected $sId;
  1302. public function __construct($sId)
  1303. {
  1304. $this->sId = $sId;
  1305. }
  1306. abstract public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog');
  1307. }
  1308. class DesignerButtonCtrl extends DesignerButtonsFieldCtrl
  1309. {
  1310. protected $sLabel;
  1311. protected $sJSAction;
  1312. public function __construct($sId, $sLabel, $sJSAction)
  1313. {
  1314. parent::__construct($sId);
  1315. $this->sLabel = $sLabel;
  1316. $this->sJSAction = $sJSAction;
  1317. }
  1318. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1319. {
  1320. $sAction = htmlentities($this->sJSAction, ENT_QUOTES, 'UTF-8');
  1321. return '<button id="'.$this->sId.'" type="button" onclick="'.$sAction.'">'.$this->sLabel.'</button>';
  1322. }
  1323. }
  1324. class DesignerSelectCtrl extends DesignerButtonsFieldCtrl
  1325. {
  1326. protected $aValues;
  1327. protected $defaultValue;
  1328. protected $sJSAction;
  1329. public function __construct($sId, $sLabel, $aValues, $defaultValue, $sJSAction)
  1330. {
  1331. parent::__construct($sId);
  1332. $this->sLabel = $sLabel;
  1333. $this->aValues = $aValues;
  1334. $this->defaultValue = $defaultValue;
  1335. $this->sJSAction = $sJSAction;
  1336. }
  1337. public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
  1338. {
  1339. $sAction = htmlentities($this->sJSAction, ENT_QUOTES, 'UTF-8');
  1340. $sHtml = '<span>'.$this->sLabel.'&nbsp;<select id="'.$this->sId.'" onchange="'.$sAction.'">';
  1341. foreach($this->aValues as $value => $sLabel)
  1342. {
  1343. $sSelected = ($value == $this->defaultValue) ? 'selected' : '';
  1344. $sHtml .= '<option value="'.htmlentities($value).'"'.$sSelected.'>'.$sLabel.'</option>';
  1345. }
  1346. $sHtml .= '</select></span>';
  1347. return $sHtml;
  1348. }
  1349. }