forms.class.inc.php 34 KB

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