advmultiselect.php 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. <?php
  2. /**
  3. * Copyright (c) 2005-2009, Laurent Laville <pear@laurent-laville.org>
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * * Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * * Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * * Neither the name of the authors nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  24. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  25. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  26. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  27. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  29. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30. * POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. * PHP versions 4 and 5
  33. *
  34. * @category HTML
  35. * @package HTML_QuickForm_advmultiselect
  36. * @author Laurent Laville <pear@laurent-laville.org>
  37. * @copyright 2005-2009 Laurent Laville
  38. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  39. * @version CVS: $Id: advmultiselect.php,v 1.36 2009/04/05 07:03:39 farell Exp $
  40. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  41. * @since File available since Release 0.4.0
  42. */
  43. /**
  44. * Code
  45. */
  46. require_once 'HTML/QuickForm/select.php';
  47. /**
  48. * Basic error codes
  49. *
  50. * @var integer
  51. * @since 1.5.0
  52. */
  53. define('HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT', 1);
  54. /**
  55. * Element for HTML_QuickForm that emulate a multi-select.
  56. *
  57. * The HTML_QuickForm_advmultiselect package adds an element to the
  58. * HTML_QuickForm package that is two select boxes next to each other
  59. * emulating a multi-select.
  60. *
  61. * @category HTML
  62. * @package HTML_QuickForm_advmultiselect
  63. * @author Laurent Laville <pear@laurent-laville.org>
  64. * @copyright 2005-2009 Laurent Laville
  65. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  66. * @version Release: @package_version@
  67. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  68. * @since Class available since Release 0.4.0
  69. */
  70. class HTML_QuickForm_advmultiselect extends HTML_QuickForm_select
  71. {
  72. /**
  73. * Prefix function name in javascript move selections
  74. *
  75. * @var string
  76. * @access private
  77. * @since 0.4.0
  78. */
  79. var $_jsPrefix;
  80. /**
  81. * Postfix function name in javascript move selections
  82. *
  83. * @var string
  84. * @access private
  85. * @since 0.4.0
  86. */
  87. var $_jsPostfix;
  88. /**
  89. * Associative array of the multi select container attributes
  90. *
  91. * @var array
  92. * @access private
  93. * @since 0.4.0
  94. */
  95. var $_tableAttributes;
  96. /**
  97. * Associative array of the add button attributes
  98. *
  99. * @var array
  100. * @access private
  101. * @since 0.4.0
  102. */
  103. var $_addButtonAttributes;
  104. /**
  105. * Associative array of the remove button attributes
  106. *
  107. * @var array
  108. * @access private
  109. * @since 0.4.0
  110. */
  111. var $_removeButtonAttributes;
  112. /**
  113. * Associative array of the select all button attributes
  114. *
  115. * @var array
  116. * @access private
  117. * @since 1.1.0
  118. */
  119. var $_allButtonAttributes;
  120. /**
  121. * Associative array of the select none button attributes
  122. *
  123. * @var array
  124. * @access private
  125. * @since 1.1.0
  126. */
  127. var $_noneButtonAttributes;
  128. /**
  129. * Associative array of the toggle selection button attributes
  130. *
  131. * @var array
  132. * @access private
  133. * @since 1.1.0
  134. */
  135. var $_toggleButtonAttributes;
  136. /**
  137. * Associative array of the move up button attributes
  138. *
  139. * @var array
  140. * @access private
  141. * @since 0.5.0
  142. */
  143. var $_upButtonAttributes;
  144. /**
  145. * Associative array of the move up button attributes
  146. *
  147. * @var array
  148. * @access private
  149. * @since 0.5.0
  150. */
  151. var $_downButtonAttributes;
  152. /**
  153. * Associative array of the move top button attributes
  154. *
  155. * @var array
  156. * @access private
  157. * @since 1.5.0
  158. */
  159. var $_topButtonAttributes;
  160. /**
  161. * Associative array of the move bottom button attributes
  162. *
  163. * @var array
  164. * @access private
  165. * @since 1.5.0
  166. */
  167. var $_bottomButtonAttributes;
  168. /**
  169. * Defines if both list (unselected, selected) will have their elements be
  170. * arranged from lowest to highest (or reverse)
  171. * depending on comparaison function.
  172. *
  173. * SORT_ASC is used to sort in ascending order
  174. * SORT_DESC is used to sort in descending order
  175. *
  176. * @var string ('none' == false, 'asc' == SORT_ASC, 'desc' == SORT_DESC)
  177. * @access private
  178. * @since 0.5.0
  179. */
  180. var $_sort;
  181. /**
  182. * Associative array of the unselected item box attributes
  183. *
  184. * @var array
  185. * @access private
  186. * @since 0.4.0
  187. */
  188. var $_attributesUnselected;
  189. /**
  190. * Associative array of the selected item box attributes
  191. *
  192. * @var array
  193. * @access private
  194. * @since 0.4.0
  195. */
  196. var $_attributesSelected;
  197. /**
  198. * Associative array of the internal hidden box attributes
  199. *
  200. * @var array
  201. * @access private
  202. * @since 0.4.0
  203. */
  204. var $_attributesHidden;
  205. /**
  206. * Default Element template string
  207. *
  208. * @var string
  209. * @access private
  210. * @since 0.4.0
  211. */
  212. var $_elementTemplate;
  213. /**
  214. * Default Element stylesheet string
  215. *
  216. * @var string
  217. * @access private
  218. * @since 0.4.0
  219. */
  220. var $_elementCSS = '
  221. #qfams_{id} {
  222. font: 13.3px sans-serif;
  223. background-color: #fff;
  224. overflow: auto;
  225. height: 14.3em;
  226. width: 12em;
  227. border-left: 1px solid #404040;
  228. border-top: 1px solid #404040;
  229. border-bottom: 1px solid #d4d0c8;
  230. border-right: 1px solid #d4d0c8;
  231. }
  232. #qfams_{id} label {
  233. padding-right: 3px;
  234. display: block;
  235. }
  236. ';
  237. /**
  238. * Class constructor
  239. *
  240. * Class constructors :
  241. * Zend Engine 1 uses HTML_QuickForm_advmultiselect, while
  242. * Zend Engine 2 uses __construct
  243. *
  244. * @param string $elementName Dual Select name attribute
  245. * @param mixed $elementLabel Label(s) for the select boxes
  246. * @param mixed $options Data to be used to populate options
  247. * @param mixed $attributes Either a typical HTML attribute string or
  248. * an associative array
  249. * @param integer $sort Either SORT_ASC for auto ascending arrange,
  250. * SORT_DESC for auto descending arrange, or
  251. * NULL for no sort (append at end: default)
  252. *
  253. * @access public
  254. * @return void
  255. * @since version 0.4.0 (2005-06-25)
  256. */
  257. function HTML_QuickForm_advmultiselect($elementName = null, $elementLabel = null,
  258. $options = null, $attributes = null,
  259. $sort = null)
  260. {
  261. $opts = $options;
  262. $options = null; // prevent to use the default select element load options
  263. $this->HTML_QuickForm_select($elementName, $elementLabel,
  264. $options, $attributes);
  265. // allow to load options at once and take care of fancy attributes
  266. $this->load($opts);
  267. // add multiple selection attribute by default if missing
  268. $this->updateAttributes(array('multiple' => 'multiple'));
  269. if (is_null($this->getAttribute('size'))) {
  270. // default size is ten item on each select box (left and right)
  271. $this->updateAttributes(array('size' => 10));
  272. }
  273. if (is_null($this->getAttribute('style'))) {
  274. // default width of each select box
  275. $this->updateAttributes(array('style' => 'width:100px;'));
  276. }
  277. $this->_tableAttributes = $this->getAttribute('class');
  278. if (is_null($this->_tableAttributes)) {
  279. // default table layout
  280. $attr = array('border' => '0',
  281. 'cellpadding' => '10', 'cellspacing' => '0');
  282. } else {
  283. $attr = array('class' => $this->_tableAttributes);
  284. $this->_removeAttr('class', $this->_attributes);
  285. }
  286. $this->_tableAttributes = $this->_getAttrString($attr);
  287. // set default add button attributes
  288. $this->setButtonAttributes('add');
  289. // set default remove button attributes
  290. $this->setButtonAttributes('remove');
  291. // set default selectall button attributes
  292. $this->setButtonAttributes('all');
  293. // set default selectnone button attributes
  294. $this->setButtonAttributes('none');
  295. // set default toggle selection button attributes
  296. $this->setButtonAttributes('toggle');
  297. // set default move up button attributes
  298. $this->setButtonAttributes('moveup');
  299. // set default move up button attributes
  300. $this->setButtonAttributes('movedown');
  301. // set default move top button attributes
  302. $this->setButtonAttributes('movetop');
  303. // set default move bottom button attributes
  304. $this->setButtonAttributes('movebottom');
  305. // defines javascript functions names
  306. $this->_jsPrefix = 'QFAMS.';
  307. $this->_jsPostfix = 'moveSelection';
  308. // set select boxes sort order (none by default)
  309. if (!isset($sort)) {
  310. $sort = false;
  311. }
  312. if ($sort === SORT_ASC) {
  313. $this->_sort = 'asc';
  314. } elseif ($sort === SORT_DESC) {
  315. $this->_sort = 'desc';
  316. } else {
  317. $this->_sort = 'none';
  318. }
  319. // set the default advmultiselect element template (with javascript embedded)
  320. $this->setElementTemplate();
  321. }
  322. /**
  323. * Sets the button attributes
  324. *
  325. * In <b>custom example 1</b>, the <i>add</i> and <i>remove</i> buttons
  326. * have look set by the css class <i>inputCommand</i>.
  327. *
  328. * In <b>custom example 2</b>, the basic text <i>add</i> and <i>remove</i>
  329. * buttons are now replaced by images.
  330. *
  331. * In <b>custom example 5</b>, we have ability to sort the selection list
  332. * (on right side) by :
  333. * <pre>
  334. * - <b>user-end</b>: with <i>Up</i> and <i>Down</i> buttons
  335. * - <b>programming</b>: with the QF element constructor $sort option
  336. * </pre>
  337. *
  338. * @param string $button Button identifier, either 'add', 'remove',
  339. * 'all', 'none', 'toggle',
  340. * 'movetop', 'movebottom'
  341. * 'moveup' or 'movedown'
  342. * @param mixed $attributes (optional) Either a typical HTML attribute string
  343. * or an associative array
  344. *
  345. * @return void
  346. * @throws PEAR_Error $button argument
  347. * is not a string, or not in range
  348. * (add, remove, all, none, toggle,
  349. * movetop, movebottom, moveup, movedown)
  350. * @access public
  351. * @since version 0.4.0 (2005-06-25)
  352. *
  353. * @example examples/qfams_custom_5.php
  354. * Custom example 5: source code
  355. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom5.png
  356. * Custom example 5: screenshot
  357. *
  358. * @example examples/qfams_custom_2.php
  359. * Custom example 2: source code
  360. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom2.png
  361. * Custom example 2: screenshot
  362. *
  363. * @example examples/qfams_custom_1.php
  364. * Custom example 1: source code
  365. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom1.png
  366. * Custom example 1: screenshot
  367. */
  368. function setButtonAttributes($button, $attributes = null)
  369. {
  370. if (!is_string($button)) {
  371. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  372. 'setButtonAttributes is not a string',
  373. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  374. array('level' => 'exception'));
  375. }
  376. switch ($button) {
  377. case 'add':
  378. if (is_null($attributes)) {
  379. $this->_addButtonAttributes
  380. = array('name' => 'add',
  381. 'value' => ' ',
  382. 'type' => 'button');
  383. } else {
  384. $this->_updateAttrArray($this->_addButtonAttributes,
  385. $this->_parseAttributes($attributes));
  386. }
  387. break;
  388. case 'remove':
  389. if (is_null($attributes)) {
  390. $this->_removeButtonAttributes
  391. = array('name' => 'remove',
  392. 'value' => ' ',
  393. 'type' => 'button');
  394. } else {
  395. $this->_updateAttrArray($this->_removeButtonAttributes,
  396. $this->_parseAttributes($attributes));
  397. }
  398. break;
  399. case 'all':
  400. if (is_null($attributes)) {
  401. $this->_allButtonAttributes
  402. = array('name' => 'all',
  403. 'value' => ' Select All ',
  404. 'type' => 'button');
  405. } else {
  406. $this->_updateAttrArray($this->_allButtonAttributes,
  407. $this->_parseAttributes($attributes));
  408. }
  409. break;
  410. case 'none':
  411. if (is_null($attributes)) {
  412. $this->_noneButtonAttributes
  413. = array('name' => 'none',
  414. 'value' => ' Select None ',
  415. 'type' => 'button');
  416. } else {
  417. $this->_updateAttrArray($this->_noneButtonAttributes,
  418. $this->_parseAttributes($attributes));
  419. }
  420. break;
  421. case 'toggle':
  422. if (is_null($attributes)) {
  423. $this->_toggleButtonAttributes
  424. = array('name' => 'toggle',
  425. 'value' => ' Toggle Selection ',
  426. 'type' => 'button');
  427. } else {
  428. $this->_updateAttrArray($this->_toggleButtonAttributes,
  429. $this->_parseAttributes($attributes));
  430. }
  431. break;
  432. case 'moveup':
  433. if (is_null($attributes)) {
  434. $this->_upButtonAttributes
  435. = array('name' => 'up',
  436. 'value' => ' Up ',
  437. 'type' => 'button');
  438. } else {
  439. $this->_updateAttrArray($this->_upButtonAttributes,
  440. $this->_parseAttributes($attributes));
  441. }
  442. break;
  443. case 'movedown':
  444. if (is_null($attributes)) {
  445. $this->_downButtonAttributes
  446. = array('name' => 'down',
  447. 'value' => ' Down ',
  448. 'type' => 'button');
  449. } else {
  450. $this->_updateAttrArray($this->_downButtonAttributes,
  451. $this->_parseAttributes($attributes));
  452. }
  453. break;
  454. case 'movetop':
  455. if (is_null($attributes)) {
  456. $this->_topButtonAttributes
  457. = array('name' => 'top',
  458. 'value' => ' Top ',
  459. 'type' => 'button');
  460. } else {
  461. $this->_updateAttrArray($this->_topButtonAttributes,
  462. $this->_parseAttributes($attributes));
  463. }
  464. break;
  465. case 'movebottom':
  466. if (is_null($attributes)) {
  467. $this->_bottomButtonAttributes
  468. = array('name' => 'bottom',
  469. 'value' => ' Bottom ',
  470. 'type' => 'button');
  471. } else {
  472. $this->_updateAttrArray($this->_bottomButtonAttributes,
  473. $this->_parseAttributes($attributes));
  474. }
  475. break;
  476. default;
  477. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  478. 'setButtonAttributes has unexpected value',
  479. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  480. array('level' => 'error'));
  481. }
  482. }
  483. /**
  484. * Sets element template
  485. *
  486. * @param string $html (optional) The HTML surrounding select boxes and buttons
  487. * @param string $js (optional) if we need to include qfams javascript handler
  488. *
  489. * @access public
  490. * @return string
  491. * @since version 0.4.0 (2005-06-25)
  492. */
  493. function setElementTemplate($html = null, $js = true)
  494. {
  495. $oldTemplate = $this->_elementTemplate;
  496. if (isset($html) && is_string($html)) {
  497. $this->_elementTemplate = $html;
  498. } else {
  499. $this->_elementTemplate = '
  500. {javascript}
  501. <table{class}>
  502. <!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
  503. <!-- BEGIN label_3 --><th>&nbsp;</th><th>{label_3}</th></tr><!-- END label_3 -->
  504. <tr>
  505. <td valign="top">{unselected}</td>
  506. <td align="center">{add}{remove}</td>
  507. <td valign="top">{selected}</td>
  508. </tr>
  509. </table>
  510. ';
  511. }
  512. if ($js == false) {
  513. $this->_elementTemplate = str_replace('{javascript}', '',
  514. $this->_elementTemplate);
  515. }
  516. return $oldTemplate;
  517. }
  518. /**
  519. * Gets default element stylesheet for a single multi-select shape render
  520. *
  521. * In <b>custom example 4</b>, the template defined allows
  522. * a single multi-select checkboxes shape. Useful when javascript is disabled
  523. * (or when browser is not js compliant). In our example, no need to add
  524. * javascript code, but css is mandatory.
  525. *
  526. * @param boolean $raw (optional) html output with style tags or just raw data
  527. *
  528. * @access public
  529. * @return string
  530. * @since version 0.4.0 (2005-06-25)
  531. *
  532. * @example qfams_custom_4.php
  533. * Custom example 4: source code
  534. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom4.png
  535. * Custom example 4: screenshot
  536. */
  537. function getElementCss($raw = true)
  538. {
  539. $id = $this->getAttribute('name');
  540. $css = str_replace('{id}', $id, $this->_elementCSS);
  541. if ($raw !== true) {
  542. $css = '<style type="text/css">' . PHP_EOL
  543. // Modified by Chamilo team, 16-MAR-2010.
  544. //. '<!--' . $css . '// -->' . PHP_EOL
  545. . '/*<![CDATA[*/' . PHP_EOL
  546. . $css . PHP_EOL
  547. . '/*]]>*/' . PHP_EOL
  548. //
  549. . '</style>';
  550. }
  551. return $css;
  552. }
  553. /**
  554. * Returns the HTML generated for the advanced mutliple select component
  555. *
  556. * @access public
  557. * @return string
  558. * @since version 0.4.0 (2005-06-25)
  559. */
  560. function toHtml()
  561. {
  562. if ($this->_flagFrozen) {
  563. return $this->getFrozenHtml();
  564. }
  565. $tabs = $this->_getTabs();
  566. $tab = $this->_getTab();
  567. $selectId = $this->getName();
  568. $selectName = $this->getName() . '[]';
  569. $selectNameFrom = $this->getName() . '-f[]';
  570. $selectNameTo = $this->getName() . '-t[]';
  571. $selected_count = 0;
  572. // placeholder {unselected} existence determines if we will render
  573. if (strpos($this->_elementTemplate, '{unselected}') === false) {
  574. // ... a single multi-select with checkboxes
  575. $this->_jsPostfix = 'editSelection';
  576. $id = $this->getAttribute('name');
  577. $strHtmlSelected = $tab . '<div id="qfams_'.$id.'">' . PHP_EOL;
  578. $unselected_count = count($this->_options);
  579. $checkbox_id_suffix = 0;
  580. foreach ($this->_options as $option) {
  581. $_labelAttributes
  582. = array('style', 'class', 'onmouseover', 'onmouseout');
  583. $labelAttributes = array();
  584. foreach ($_labelAttributes as $attr) {
  585. if (isset($option['attr'][$attr])) {
  586. $labelAttributes[$attr] = $option['attr'][$attr];
  587. unset($option['attr'][$attr]);
  588. }
  589. }
  590. if (is_array($this->_values)
  591. && in_array((string)$option['attr']['value'], $this->_values)) {
  592. // The items is *selected*
  593. $checked = ' checked="checked"';
  594. $selected_count++;
  595. } else {
  596. // The item is *unselected* so we want to put it
  597. $checked = '';
  598. }
  599. $checkbox_id_suffix++;
  600. $strHtmlSelected .= $tab
  601. . '<label'
  602. . $this->_getAttrString($labelAttributes) .'>'
  603. . '<input type="checkbox"'
  604. . ' id="'.$selectId . $checkbox_id_suffix.'"'
  605. . ' name="'.$selectName.'"'
  606. . $checked
  607. . $this->_getAttrString($option['attr'])
  608. . ' />' . $option['text'] . '</label>'
  609. . PHP_EOL;
  610. }
  611. $strHtmlSelected .= $tab . '</div>'. PHP_EOL;
  612. $strHtmlHidden = '';
  613. $strHtmlUnselected = '';
  614. $strHtmlAdd = '';
  615. $strHtmlRemove = '';
  616. // build the select all button with all its attributes
  617. $jsName = $this->_jsPrefix . $this->_jsPostfix;
  618. $attributes = array('onclick' => $jsName .
  619. "('". $selectId ."', 1);");
  620. $this->_allButtonAttributes
  621. = array_merge($this->_allButtonAttributes, $attributes);
  622. $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
  623. $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
  624. // build the select none button with all its attributes
  625. $attributes = array('onclick' => $jsName .
  626. "('". $selectId ."', 0);");
  627. $this->_noneButtonAttributes
  628. = array_merge($this->_noneButtonAttributes, $attributes);
  629. $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
  630. $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
  631. // build the toggle selection button with all its attributes
  632. $attributes = array('onclick' => $jsName .
  633. "('". $selectId ."', 2);");
  634. $this->_toggleButtonAttributes
  635. = array_merge($this->_toggleButtonAttributes,
  636. $attributes);
  637. $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
  638. $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
  639. $strHtmlMoveUp = '';
  640. $strHtmlMoveDown = '';
  641. $strHtmlMoveTop = '';
  642. $strHtmlMoveBottom = '';
  643. // default selection counters
  644. $strHtmlSelectedCount = $selected_count . '/' . $unselected_count;
  645. } else {
  646. // ... or a dual multi-select
  647. $this->_jsPostfix = 'moveSelection';
  648. $jsName = $this->_jsPrefix . $this->_jsPostfix;
  649. // set name of Select From Box
  650. $this->_attributesUnselected
  651. = array('id' => $selectId . '-f',
  652. 'name' => $selectNameFrom,
  653. 'ondblclick' => $jsName .
  654. "('{$selectId}', ".
  655. "this.form.elements['" . $selectNameFrom . "'], " .
  656. "this.form.elements['" . $selectNameTo . "'], " .
  657. "this.form.elements['" . $selectName . "'], " .
  658. "'add', '{$this->_sort}')");
  659. $this->_attributesUnselected
  660. = array_merge($this->_attributes, $this->_attributesUnselected);
  661. $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
  662. // set name of Select To Box
  663. $this->_attributesSelected
  664. = array('id' => $selectId . '-t',
  665. 'name' => $selectNameTo,
  666. 'ondblclick' => $jsName .
  667. "('{$selectId}', " .
  668. "this.form.elements['" . $selectNameFrom . "'], " .
  669. "this.form.elements['" . $selectNameTo . "'], ".
  670. "this.form.elements['" . $selectName . "'], " .
  671. "'remove', '{$this->_sort}')");
  672. $this->_attributesSelected
  673. = array_merge($this->_attributes, $this->_attributesSelected);
  674. $attrSelected = $this->_getAttrString($this->_attributesSelected);
  675. // set name of Select hidden Box
  676. $this->_attributesHidden
  677. = array('name' => $selectName,
  678. 'style' => 'overflow: hidden; visibility: hidden; ' .
  679. 'width: 1px; height: 0;');
  680. $this->_attributesHidden
  681. = array_merge($this->_attributes, $this->_attributesHidden);
  682. $attrHidden = $this->_getAttrString($this->_attributesHidden);
  683. // prepare option tables to be displayed as in POST order
  684. $append = count($this->_values);
  685. if ($append > 0) {
  686. $arrHtmlSelected = array_fill(0, $append, ' ');
  687. } else {
  688. $arrHtmlSelected = array();
  689. }
  690. $options = count($this->_options);
  691. $arrHtmlUnselected = array();
  692. if ($options > 0) {
  693. $arrHtmlHidden = array_fill(0, $options, ' ');
  694. foreach ($this->_options as $option) {
  695. if (is_array($this->_values)
  696. && in_array((string)$option['attr']['value'],
  697. $this->_values)) {
  698. // Get the post order
  699. $key = array_search($option['attr']['value'],
  700. $this->_values);
  701. /** The items is *selected* so we want to put it
  702. in the 'selected' multi-select */
  703. $arrHtmlSelected[$key] = $option;
  704. /** Add it to the 'hidden' multi-select
  705. and set it as 'selected' */
  706. if (isset($option['attr']['disabled'])) {
  707. unset($option['attr']['disabled']);
  708. }
  709. $option['attr']['selected'] = 'selected';
  710. $arrHtmlHidden[$key] = $option;
  711. } else {
  712. /** The item is *unselected* so we want to put it
  713. in the 'unselected' multi-select */
  714. $arrHtmlUnselected[] = $option;
  715. // Add it to the hidden multi-select as 'unselected'
  716. $arrHtmlHidden[$append] = $option;
  717. $append++;
  718. }
  719. }
  720. } else {
  721. $arrHtmlHidden = array();
  722. }
  723. // The 'unselected' multi-select which appears on the left
  724. $unselected_count = count($arrHtmlUnselected);
  725. if ($unselected_count == 0) {
  726. $this->_attributesUnselected['disabled'] = 'disabled';
  727. $this->_attributesUnselected
  728. = array_merge($this->_attributes, $this->_attributesUnselected);
  729. $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
  730. }
  731. $strHtmlUnselected = "<select$attrUnselected>". PHP_EOL;
  732. if ($unselected_count > 0) {
  733. foreach ($arrHtmlUnselected as $data) {
  734. $strHtmlUnselected
  735. .= $tabs . $tab
  736. . '<option' . $this->_getAttrString($data['attr']) . '>'
  737. . $data['text'] . '</option>' . PHP_EOL;
  738. }
  739. } else {
  740. $strHtmlUnselected .= '<option value="">&nbsp;</option>';
  741. }
  742. $strHtmlUnselected .= '</select>';
  743. // The 'selected' multi-select which appears on the right
  744. $selected_count = count($arrHtmlSelected);
  745. if ($selected_count == 0) {
  746. $this->_attributesSelected['disabled'] = 'disabled';
  747. $this->_attributesSelected
  748. = array_merge($this->_attributes, $this->_attributesSelected);
  749. $attrSelected = $this->_getAttrString($this->_attributesSelected);
  750. }
  751. $strHtmlSelected = "<select$attrSelected>". PHP_EOL;
  752. if ($selected_count > 0) {
  753. foreach ($arrHtmlSelected as $data) {
  754. $strHtmlSelected
  755. .= $tabs . $tab
  756. . '<option' . $this->_getAttrString($data['attr']) . '>'
  757. . $data['text'] . '</option>' . PHP_EOL;
  758. }
  759. } else {
  760. $strHtmlSelected .= '<option value="">&nbsp;</option>';
  761. }
  762. $strHtmlSelected .= '</select>';
  763. // The 'hidden' multi-select
  764. $strHtmlHidden = "<select$attrHidden>". PHP_EOL;
  765. if (count($arrHtmlHidden) > 0) {
  766. foreach ($arrHtmlHidden as $data) {
  767. $strHtmlHidden
  768. .= $tabs . $tab
  769. . '<option' . $this->_getAttrString($data['attr']) . '>'
  770. . $data['text'] . '</option>' . PHP_EOL;
  771. }
  772. }
  773. $strHtmlHidden .= '</select>';
  774. // build the remove button with all its attributes
  775. $attributes
  776. = array('onclick' => $jsName .
  777. "('{$selectId}', " .
  778. "this.form.elements['" . $selectNameFrom . "'], " .
  779. "this.form.elements['" . $selectNameTo . "'], " .
  780. "this.form.elements['" . $selectName . "'], " .
  781. "'remove', '{$this->_sort}'); return false;");
  782. $this->_removeButtonAttributes
  783. = array_merge($this->_removeButtonAttributes, $attributes);
  784. $attrStrRemove = $this->_getAttrString($this->_removeButtonAttributes);
  785. $strHtmlRemove = "<input$attrStrRemove />". PHP_EOL;
  786. // build the add button with all its attributes
  787. $attributes
  788. = array('onclick' => $jsName .
  789. "('{$selectId}', " .
  790. "this.form.elements['" . $selectNameFrom . "'], " .
  791. "this.form.elements['" . $selectNameTo . "'], " .
  792. "this.form.elements['" . $selectName . "'], " .
  793. "'add', '{$this->_sort}'); return false;");
  794. $this->_addButtonAttributes
  795. = array_merge($this->_addButtonAttributes, $attributes);
  796. $attrStrAdd = $this->_getAttrString($this->_addButtonAttributes);
  797. $strHtmlAdd = "<input$attrStrAdd />". PHP_EOL;
  798. // build the select all button with all its attributes
  799. $attributes
  800. = array('onclick' => $jsName .
  801. "('{$selectId}', " .
  802. "this.form.elements['" . $selectNameFrom . "'], " .
  803. "this.form.elements['" . $selectNameTo . "'], " .
  804. "this.form.elements['" . $selectName . "'], " .
  805. "'all', '{$this->_sort}'); return false;");
  806. $this->_allButtonAttributes
  807. = array_merge($this->_allButtonAttributes, $attributes);
  808. $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
  809. $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
  810. // build the select none button with all its attributes
  811. $attributes
  812. = array('onclick' => $jsName .
  813. "('{$selectId}', " .
  814. "this.form.elements['" . $selectNameFrom . "'], " .
  815. "this.form.elements['" . $selectNameTo . "'], " .
  816. "this.form.elements['" . $selectName . "'], " .
  817. "'none', '{$this->_sort}'); return false;");
  818. $this->_noneButtonAttributes
  819. = array_merge($this->_noneButtonAttributes, $attributes);
  820. $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
  821. $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
  822. // build the toggle button with all its attributes
  823. $attributes
  824. = array('onclick' => $jsName .
  825. "('{$selectId}', " .
  826. "this.form.elements['" . $selectNameFrom . "'], " .
  827. "this.form.elements['" . $selectNameTo . "'], " .
  828. "this.form.elements['" . $selectName . "'], " .
  829. "'toggle', '{$this->_sort}'); return false;");
  830. $this->_toggleButtonAttributes
  831. = array_merge($this->_toggleButtonAttributes, $attributes);
  832. $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
  833. $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
  834. // build the move up button with all its attributes
  835. $attributes
  836. = array('onclick' => "{$this->_jsPrefix}moveUp" .
  837. "(this.form.elements['" . $selectNameTo . "'], " .
  838. "this.form.elements['" . $selectName . "']); " .
  839. "return false;");
  840. $this->_upButtonAttributes
  841. = array_merge($this->_upButtonAttributes, $attributes);
  842. $attrStrUp = $this->_getAttrString($this->_upButtonAttributes);
  843. $strHtmlMoveUp = "<input$attrStrUp />". PHP_EOL;
  844. // build the move down button with all its attributes
  845. $attributes
  846. = array('onclick' => "{$this->_jsPrefix}moveDown" .
  847. "(this.form.elements['" . $selectNameTo . "'], " .
  848. "this.form.elements['" . $selectName . "']); " .
  849. "return false;");
  850. $this->_downButtonAttributes
  851. = array_merge($this->_downButtonAttributes, $attributes);
  852. $attrStrDown = $this->_getAttrString($this->_downButtonAttributes);
  853. $strHtmlMoveDown = "<input$attrStrDown />". PHP_EOL;
  854. // build the move top button with all its attributes
  855. $attributes
  856. = array('onclick' => "{$this->_jsPrefix}moveTop" .
  857. "(this.form.elements['" . $selectNameTo . "'], " .
  858. "this.form.elements['" . $selectName . "']); " .
  859. "return false;");
  860. $this->_topButtonAttributes
  861. = array_merge($this->_topButtonAttributes, $attributes);
  862. $attrStrTop = $this->_getAttrString($this->_topButtonAttributes);
  863. $strHtmlMoveTop = "<input$attrStrTop />". PHP_EOL;
  864. // build the move bottom button with all its attributes
  865. $attributes
  866. = array('onclick' => "{$this->_jsPrefix}moveBottom" .
  867. "(this.form.elements['" . $selectNameTo . "'], " .
  868. "this.form.elements['" . $selectName . "']); " .
  869. "return false;");
  870. $this->_bottomButtonAttributes
  871. = array_merge($this->_bottomButtonAttributes, $attributes);
  872. $attrStrBottom = $this->_getAttrString($this->_bottomButtonAttributes);
  873. $strHtmlMoveBottom = "<input$attrStrBottom />". PHP_EOL;
  874. // default selection counters
  875. $strHtmlSelectedCount = $selected_count;
  876. }
  877. $strHtmlUnselectedCount = $unselected_count;
  878. $strHtmlSelectedCountId = $selectId .'_selected';
  879. $strHtmlUnselectedCountId = $selectId .'_unselected';
  880. // render all part of the multi select component with the template
  881. $strHtml = $this->_elementTemplate;
  882. // Prepare multiple labels
  883. $labels = $this->getLabel();
  884. if (is_array($labels)) {
  885. array_shift($labels);
  886. }
  887. // render extra labels, if any
  888. if (is_array($labels)) {
  889. foreach ($labels as $key => $text) {
  890. $key = is_int($key)? $key + 2: $key;
  891. $strHtml = str_replace("{label_{$key}}", $text, $strHtml);
  892. $strHtml = str_replace("<!-- BEGIN label_{$key} -->", '', $strHtml);
  893. $strHtml = str_replace("<!-- END label_{$key} -->", '', $strHtml);
  894. }
  895. }
  896. // clean up useless label tags
  897. if (strpos($strHtml, '{label_')) {
  898. $strHtml = preg_replace('/\s*<!-- BEGIN label_(\S+) -->'.
  899. '.*<!-- END label_\1 -->\s*/i', '', $strHtml);
  900. }
  901. $placeHolders = array(
  902. '{stylesheet}', '{javascript}',
  903. '{class}',
  904. '{unselected_count_id}', '{selected_count_id}',
  905. '{unselected_count}', '{selected_count}',
  906. '{unselected}', '{selected}',
  907. '{add}', '{remove}',
  908. '{all}', '{none}', '{toggle}',
  909. '{moveup}', '{movedown}',
  910. '{movetop}', '{movebottom}'
  911. );
  912. $htmlElements = array(
  913. $this->getElementCss(false), $this->getElementJs(false),
  914. $this->_tableAttributes,
  915. $strHtmlUnselectedCountId, $strHtmlSelectedCountId,
  916. $strHtmlUnselectedCount, $strHtmlSelectedCount,
  917. $strHtmlUnselected, $strHtmlSelected . $strHtmlHidden,
  918. $strHtmlAdd, $strHtmlRemove,
  919. $strHtmlAll, $strHtmlNone, $strHtmlToggle,
  920. $strHtmlMoveUp, $strHtmlMoveDown,
  921. $strHtmlMoveTop, $strHtmlMoveBottom
  922. );
  923. $strHtml = str_replace($placeHolders, $htmlElements, $strHtml);
  924. $comment = $this->getComment();
  925. if (!empty($comment)) {
  926. $strHtml = $tabs . '<!-- ' . $comment . " //-->" . PHP_EOL . $strHtml;
  927. }
  928. return $strHtml;
  929. }
  930. /**
  931. * Returns the javascript code generated to handle this element
  932. *
  933. * @param boolean $raw (optional) html output with script tags or just raw data
  934. * @param boolean $min (optional) uses javascript compressed version
  935. *
  936. * @access public
  937. * @return string
  938. * @since version 0.4.0 (2005-06-25)
  939. */
  940. function getElementJs($raw = true, $min = true)
  941. {
  942. $js = '@data_dir@' . DIRECTORY_SEPARATOR
  943. . '@package_name@' . DIRECTORY_SEPARATOR;
  944. $js = api_get_path(LIBRARY_PATH).'javascript'.DIRECTORY_SEPARATOR.'pear'.DIRECTORY_SEPARATOR;
  945. if ($min) {
  946. $js .= 'qfamsHandler-min.js';
  947. } else {
  948. $js .= 'qfamsHandler.js';
  949. }
  950. if (file_exists($js)) {
  951. $js = file_get_contents($js);
  952. } else {
  953. $js = '';
  954. }
  955. if ($raw !== true) {
  956. $js = '<script type="text/javascript">'
  957. . PHP_EOL . '//<![CDATA['
  958. . PHP_EOL . $js
  959. . PHP_EOL . '//]]>'
  960. . PHP_EOL . '</script>'
  961. . PHP_EOL;
  962. }
  963. return $js;
  964. }
  965. /**
  966. * Loads options from different types of data sources
  967. *
  968. * This method overloaded parent method of select element, to allow
  969. * loading options with fancy attributes.
  970. *
  971. * @param mixed &$options Options source currently supports assoc array or DB_result
  972. * @param mixed $param1 (optional) See function detail
  973. * @param mixed $param2 (optional) See function detail
  974. * @param mixed $param3 (optional) See function detail
  975. * @param mixed $param4 (optional) See function detail
  976. *
  977. * @access public
  978. * @since version 1.5.0 (2009-02-15)
  979. * @return PEAR_Error|NULL on error and TRUE on success
  980. * @throws PEAR_Error
  981. * @see loadArray()
  982. */
  983. function load(&$options,
  984. $param1 = null, $param2 = null, $param3 = null, $param4 = null)
  985. {
  986. if (is_array($options)) {
  987. $ret = $this->loadArray($options, $param1);
  988. } else {
  989. $ret = parent::load($options, $param1, $param2, $param3, $param4);
  990. }
  991. return $ret;
  992. }
  993. /**
  994. * Loads the options from an associative array
  995. *
  996. * This method overloaded parent method of select element, to allow to load
  997. * array of options with fancy attributes.
  998. *
  999. * @param array $arr Associative array of options
  1000. * @param mixed $values (optional) Array or comma delimited string of selected values
  1001. *
  1002. * @since version 1.5.0 (2009-02-15)
  1003. * @access public
  1004. * @return PEAR_Error on error and TRUE on success
  1005. * @throws PEAR_Error
  1006. * @see load()
  1007. */
  1008. function loadArray($arr, $values = null)
  1009. {
  1010. if (!is_array($arr)) {
  1011. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  1012. 'loadArray is not a valid array',
  1013. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1014. array('level' => 'exception'));
  1015. }
  1016. if (isset($values)) {
  1017. $this->setSelected($values);
  1018. }
  1019. if (is_array($arr)) {
  1020. foreach ($arr as $key => $val) {
  1021. if (is_array($val)) {
  1022. $this->addOption($val[0], $key, $val[1]);
  1023. } else {
  1024. $this->addOption($val, $key);
  1025. }
  1026. }
  1027. }
  1028. return true;
  1029. }
  1030. /**
  1031. * Sets which items should be persistant
  1032. *
  1033. * Sets which items should have the disabled attribute
  1034. * to keep it persistant
  1035. *
  1036. * @param mixed $optionValues Options (key-values) that should be persistant
  1037. * @param bool $persistant (optional) TRUE if persistant, FALSE otherwise
  1038. *
  1039. * @since version 1.5.0 (2009-02-15)
  1040. * @access public
  1041. * @return PEAR_Error on error and TRUE on success
  1042. * @throws PEAR_Error
  1043. */
  1044. function setPersistantOptions($optionValues, $persistant = true)
  1045. {
  1046. if (!is_bool($persistant)) {
  1047. return PEAR::throwError('Argument 2 of HTML_QuickForm_advmultiselect::' .
  1048. 'setPersistantOptions is not a boolean',
  1049. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1050. array('level' => 'exception'));
  1051. }
  1052. if (is_string($optionValues)) {
  1053. $optionValues = array($optionValues);
  1054. }
  1055. if (!is_array($optionValues)) {
  1056. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  1057. 'setPersistantOptions is not a valid array',
  1058. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1059. array('level' => 'exception'));
  1060. }
  1061. foreach ($this->_options as $k => $v) {
  1062. if (in_array($v['attr']['value'], $optionValues)) {
  1063. if ($persistant) {
  1064. $this->_options[$k]['attr']['disabled'] = 'disabled';
  1065. } else {
  1066. unset($this->_options[$k]['attr']['disabled']);
  1067. }
  1068. }
  1069. }
  1070. return true;
  1071. }
  1072. /**
  1073. * Returns list of persistant options
  1074. *
  1075. * Returns list of persistant options (key-values) that could not
  1076. * be selected or unselected.
  1077. *
  1078. * @since version 1.5.0 (2009-02-15)
  1079. * @access public
  1080. * @return array
  1081. */
  1082. function getPersistantOptions()
  1083. {
  1084. $options = array();
  1085. foreach ($this->_options as $k => $v) {
  1086. if (isset($v['attr']['disabled'])) {
  1087. $options[] = $this->_options[$k]['attr']['value'];
  1088. }
  1089. }
  1090. return $options;
  1091. }
  1092. }
  1093. // @todo this fires an PHP notice, needed in order to parse the advmultiselect test it in main/group/group_edit.php
  1094. if (class_exists('HTML_QuickForm')) {
  1095. @HTML_QuickForm::registerElementType('advmultiselect','HTML/QuickForm/advmultiselect.php', 'HTML_QuickForm_advmultiselect');
  1096. }