advmultiselect.php 46 KB

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