advmultiselect.php 38 KB

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