advmultiselect.php 45 KB

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