PageRenderTime 81ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/e107_handlers/admin_ui.php

https://github.com/CasperGemini/e107
PHP | 5872 lines | 3529 code | 715 blank | 1628 comment | 486 complexity | 34ed6a3c5068ad414d571024b6ab9174 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * e107 website system
  4. *
  5. * Copyright (C) 2008-2012 e107 Inc (e107.org)
  6. * Released under the terms and conditions of the
  7. * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
  8. *
  9. * Administration User Interface logic
  10. *
  11. * $URL$
  12. * $Id$
  13. */
  14. /**
  15. * @package e107
  16. * @subpackage e107_handlers
  17. * @version $Id$
  18. *
  19. * Administration User Interface logic
  20. */
  21. /**
  22. * @todo core request handler (non-admin), core response
  23. */
  24. if (!defined('e107_INIT')){ exit; }
  25. class e_admin_request
  26. {
  27. /**
  28. * Current GET request array
  29. * @var array
  30. */
  31. protected $_request_qry;
  32. /**
  33. * Current POST array
  34. * @var array
  35. */
  36. protected $_posted_qry;
  37. /**
  38. * Current Mode
  39. * @var string
  40. */
  41. protected $_mode = '';
  42. /**
  43. * Default Mode
  44. * @var string
  45. */
  46. protected $_default_mode = 'main';
  47. /**
  48. * Key name for mode search
  49. * @var string
  50. */
  51. protected $_mode_key = 'mode';
  52. /**
  53. * Current action
  54. * @var string
  55. */
  56. protected $_action = '';
  57. /**
  58. * Default Action
  59. * @var string
  60. */
  61. protected $_default_action = 'index';
  62. /**
  63. * Key name for action search
  64. * @var string
  65. */
  66. protected $_action_key = 'action';
  67. /**
  68. * Current ID
  69. * @var integer
  70. */
  71. protected $_id = 0;
  72. /**
  73. * Key name for ID search
  74. * @var string
  75. */
  76. protected $_id_key = 'id';
  77. /**
  78. * Constructor
  79. *
  80. * @param string|array $qry [optional]
  81. * @return none
  82. */
  83. public function __construct($request_string = null, $parse = true)
  84. {
  85. if(null === $request_string)
  86. {
  87. $request_string = str_replace('&amp;', '&', e_QUERY);
  88. }
  89. if($parse)
  90. {
  91. $this->parseRequest($request_string);
  92. }
  93. }
  94. /**
  95. * Parse request data
  96. * @param string|array $request_data
  97. * @return e_admin_request
  98. */
  99. protected function parseRequest($request_data)
  100. {
  101. if(is_string($request_data))
  102. {
  103. parse_str($request_data, $request_data);
  104. }
  105. $this->_request_qry = (array) $request_data;
  106. // Set current mode
  107. if(isset($this->_request_qry[$this->_mode_key]))
  108. {
  109. $this->_mode = preg_replace('/[^\w]/', '', $this->_request_qry[$this->_mode_key]);
  110. }
  111. // Set current action
  112. if(isset($this->_request_qry[$this->_action_key]))
  113. {
  114. $this->_action = preg_replace('/[^\w]/', '', $this->_request_qry[$this->_action_key]);
  115. }
  116. // Set current id
  117. if(isset($this->_request_qry[$this->_id_key]))
  118. {
  119. $this->_id = preg_replace('/[^\w-:.]/', '', $this->_request_qry[$this->_id_key]);
  120. }
  121. $this->_posted_qry =& $_POST; //raw?
  122. return $this;
  123. }
  124. /**
  125. * Retrieve variable from GET scope
  126. * If $key is null, all GET data will be returned
  127. *
  128. * @param string $key [optional]
  129. * @param mixed $default [optional]
  130. * @return mixed
  131. */
  132. public function getQuery($key = null, $default = null)
  133. {
  134. if(null === $key)
  135. {
  136. return $this->_request_qry;
  137. }
  138. return (isset($this->_request_qry[$key]) ? $this->_request_qry[$key] : $default);
  139. }
  140. /**
  141. * Set/Unset GET variable
  142. * If $key is array, $value is not used.
  143. * If $value is null, (string) $key is unset
  144. *
  145. * @param string|array $key
  146. * @param mixed $value [optional]
  147. * @return e_admin_request
  148. */
  149. public function setQuery($key, $value = null)
  150. {
  151. if(is_array($key))
  152. {
  153. foreach ($key as $k=>$v)
  154. {
  155. $this->setQuery($k, $v);
  156. }
  157. return $this;
  158. }
  159. if(null === $value)
  160. {
  161. unset($this->_request_qry[$key]);
  162. unset($_GET[$key]);
  163. return $this;
  164. }
  165. $this->_request_qry[$key] = $value;
  166. $_GET[$key] = $value;
  167. return $this;
  168. }
  169. /**
  170. * Retrieve variable from POST scope
  171. * If $key is null, all POST data will be returned
  172. *
  173. * @param string $key [optional]
  174. * @param mixed $default [optional]
  175. * @return mixed
  176. */
  177. public function getPosted($key = null, $default = null)
  178. {
  179. if(null === $key)
  180. {
  181. return $this->_posted_qry;
  182. }
  183. return (isset($this->_posted_qry[$key]) ? $this->_posted_qry[$key] : $default);
  184. }
  185. /**
  186. * Set/Unset POST variable
  187. * If $key is array, $value is not used.
  188. * If $value is null, (string) $key is unset
  189. *
  190. * @param object $key
  191. * @param object $value [optional]
  192. * @return e_admin_request
  193. */
  194. public function setPosted($key, $value = null)
  195. {
  196. if(is_array($key))
  197. {
  198. if(empty($key))
  199. {
  200. $this->_posted_qry = array(); //POST reset
  201. return $this;
  202. }
  203. foreach ($key as $k=>$v)
  204. {
  205. $this->setPosted($k, $v);
  206. }
  207. return $this;
  208. }
  209. if(null === $value)
  210. {
  211. unset($this->_posted_qry[$key]);
  212. return $this;
  213. }
  214. $tp = e107::getParser();
  215. $this->_posted_qry[$tp->post_toForm($key)] = $tp->post_toForm($value);
  216. return $this;
  217. }
  218. /**
  219. * Get current mode
  220. * @return string
  221. */
  222. public function getMode()
  223. {
  224. if(!$this->_mode) return $this->getDefaultMode();
  225. return $this->_mode;
  226. }
  227. /**
  228. * Get default mode
  229. * @return string
  230. */
  231. public function getDefaultMode()
  232. {
  233. return $this->_default_mode;
  234. }
  235. /**
  236. * Get current mode name
  237. *
  238. * @return string
  239. */
  240. public function getModeName()
  241. {
  242. return strtolower(str_replace('-', '_', $this->getMode()));
  243. }
  244. /**
  245. * Reset current mode
  246. * @param string $mode
  247. * @return e_admin_request
  248. */
  249. public function setMode($mode)
  250. {
  251. $this->_mode = preg_replace('/[^\w]/', '', $mode);
  252. $this->setQuery($this->_mode_key, $this->_mode);
  253. return $this;
  254. }
  255. /**
  256. * Set default mode
  257. * @param string $mode
  258. * @return e_admin_request
  259. */
  260. public function setDefaultMode($mode)
  261. {
  262. if($mode) $this->_default_mode = $mode;
  263. return $this;
  264. }
  265. /**
  266. * Set mode key name
  267. * @param string $key
  268. * @return e_admin_request
  269. */
  270. public function setModeKey($key)
  271. {
  272. $this->_mode_key = $key;
  273. return $this;
  274. }
  275. /**
  276. * Get current action
  277. * @return TBD
  278. */
  279. public function getAction()
  280. {
  281. if(!$this->_action) return $this->getDefaultAction();
  282. return $this->_action;
  283. }
  284. /**
  285. * Get default action
  286. * @return string
  287. */
  288. public function getDefaultAction()
  289. {
  290. return $this->_default_action;
  291. }
  292. /**
  293. * Get current action name
  294. * @return string camelized action
  295. */
  296. public function getActionName()
  297. {
  298. return $this->camelize($this->getAction());
  299. }
  300. /**
  301. * Reset current action
  302. *
  303. * @param string $action
  304. * @return e_admin_request
  305. */
  306. public function setAction($action)
  307. {
  308. $this->_action = preg_replace('/[^\w]/', '', $action);
  309. $this->setQuery($this->_action_key, $this->_action);
  310. return $this;
  311. }
  312. /**
  313. * Set default action
  314. *
  315. * @param string $action
  316. * @return e_admin_request
  317. */
  318. public function setDefaultAction($action)
  319. {
  320. if($action) $this->_default_action = $action;
  321. return $this;
  322. }
  323. /**
  324. * Set action key name
  325. * @param string $key
  326. * @return e_admin_request
  327. */
  328. public function setActionKey($key)
  329. {
  330. $this->_action_key = $key;
  331. return $this;
  332. }
  333. /**
  334. * Get current ID
  335. * @return integer
  336. */
  337. public function getId()
  338. {
  339. return $this->_id;
  340. }
  341. /**
  342. * Reset current ID
  343. * @param string $id
  344. * @return e_admin_request
  345. */
  346. public function setId($id)
  347. {
  348. $id = intval($id);
  349. $this->_id = $id;
  350. $this->setQuery($this->_id_key, $id);
  351. return $this;
  352. }
  353. /**
  354. * Set id key name
  355. * @param string $key
  356. * @return e_admin_request
  357. */
  358. public function setIdKey($key)
  359. {
  360. $this->_id_key = $key;
  361. return $this;
  362. }
  363. /**
  364. * Build query string from current request array
  365. * NOTE: changing url separator to &amp; ($encode==true) (thus URL XHTML compliance) works in PHP 5.1.2+ environment
  366. *
  367. * @param string|array $merge_with [optional] override request values
  368. * @param boolean $encode if true &amp; separator will be used, all values will be http encoded, default true
  369. * @param string|array $exclude_from_query numeric array/comma separated list of vars to be excluded from current query, true - don't use current query at all
  370. * @param boolean $keepSpecial don't exclude special vars as 'mode' and 'action'
  371. * @return string url encoded query string
  372. */
  373. public function buildQueryString($merge_with = array(), $encode = true, $exclude_from_query = '', $keepSpecial = true)
  374. {
  375. $ret = $this->getQuery();
  376. //special case - exclude all current
  377. if(true === $exclude_from_query)
  378. {
  379. $exclude_from_query = array_keys($ret);
  380. }
  381. // to array
  382. if(is_string($exclude_from_query))
  383. {
  384. $exclude_from_query = array_map('trim', explode(',', $exclude_from_query));
  385. }
  386. if($exclude_from_query)
  387. {
  388. foreach ($exclude_from_query as $var)
  389. {
  390. if($keepSpecial && $var != $this->_action_key && $var != $this->_mode_key) unset($ret[$var]);
  391. }
  392. }
  393. if(is_string($merge_with))
  394. {
  395. parse_str($merge_with, $merge_with);
  396. }
  397. $ret = array_merge($ret, (array) $merge_with);
  398. $separator = '&';
  399. if($encode)
  400. {
  401. $separator = '&amp;';
  402. //$ret = array_map('rawurlencode', $ret);
  403. }
  404. $ret = http_build_query($ret, 'numeric_', $separator);
  405. if(!$encode)
  406. {
  407. return rawurldecode($ret);
  408. }
  409. return $ret;
  410. }
  411. /**
  412. * Convert string to CamelCase
  413. *
  414. * @param string $str
  415. * @return string
  416. */
  417. public function camelize($str)
  418. {
  419. return implode('', array_map('ucfirst', explode('-', str_replace('_', '-', $str))));
  420. }
  421. }
  422. /**
  423. * TODO - front response parent, should do all the header.php work
  424. */
  425. class e_admin_response
  426. {
  427. /**
  428. * Body segments
  429. *
  430. * @var array
  431. */
  432. protected $_body = array();
  433. /**
  434. * Title segments
  435. *
  436. * @var unknown_type
  437. */
  438. protected $_title = array();
  439. /**
  440. * e107 meta title
  441. *
  442. * @var array
  443. */
  444. protected $_e_PAGETITLE = array();
  445. /**
  446. * e107 meta description
  447. *
  448. * @var array
  449. */
  450. protected $_META_DESCRIPTION = array();
  451. /**
  452. * e107 meta keywords
  453. *
  454. * @var array
  455. */
  456. protected $_META_KEYWORDS = array();
  457. /**
  458. * Render mods
  459. *
  460. * @var array
  461. */
  462. protected $_render_mod = array();
  463. /**
  464. * Meta title segment description
  465. *
  466. * @var string
  467. */
  468. protected $_meta_title_separator = ' - ';
  469. /**
  470. * Title segment separator
  471. *
  472. * @var string
  473. */
  474. protected $_title_separator = ' &raquo; ';
  475. /**
  476. * Constructor
  477. *
  478. */
  479. public function __construct()
  480. {
  481. $this->_render_mod['default'] = 'admin_page';
  482. }
  483. /**
  484. * Set body segments for a namespace
  485. *
  486. * @param string $content
  487. * @param string $namespace segment namesapce
  488. * @return e_admin_response
  489. */
  490. function setBody($content, $namespace = 'default')
  491. {
  492. $this->_body[$namespace] = $content;
  493. return $this;
  494. }
  495. /**
  496. * Append body segment to a namespace
  497. *
  498. * @param string $content
  499. * @param string $namespace segment namesapce
  500. * @return e_admin_response
  501. */
  502. function appendBody($content, $namespace = 'default')
  503. {
  504. if(!isset($this->_body[$namespace]))
  505. {
  506. $this->_body[$namespace] = array();
  507. }
  508. $this->_body[$namespace][] = $content;
  509. return $this;
  510. }
  511. /**
  512. * Prepend body segment to a namespace
  513. *
  514. * @param string $content
  515. * @param string $namespace segment namespace
  516. * @return e_admin_response
  517. */
  518. function prependBody($content, $namespace = 'default')
  519. {
  520. if(!isset($this->_body[$namespace]))
  521. {
  522. $this->_body[$namespace] = array();
  523. }
  524. $this->_body[$namespace] = array_merge(array($content), $this->_body[$namespace]);
  525. return $this;
  526. }
  527. /**
  528. * Get body segments from a namespace
  529. *
  530. * @param string $namespace segment namesapce
  531. * @param boolean $reset reset segment namespace
  532. * @param string|boolean $glue if false return array, else return string
  533. * @return string|array
  534. */
  535. function getBody($namespace = 'default', $reset = false, $glue = '')
  536. {
  537. $content = vartrue($this->_body[$namespace], array());
  538. if($reset)
  539. {
  540. $this->_body[$namespace] = array();
  541. }
  542. if(is_bool($glue))
  543. {
  544. return ($glue ? $content : implode('', $content));
  545. }
  546. return implode($glue, $content);
  547. }
  548. /**
  549. * Set title segments for a namespace
  550. *
  551. * @param string $title
  552. * @param string $namespace
  553. * @return e_admin_response
  554. */
  555. function setTitle($title, $namespace = 'default')
  556. {
  557. $this->_title[$namespace] = array($title);
  558. return $this;
  559. }
  560. /**
  561. * Append title segment to a namespace
  562. *
  563. * @param string $title
  564. * @param string $namespace segment namesapce
  565. * @return e_admin_response
  566. */
  567. function appendTitle($title, $namespace = 'default')
  568. {
  569. if(empty($title))
  570. {
  571. return $this;
  572. }
  573. if(!isset($this->_title[$namespace]))
  574. {
  575. $this->_title[$namespace] = array();
  576. }
  577. $this->_title[$namespace][] = $title;
  578. return $this;
  579. }
  580. /**
  581. * Prepend title segment to a namespace
  582. *
  583. * @param string $title
  584. * @param string $namespace segment namespace
  585. * @return e_admin_response
  586. */
  587. function prependTitle($title, $namespace = 'default')
  588. {
  589. if(empty($title))
  590. {
  591. return $this;
  592. }
  593. if(!isset($this->_title[$namespace]))
  594. {
  595. $this->_title[$namespace] = array();
  596. }
  597. $this->_title[$namespace] = array_merge(array($title), $this->_title[$namespace]);
  598. return $this;
  599. }
  600. /**
  601. * Get title segments from namespace
  602. *
  603. * @param string $namespace
  604. * @param boolean $reset
  605. * @param boolean|string $glue
  606. * @return unknown
  607. */
  608. function getTitle($namespace = 'default', $reset = false, $glue = ' ')
  609. {
  610. $content = array();
  611. if(isset($this->_title[$namespace]) && is_array($this->_title[$namespace]))
  612. {
  613. $content = $this->_title[$namespace];
  614. }
  615. if($reset)
  616. {
  617. unset($this->_title[$namespace]);
  618. }
  619. if(is_bool($glue) || empty($glue))
  620. {
  621. return ($glue ? $content : implode($this->_title_separator, $content));
  622. }
  623. $glue = deftrue('SEP',' - '); // Defined by admin theme. // admin-ui used only by bootstrap.
  624. return implode($glue, $content);
  625. // return $head. implode($glue, $content).$foot;
  626. }
  627. /**
  628. * Set render mode for a namespace
  629. *
  630. * @param string $render_mod
  631. * @param string $namespace
  632. * @return e_admin_response
  633. */
  634. function setRenderMod($render_mod, $namespace = 'default')
  635. {
  636. $this->_render_mod[$namespace] = $render_mod;
  637. return $this;
  638. }
  639. /**
  640. * Set render mode for namespace
  641. *
  642. * @param string $namespace
  643. * @return string
  644. */
  645. function getRenderMod($namespace = 'default')
  646. {
  647. return varset($this->_render_mod[$namespace], null);
  648. }
  649. /**
  650. * Add meta title, description and keywords segments
  651. *
  652. * @param string $meta property name
  653. * @param string $content meta content
  654. * @return e_admin_response
  655. */
  656. function addMetaData($meta, $content)
  657. {
  658. $tp = e107::getParser();
  659. $meta = '_' . $meta;
  660. if(isset($this->{$meta}) && !empty($content))
  661. {
  662. $this->{$meta}[] = strip_tags($content);
  663. }
  664. return $this;
  665. }
  666. /**
  667. * Add meta title segment
  668. *
  669. * @param string $title
  670. * @return e_admin_response
  671. */
  672. function addMetaTitle($title)
  673. {
  674. $this->addMetaData('e_PAGETITLE', $title);
  675. return $this;
  676. }
  677. /**
  678. * Add meta description segment
  679. *
  680. * @param string $description
  681. * @return e_admin_response
  682. */
  683. function addMetaDescription($description)
  684. {
  685. $this->addMetaData('META_DESCRIPTION', $description);
  686. return $this;
  687. }
  688. /**
  689. * Add meta keywords segment
  690. *
  691. * @param string $keyword
  692. * @return e_admin_response
  693. */
  694. function addMetaKeywords($keyword)
  695. {
  696. $this->addMetaData('META_KEYWORDS', $keyword);
  697. return $this;
  698. }
  699. /**
  700. * Send e107 meta-data
  701. *
  702. * @return e_admin_response
  703. */
  704. function sendMeta()
  705. {
  706. //HEADERF already included or meta content already sent
  707. if(e_AJAX_REQUEST || defined('HEADER_INIT') || defined('e_PAGETITLE'))
  708. return $this;
  709. if(!defined('e_PAGETITLE') && !empty($this->_e_PAGETITLE))
  710. {
  711. define('e_PAGETITLE', implode($this->_meta_title_separator, $this->_e_PAGETITLE));
  712. }
  713. if(!defined('META_DESCRIPTION') && !empty($this->_META_DESCRIPTION))
  714. {
  715. define('META_DESCRIPTION', implode(' ', $this->_META_DESCRIPTION));
  716. }
  717. if(!defined('META_KEYWORDS') && !empty($this->_META_KEYWORDS))
  718. {
  719. define('META_KEYWORDS', implode(', ', $this->_META_KEYWORDS));
  720. }
  721. return $this;
  722. }
  723. /**
  724. * Add content segment to the header namespace
  725. *
  726. * @param string $content
  727. * @return e_admin_response
  728. */
  729. function addHeaderContent($content)
  730. {
  731. $this->appendBody($content, 'header_content');
  732. return $this;
  733. }
  734. /**
  735. * Get page header namespace content segments
  736. *
  737. * @param boolean $reset
  738. * @param boolean $glue
  739. * @return string
  740. */
  741. function getHeaderContent($reset = true, $glue = "\n\n")
  742. {
  743. return $this->getBody('header_content', $reset, $glue);
  744. }
  745. /**
  746. * Switch to iframe mod
  747. * FIXME - implement e_IFRAME to frontend - header_default.php
  748. *
  749. * @return e_admin_response
  750. */
  751. function setIframeMod()
  752. {
  753. global $HEADER, $FOOTER, $CUSTOMHEADER, $CUSTOMFOOTER;
  754. $HEADER = $FOOTER = '';
  755. $CUSTOMHEADER = $CUSTOMFOOTER = array();
  756. //TODO generic $_GET to activate for any page of admin.
  757. // New
  758. if(!defined('e_IFRAME'))
  759. {
  760. define('e_IFRAME', true);
  761. }
  762. return $this;
  763. }
  764. /**
  765. * Send Response Output
  766. *
  767. * @param string $name segment
  768. * @param array $options valid keys are: messages|render|meta|return|raw|ajax
  769. * @return mixed
  770. */
  771. function send($name = 'default', $options = array())
  772. {
  773. if(is_string($options))
  774. {
  775. parse_str($options, $options);
  776. }
  777. // Merge with all available default options
  778. $options = array_merge(array(
  779. 'messages' => true,
  780. 'render' => true,
  781. 'meta' => false,
  782. 'return' => false,
  783. 'raw' => false,
  784. 'ajax' => false
  785. ), $options);
  786. $content = $this->getBody($name, true);
  787. $title = $this->getTitle($name, true);
  788. $return = $options['return'];
  789. if($options['ajax'] || e_AJAX_REQUEST)
  790. {
  791. $type = $options['ajax'] && is_string($options['ajax']) ? $options['ajax'] : '';
  792. $this->getJsHelper()->sendResponse($type);
  793. }
  794. if($options['messages'])
  795. {
  796. $content = e107::getMessage()->render().$content;
  797. }
  798. if($options['meta'])
  799. {
  800. $this->sendMeta();
  801. }
  802. // raw output expected - force return array
  803. if($options['raw'])
  804. {
  805. return array($title, $content, $this->getRenderMod($name));
  806. }
  807. //render disabled by the controller
  808. if(!$this->getRenderMod($name))
  809. {
  810. $options['render'] = false;
  811. }
  812. if($options['render'])
  813. {
  814. return e107::getRender()->tablerender($title, $content, $this->getRenderMod($name), $return);
  815. }
  816. if($return)
  817. {
  818. return $content;
  819. }
  820. print($content);
  821. return '';
  822. }
  823. /**
  824. * Get JS Helper instance
  825. *
  826. * @return e_jshelper
  827. */
  828. public function getJsHelper()
  829. {
  830. return e107::getSingleton('e_jshelper', true, 'admin_response');
  831. }
  832. }
  833. /**
  834. * TODO - request related code should be moved to core
  835. * request handler
  836. */
  837. class e_admin_dispatcher
  838. {
  839. /**
  840. * @var e_admin_request
  841. */
  842. protected $_request = null;
  843. /**
  844. * @var e_admin_response
  845. */
  846. protected $_response = null;
  847. /**
  848. * @var e_admin_controller
  849. */
  850. protected $_current_controller = null;
  851. /**
  852. * Required (set by child class).
  853. * Controller map array in format
  854. * 'MODE' => array('controller' =>'CONTROLLER_CLASS_NAME'[, 'path' => 'CONTROLLER SCRIPT PATH', 'ui' => extend of 'comments_admin_form_ui', 'uipath' => 'path/to/ui/']);
  855. *
  856. * @var array
  857. */
  858. protected $modes = array();
  859. /**
  860. * Optional - access restrictions per action
  861. * Access array in format (similar to adminMenu)
  862. * 'MODE/ACTION' => e_UC_* (userclass constant, or custom userclass ID if dynamically set)
  863. *
  864. * @var array
  865. */
  866. protected $access = array();
  867. /**
  868. * Optional - generic entry point access restriction (via getperms())
  869. * Value of this for plugins would be always 'P'.
  870. * More detailed access control is granted with $access and $modes[MODE]['perm'] or $modes[MODE]['userclass'] settings
  871. *
  872. * @var string
  873. */
  874. protected $perm;
  875. /**
  876. * @var string
  877. */
  878. protected $defaultMode = '';
  879. /**
  880. * @var string
  881. */
  882. protected $defaultAction = '';
  883. /**
  884. * Optional - map 'mode/action' pair to 'modeAlias/actionAlias'
  885. * @var string
  886. */
  887. protected $adminMenuAliases = array();
  888. /**
  889. * Optional (set by child class).
  890. * Required for admin menu render
  891. * Format: 'mode/action' => array('caption' => 'Link title'[, 'perm' => '0', 'url' => '{e_PLUGIN}plugname/admin_config.php'], ...);
  892. * Note that 'perm' and 'userclass' restrictions are inherited from the $modes, $access and $perm, so you don't have to set that vars if
  893. * you don't need any additional 'visual' control.
  894. * All valid key-value pair (see e107::getNav()->admin function) are accepted.
  895. * @var array
  896. */
  897. protected $adminMenu = array();
  898. /**
  899. * Optional (set by child class).
  900. * Page titles for pages not in adminMenu (e.g. main/edit)
  901. * Format array(mod/action => Page Title)
  902. * @var string
  903. */
  904. protected $pageTitles = array(
  905. 'main/edit' => LAN_MANAGE,
  906. );
  907. /**
  908. * Optional (set by child class).
  909. * @var string
  910. */
  911. protected $menuTitle = 'Menu';
  912. /**
  913. * @var string
  914. */
  915. protected $pluginTitle = '';
  916. /**
  917. * Constructor
  918. *
  919. * @param string|array|e_admin_request $request [optional]
  920. * @param e_admin_response $response
  921. */
  922. public function __construct($auto_observe = true, $request = null, $response = null)
  923. {
  924. // we let know some admin routines we are in UI mod - related with some legacy checks and fixes
  925. if(!defined('e_ADMIN_UI'))
  926. {
  927. define('e_ADMIN_UI', true);
  928. }
  929. require_once(e_ADMIN.'boot.php');
  930. if(null === $request || !is_object($request))
  931. {
  932. $request = new e_admin_request($request);
  933. }
  934. if(null === $response)
  935. {
  936. $response = new e_admin_response();
  937. }
  938. $this->setRequest($request)->setResponse($response)->init();
  939. if(!$this->defaultMode || !$this->defaultAction)
  940. {
  941. $this->setDefaults();
  942. }
  943. $request->setDefaultMode($this->defaultMode)->setDefaultAction($this->defaultAction);
  944. // register itself
  945. e107::setRegistry('admin/ui/dispatcher', $this);
  946. // permissions and restrictions
  947. $this->checkAccess();
  948. if($auto_observe)
  949. {
  950. $this->runObservers(true);
  951. }
  952. }
  953. /**
  954. * User defined constructor - called before _initController() method
  955. * @return e_admin_dispatcher
  956. */
  957. public function init()
  958. {
  959. }
  960. public function checkAccess()
  961. {
  962. $request = $this->getRequest();
  963. $currentMode = $request->getMode();
  964. // access based on mode setting - general controller access
  965. if(!$this->checkModeAccess($currentMode))
  966. {
  967. $request->setAction('e403');
  968. e107::getMessage()->addError('You don\'t have permissions to view this page.')
  969. ->addDebug('Mode access restriction triggered.');
  970. return false;
  971. }
  972. // access based on $access settings - access per action
  973. $currentAction = $request->getAction();
  974. $route = $currentMode.'/'.$currentAction;
  975. if(!$this->checkRouteAccess($route))
  976. {
  977. $request->setAction('e403');
  978. e107::getMessage()->addError('You don\'t have permissions to view this page.')
  979. ->addDebug('Route access restriction triggered.');
  980. return false;
  981. }
  982. return true;
  983. }
  984. public function checkModeAccess($mode)
  985. {
  986. // mode userclass (former check_class())
  987. if(isset($this->modes[$mode]['userclass']) && !e107::getUser()->checkClass($this->modes[$mode]['userclass'], false))
  988. {
  989. return false;
  990. }
  991. // mode admin permission (former getperms())
  992. if(isset($this->modes[$mode]['perm']) && !e107::getUser()->checkAdminPerms($this->modes[$mode]['perm']))
  993. {
  994. return false;
  995. }
  996. // generic dispatcher admin permission (former getperms())
  997. if(null !== $this->perm && !e107::getUser()->checkAdminPerms($this->perm))
  998. {
  999. return false;
  1000. }
  1001. return true;
  1002. }
  1003. public function checkRouteAccess($route)
  1004. {
  1005. if(isset($this->access[$route]) && !e107::getUser()->checkClass($this->access[$route], false))
  1006. {
  1007. return false;
  1008. }
  1009. return true;
  1010. }
  1011. /**
  1012. * Retrieve missing default action/mode
  1013. * @return e_admin_dispatcher
  1014. */
  1015. public function setDefaults()
  1016. {
  1017. // try Admin menu first
  1018. if($this->adminMenu)
  1019. {
  1020. reset($this->adminMenu);
  1021. list($mode, $action) = explode('/', key($this->adminMenu), 3);
  1022. }
  1023. else
  1024. {
  1025. reset($this->modes);
  1026. $mode = key($this->modes);
  1027. $action = $this->modes[$mode]['index'];
  1028. }
  1029. if(!$this->defaultMode) $this->defaultMode = $mode;
  1030. if(!$this->defaultAction) $this->defaultAction = $action;
  1031. return $this;
  1032. }
  1033. /**
  1034. * Get admin menu array
  1035. * @return array
  1036. */
  1037. public function getMenuData()
  1038. {
  1039. return $this->adminMenu;
  1040. }
  1041. /**
  1042. * Get admin menu array
  1043. * @return array
  1044. */
  1045. public function getPageTitles()
  1046. {
  1047. return $this->pageTitles;
  1048. }
  1049. /**
  1050. * Get admin menu array
  1051. * @return array
  1052. */
  1053. public function getMenuAliases()
  1054. {
  1055. return $this->adminMenuAliases;
  1056. }
  1057. /**
  1058. * Get request object
  1059. * @return e_admin_request
  1060. */
  1061. public function getRequest()
  1062. {
  1063. return $this->_request;
  1064. }
  1065. /**
  1066. * Set request object
  1067. * @param e_admin_request $request
  1068. * @return e_admin_dispatcher
  1069. */
  1070. public function setRequest($request)
  1071. {
  1072. $this->_request = $request;
  1073. return $this;
  1074. }
  1075. /**
  1076. * Get response object
  1077. * @return e_admin_response
  1078. */
  1079. public function getResponse()
  1080. {
  1081. return $this->_response;
  1082. }
  1083. /**
  1084. * Set response object
  1085. * @param e_admin_response $response
  1086. * @return e_admin_dispatcher
  1087. */
  1088. public function setResponse($response)
  1089. {
  1090. $this->_response = $response;
  1091. return $this;
  1092. }
  1093. /**
  1094. * Dispatch & render all
  1095. *
  1096. * @param boolean $run_header see runObservers()
  1097. * @param boolean $return see runPage()
  1098. * @return string|array current admin page body
  1099. */
  1100. public function run($run_header = true, $return = 'render')
  1101. {
  1102. return $this->runObservers()->runPage($return);
  1103. }
  1104. /**
  1105. * Run observers/headers only, should be called before header.php call
  1106. *
  1107. * @return e_admin_dispatcher
  1108. */
  1109. public function runObservers($run_header = true)
  1110. {
  1111. //search for $actionName.'Observer' method. Additional $actionName.$triggerName.'Trigger' methods will be called as well
  1112. $this->getController()->dispatchObserver();
  1113. //search for $actionName.'Header' method, js manager should be used inside for sending JS to the page,
  1114. // meta information should be created there as well
  1115. if($run_header)
  1116. {
  1117. $this->getController()->dispatchHeader();
  1118. }
  1119. return $this;
  1120. }
  1121. /**
  1122. * Run page action.
  1123. * If return type is array, it should contain allowed response options (see e_admin_response::send())
  1124. * Available return type string values:
  1125. * - render_return: return rendered content ( see e107::getRender()->tablerender()), add system messages, send meta information
  1126. * - render: outputs rendered content ( see e107::getRender()->tablerender()), add system messages
  1127. * - response: return response object
  1128. * - raw: return array(title, content, render mode)
  1129. * - ajax: force ajax output (and exit)
  1130. *
  1131. * @param string|array $return_type expected string values: render|render_out|response|raw|ajax[_text|_json|_xml]
  1132. * @return mixed
  1133. */
  1134. public function runPage($return_type = 'render')
  1135. {
  1136. $response = $this->getController()->dispatchPage();
  1137. if(is_array($return_type))
  1138. {
  1139. return $response->send('default', $return_type);
  1140. }
  1141. switch($return_type)
  1142. {
  1143. case 'render_return':
  1144. $options = array(
  1145. 'messages' => true,
  1146. 'render' => true,
  1147. 'meta' => true,
  1148. 'return' => true,
  1149. 'raw' => false
  1150. );
  1151. break;
  1152. case 'raw':
  1153. $options = array(
  1154. 'messages' => false,
  1155. 'render' => false,
  1156. 'meta' => false,
  1157. 'return' => true,
  1158. 'raw' => true
  1159. );
  1160. break;
  1161. case 'ajax':
  1162. case 'ajax_text':
  1163. case 'ajax_xml';
  1164. case 'ajax_json';
  1165. $options = array(
  1166. 'messages' => false,
  1167. 'render' => false,
  1168. 'meta' => false,
  1169. 'return' => false,
  1170. 'raw' => false,
  1171. 'ajax' => str_replace(array('ajax_', 'ajax'), array('', 'text'), $return_type)
  1172. );
  1173. break;
  1174. case 'response':
  1175. return $response;
  1176. break;
  1177. case 'render':
  1178. default:
  1179. $options = array(
  1180. 'messages' => true,
  1181. 'render' => true,
  1182. 'meta' => false,
  1183. 'return' => false,
  1184. 'raw' => false
  1185. );
  1186. break;
  1187. }
  1188. return $response->send('default', $options);
  1189. }
  1190. /**
  1191. * Proxy method
  1192. *
  1193. * @return string
  1194. */
  1195. public function getHeader()
  1196. {
  1197. return $this->getController()->getHeader();
  1198. }
  1199. /**
  1200. * Get current controller object
  1201. * @return e_admin_controller
  1202. */
  1203. public function getController()
  1204. {
  1205. if(null === $this->_current_controller)
  1206. {
  1207. $this->_initController();
  1208. }
  1209. return $this->_current_controller;
  1210. }
  1211. /**
  1212. * Try to init Controller from request using current controller map
  1213. *
  1214. * @return e_admin_dispatcher
  1215. */
  1216. protected function _initController()
  1217. {
  1218. $request = $this->getRequest();
  1219. $response = $this->getResponse();
  1220. if(isset($this->modes[$request->getModeName()]) && isset($this->modes[$request->getModeName()]['controller']))
  1221. {
  1222. $class_name = $this->modes[$request->getModeName()]['controller'];
  1223. $class_path = vartrue($this->modes[$request->getModeName()]['path']);
  1224. if($class_path)
  1225. {
  1226. require_once(e107::getParser()->replaceConstants($class_path));
  1227. }
  1228. if($class_name && class_exists($class_name))//NOTE: autoload in the play
  1229. {
  1230. $this->_current_controller = new $class_name($request, $response);
  1231. //give access to current request object, user defined init
  1232. $this->_current_controller->setRequest($this->getRequest())->init();
  1233. }
  1234. // Known controller (found in e_admin_dispatcher::$modes), class not found exception
  1235. else
  1236. {
  1237. // TODO - admin log
  1238. // get default controller
  1239. $this->_current_controller = $this->getDefaultController();
  1240. // add messages
  1241. e107::getMessage()->add('Can\'t find class <strong>&quot;'.($class_name ? $class_name : 'n/a').'&quot;</strong> for controller <strong>&quot;'.ucfirst($request->getModeName()).'&quot;</strong>', E_MESSAGE_ERROR)
  1242. ->add('Requested: '.e_SELF.'?'.$request->buildQueryString(), E_MESSAGE_DEBUG);
  1243. //
  1244. $request->setMode($this->getDefaultControllerName())->setAction('e404');
  1245. $this->_current_controller->setRequest($request)->init();
  1246. }
  1247. if(vartrue($this->modes[$request->getModeName()]['ui']))
  1248. {
  1249. $class_name = $this->modes[$request->getModeName()]['ui'];
  1250. $class_path = vartrue($this->modes[$request->getModeName()]['uipath']);
  1251. if($class_path)
  1252. {
  1253. require_once(e107::getParser()->replaceConstants($class_path));
  1254. }
  1255. if(class_exists($class_name))//NOTE: autoload in the play
  1256. {
  1257. $this->_current_controller->setParam('ui', new $class_name($this->_current_controller));
  1258. }
  1259. }
  1260. $this->_current_controller->setParam('modes', $this->modes);
  1261. }
  1262. // Not known controller (not found in e_admin_dispatcher::$modes) exception
  1263. else
  1264. {
  1265. // TODO - admin log
  1266. $this->_current_controller = $this->getDefaultController();
  1267. // add messages
  1268. e107::getMessage()->add('Can\'t find class for controller <strong>&quot;'.ucfirst($request->getModeName()).'&quot;</strong>', E_MESSAGE_ERROR)
  1269. ->add('Requested: '.e_SELF.'?'.$request->buildQueryString(), E_MESSAGE_DEBUG);
  1270. // go to not found page
  1271. $request->setMode($this->getDefaultControllerName())->setAction('e404');
  1272. $this->_current_controller->setRequest($request)->init();
  1273. }
  1274. return $this;
  1275. }
  1276. /**
  1277. * Default controller object - needed if controller not found
  1278. * @return e_admin_controller
  1279. */
  1280. public function getDefaultController()
  1281. {
  1282. $class_name = $this->getDefaultControllerName();
  1283. return new $class_name($this->getRequest(), $this->getResponse());
  1284. }
  1285. /**
  1286. * Default controller name - needed if controller not found
  1287. * @return string name of controller
  1288. */
  1289. public function getDefaultControllerName()
  1290. {
  1291. return 'e_admin_controller';
  1292. }
  1293. /**
  1294. * Generic Admin Menu Generator
  1295. * @return string
  1296. */
  1297. function renderMenu()
  1298. {
  1299. $tp = e107::getParser();
  1300. $var = array();
  1301. $selected = false;
  1302. foreach($this->adminMenu as $key => $val)
  1303. {
  1304. $tmp = explode('/', trim($key, '/'), 3);
  1305. // sync with mode/route access
  1306. if(!$this->checkModeAccess($tmp[0]) || !$this->checkRouteAccess($tmp[0].'/'.$tmp[1]))
  1307. {
  1308. continue;
  1309. }
  1310. // custom 'selected' check
  1311. if(isset($val['selected']) && $val['selected']) $selected = $val['selected'] === true ? $key : $val['selected'];
  1312. foreach ($val as $k=>$v)
  1313. {
  1314. switch($k)
  1315. {
  1316. case 'caption':
  1317. $k2 = 'text';
  1318. $v = defset($v, $v);
  1319. break;
  1320. case 'url':
  1321. $k2 = 'link';
  1322. $v = $tp->replaceConstants($v, 'abs').'?mode='.$tmp[0].'&amp;action='.$tmp[1];
  1323. break;
  1324. case 'uri':
  1325. $k2 = 'link';
  1326. $v = $tp->replaceConstants($v, 'abs');
  1327. break;
  1328. default:
  1329. $k2 = $k;
  1330. break;
  1331. }
  1332. // Access check done above
  1333. // if($val['perm']!= null) // check perms
  1334. // {
  1335. // if(getperms($val['perm']))
  1336. // {
  1337. // $var[$key][$k2] = $v;
  1338. // }
  1339. // }
  1340. // else
  1341. {
  1342. $var[$key][$k2] = $v;
  1343. }
  1344. }
  1345. // TODO slide down menu options?
  1346. if(!vartrue($var[$key]['link']))
  1347. {
  1348. $var[$key]['link'] = e_SELF.'?mode='.$tmp[0].'&amp;action='.$tmp[1]; // FIXME - URL based on $modes, remove url key
  1349. }
  1350. if(varset($val['tab']))
  1351. {
  1352. $var[$key]['link'] .= "&amp;tab=".$val['tab'];
  1353. }
  1354. /*$var[$key]['text'] = $val['caption'];
  1355. $var[$key]['link'] = (vartrue($val['url']) ? $tp->replaceConstants($val['url'], 'abs') : e_SELF).'?mode='.$tmp[0].'&action='.$tmp[1];
  1356. $var[$key]['perm'] = $val['perm']; */
  1357. }
  1358. if(empty($var)) return '';
  1359. $request = $this->getRequest();
  1360. if(!$selected) $selected = $request->getMode().'/'.$request->getAction();
  1361. $selected = vartrue($this->adminMenuAliases[$selected], $selected);
  1362. return e107::getNav()->admin($this->menuTitle, $selected, $var);
  1363. }
  1364. /**
  1365. * Render Help Text in <ul> format. XXX TODO
  1366. */
  1367. function renderHelp()
  1368. {
  1369. }
  1370. /**
  1371. * Check for table issues and warn the user. XXX TODO
  1372. * ie. user is using French interface but no french tables found for the current DB tables.
  1373. */
  1374. function renderWarnings()
  1375. {
  1376. }
  1377. }
  1378. class e_admin_controller
  1379. {
  1380. /**
  1381. * @var e_admin_request
  1382. */
  1383. protected $_request;
  1384. /**
  1385. * @var e_admin_response
  1386. */
  1387. protected $_response;
  1388. /**
  1389. * @var array User defined parameters
  1390. */
  1391. protected $_params = array();
  1392. /**
  1393. * @var string default action name
  1394. */
  1395. protected $_default_action = 'index';
  1396. /**
  1397. * List (numerical array) of only allowed for this controller actions
  1398. * Useful to grant access for certain pre-defined actions only
  1399. * XXX - we may move this in dispatcher (or even having it also there), still searching the most 'friendly' way
  1400. * @var array
  1401. */
  1402. protected $allow = array();
  1403. /**
  1404. * List (numerical array) of only disallowed for this controller actions
  1405. * Useful to restrict access for certain pre-defined actions only
  1406. * XXX - we may move this in dispatcher (or even having it also there), still searching the most 'friendly' way
  1407. * @var array
  1408. */
  1409. protected $disallow = array();
  1410. /**
  1411. * Constructor
  1412. * @param e_admin_request $request [optional]
  1413. */
  1414. public function __construct($request, $response, $params = array())
  1415. {
  1416. $this->_params = array_merge(array('enable_triggers' => false), $params);
  1417. $this->setRequest($request)
  1418. ->setResponse($response)
  1419. ->setParams($params);
  1420. $this->checkAccess();
  1421. }
  1422. /**
  1423. * Check against allowed/disallowed actions
  1424. * FIXME check plugin admin access (check_class(P)), confirm e-token is verified
  1425. */
  1426. public function checkAccess()
  1427. {
  1428. $request = $this->getRequest();
  1429. $currentAction = $request->getAction();
  1430. // access based on mode setting - general controller access
  1431. if(!empty($this->disallow) && in_array($currentAction, $this->disallow))
  1432. {
  1433. $request->setAction('e403');
  1434. e107::getMessage()->addError('You don\'t have permissions to view this page.')
  1435. ->addDebug('Controller action disallowed restriction triggered.');
  1436. return false;
  1437. }
  1438. // access based on $access settings - access per action
  1439. if(!empty($this->allow) && !in_array($currentAction, $this->allow))
  1440. {
  1441. $request->setAction('e403');
  1442. e107::getMessage()->addError('You don\'t have permissions to view this page.')
  1443. ->addDebug('Controller action not in allowed list restriction triggered.');
  1444. return false;
  1445. }
  1446. return true;
  1447. }
  1448. /**
  1449. * User defined init
  1450. * Called before dispatch routine
  1451. */
  1452. public function init()
  1453. {
  1454. }
  1455. /**
  1456. * Get controller parameter
  1457. * Currently used core parameters:
  1458. * - enable_triggers: don't use it direct, see {@link setTriggersEnabled()}
  1459. * - modes - see dispatcher::$modes
  1460. * - ajax_response - text|xml|json - default is 'text'; this should be set by the action method
  1461. * - TODO - more parameters/add missing to this list
  1462. *
  1463. * @param string $key [optional] if null - get whole array
  1464. * @param mixed $default [optional]
  1465. * @return mixed
  1466. */
  1467. public function getParam($key = null, $default = null)
  1468. {
  1469. if(null === $key)
  1470. {
  1471. return $this->_params;
  1472. }
  1473. return (isset($this->_params[$key]) ? $this->_params[$key] : $default);
  1474. }
  1475. /**
  1476. * Set parameter
  1477. * @param string $key
  1478. * @param mixed $value
  1479. * @return e_admin_controller
  1480. */
  1481. public function setParam($key, $value)
  1482. {
  1483. if(null === $value)
  1484. {
  1485. unset($this->_params[$key]);
  1486. return $this;
  1487. }
  1488. $this->_params[$key] = $value;
  1489. return $this;
  1490. }
  1491. /**
  1492. * Merge passed parameter array with current parameters
  1493. * @param array $params
  1494. * @return e_admin_controller
  1495. */
  1496. public function setParams($params)
  1497. {
  1498. $this->_params = array_merge($this->_params, $params);
  1499. return $this;
  1500. }
  1501. /**
  1502. * Reset parameter array
  1503. * @param array $params
  1504. * @return e_admin_controller
  1505. */
  1506. public function resetParams($params)
  1507. {
  1508. $this->_params = $params;
  1509. return $this;
  1510. }
  1511. /**
  1512. * Get current request object
  1513. * @return e_admin_request
  1514. */
  1515. public function getRequest()
  1516. {
  1517. return $this->_request;
  1518. }
  1519. /**
  1520. * Set current request object
  1521. * @param e_admin_request $request
  1522. * @return e_admin_controller
  1523. */
  1524. public function setRequest($request)
  1525. {
  1526. $this->_request = $request;
  1527. return $this;
  1528. }
  1529. /**
  1530. * Get current response object
  1531. * @return e_admin_response
  1532. */
  1533. public function getResponse()
  1534. {
  1535. return $this->_response;
  1536. }
  1537. /**
  1538. * Set current response object
  1539. * @param e_admin_response $response
  1540. * @return e_admin_controller
  1541. */
  1542. public function setResponse($response)
  1543. {
  1544. $this->_response = $response;
  1545. return $this;
  1546. }
  1547. /**
  1548. * Get current dispatcher object
  1549. * @return e_admin_dispatcher
  1550. */
  1551. public function getDispatcher()
  1552. {
  1553. return e107::getRegistry('admin/ui/dispatcher');
  1554. }
  1555. /**
  1556. * Request proxy method
  1557. * @param string $key [optional]
  1558. * @param mixed $default [optional]
  1559. * @return mixed
  1560. */
  1561. public function getQuery($key = null, $default = null)
  1562. {
  1563. return $this->getRequest()->getQuery($key, $default);
  1564. }
  1565. /**
  1566. * Request proxy method
  1567. * @param string|array $key
  1568. * @param mixed $value [optional]
  1569. * @return e_admin_controller
  1570. */
  1571. public function setQuery($key, $value = null)
  1572. {
  1573. $this->getRequest()->setQuery($key, $value);
  1574. return $this;
  1575. }
  1576. /**
  1577. * Request proxy method
  1578. * @param string $key [optional]
  1579. * @param mixed $default [optional]
  1580. * @return mixed
  1581. */
  1582. public function getPosted($key = null, $default = null)
  1583. {
  1584. return $this->getRequest()->getPosted($key, $default);
  1585. }
  1586. /**
  1587. * Request proxy method
  1588. * @param string $key
  1589. * @param mixed $value [optional]
  1590. * @return e_admin_controller
  1591. */
  1592. public function setPosted($key, $value = null)
  1593. {
  1594. $this->getRequest()->setPosted($key, $value);
  1595. return $this;
  1596. }
  1597. /**
  1598. * Add page title, response proxy method
  1599. *
  1600. * @param string $title if boolean true - current menu caption will be used
  1601. * @param boolean $meta add to meta as well
  1602. * @return e_admin_controller
  1603. */
  1604. public function addTitle($title = true, $meta = true)
  1605. {
  1606. if(true === $title)
  1607. {
  1608. $_dispatcher = $this->getDispatcher();
  1609. $data = $_dispatcher->getPageTitles();
  1610. $search = $this->getMode().'/'.$this->getAction();
  1611. if(isset($data[$search])) $res['caption'] = $data[$search];
  1612. else
  1613. {
  1614. $data = $_dispatcher->getMenuData();
  1615. if(isset($data[$search])) $res = $data[$search];
  1616. else return $this;
  1617. }
  1618. $title = $res['caption'];
  1619. }
  1620. // print_a($title);
  1621. $this->getResponse()->appendTitle($title);
  1622. if($meta) $this->addMetaTitle($title);
  1623. return $this;
  1624. }
  1625. /**
  1626. * Add page meta title, response proxy method.
  1627. * Should be called before header.php
  1628. *
  1629. * @param string $title
  1630. * @return e_admin_controller
  1631. */
  1632. public function addMetaTitle($title)
  1633. {
  1634. $this->getResponse()->addMetaTitle($title);
  1635. return $this;
  1636. }
  1637. /**
  1638. * Add header content, response proxy method
  1639. * Should be called before header.php
  1640. *
  1641. * @param string $content
  1642. * @return e_admin_controller
  1643. */
  1644. public function addHeader($content)
  1645. {
  1646. $this->getResponse()->addHeaderContent(vartrue($content));
  1647. return $this;
  1648. }
  1649. /**
  1650. * Get header content, response proxy method
  1651. *
  1652. * @return string
  1653. */
  1654. public function getHeader()
  1655. {
  1656. return $this->getResponse()->getHeaderContent();
  1657. }
  1658. /**
  1659. * Get current mode, response proxy method
  1660. * @return string
  1661. */
  1662. public function getMode()
  1663. {
  1664. return $this->getRequest()->getMode();
  1665. }
  1666. /**
  1667. * Get current actin, response proxy method
  1668. * @return string
  1669. */
  1670. public function getAction()
  1671. {
  1672. return $this->getRequest()->getAction();
  1673. }
  1674. /**
  1675. * Get current ID, response proxy method
  1676. * @return string
  1677. */
  1678. public function getId()
  1679. {
  1680. return $this->getRequest()->getId();
  1681. }
  1682. /**
  1683. * Get response owned JS Helper instance, response proxy method
  1684. *
  1685. * @return e_jshelper
  1686. */
  1687. public function getJsHelper()
  1688. {
  1689. return $this->getResponse()->getJsHelper();
  1690. }
  1691. protected function _preDispatch($action = '')
  1692. {
  1693. if(!$action) $action = $this->getRequest()->getActionName();
  1694. $method = $this->toMethodName($action, 'page');
  1695. if(!method_exists($this, $method))
  1696. {
  1697. $this->getRequest()->setAction($this->getDefaultAction());
  1698. }
  1699. // switch to 404 if needed
  1700. $method = $this->toMethodName($this->getRequest()->getActionName(), 'page');
  1701. if(!method_exists($this, $method))
  1702. {
  1703. $this->getRequest()->setAction('e404');
  1704. $message = e107::getParser()->lanVars(LAN_UI_404_METHOD_ERROR, $method, true);
  1705. e107::getMessage()->add($message, E_MESSAGE_ERROR);
  1706. }
  1707. }
  1708. /**
  1709. * Dispatch observer, check for triggers
  1710. *
  1711. * @param string $action [optional]
  1712. * @return e_admin_controller
  1713. */
  1714. public function dispatchObserver($action = null)
  1715. {
  1716. $request = $this->getRequest();
  1717. if(null === $request)
  1718. {
  1719. $request = new e_admin_request();
  1720. $this->setRequest($request);
  1721. }
  1722. $this->_preDispatch($action);
  1723. if(null === $action)
  1724. {
  1725. $action = $request->getActionName();
  1726. }
  1727. // check for observer
  1728. $actionObserverName = $this->toMethodName($action, 'observer', e_AJAX_REQUEST);
  1729. if(method_exists($this, $actionObserverName))
  1730. {
  1731. $this->$actionObserverName();
  1732. }
  1733. // check for triggers, not available in Ajax mode
  1734. if(!e_AJAX_REQUEST && $this->triggersEnabled())
  1735. {
  1736. $posted = $request->getPosted();
  1737. foreach ($posted as $key => $value)
  1738. {
  1739. if(strpos($key, 'etrigger_') === 0)
  1740. {
  1741. $actionTriggerName = $this->toMethodName($action.$request->camelize(substr($key, 9)), 'trigger', false);
  1742. if(method_exists($this, $actionTriggerName))
  1743. {
  1744. $this->$actionTriggerName($value);
  1745. }
  1746. //Check if triggers are still enabled
  1747. if(!$this->triggersEnabled())
  1748. {
  1749. break;
  1750. }
  1751. }
  1752. }
  1753. }
  1754. return $this;
  1755. }
  1756. /**
  1757. * Dispatch header, not allowed in Ajax mode
  1758. * @param string $action [optional]
  1759. * @return e_admin_controller
  1760. */
  1761. public function dispatchHeader($action = null)
  1762. {
  1763. // not available in Ajax mode
  1764. if(e_AJAX_REQUEST)
  1765. {
  1766. return $this;
  1767. }
  1768. $request = $this->getRequest();
  1769. if(null === $request)
  1770. {
  1771. $request = new e_admin_request();
  1772. $this->setRequest($request);
  1773. }
  1774. $this->_preDispatch($action);
  1775. if(null === $action)
  1776. {
  1777. $action = $request->getActionName();
  1778. }
  1779. // check for observer
  1780. $actionHeaderName = $this->toMethodName($action, 'header', false);
  1781. if(method_exists($this, $actionHeaderName))
  1782. {
  1783. $this->$actionHeaderName();
  1784. }
  1785. //send meta data
  1786. $this->getResponse()->sendMeta();
  1787. return $this;
  1788. }
  1789. /**
  1790. * Dispatch controller action
  1791. *
  1792. * @param string $action [optional]
  1793. * @return e_admin_response
  1794. */
  1795. public function dispatchPage($action = null)
  1796. {
  1797. $request = $this->getRequest();
  1798. if(null === $request)
  1799. {
  1800. $request = new e_admin_request();
  1801. $this->setRequest($request);
  1802. }
  1803. $response = $this->getResponse();
  1804. $this->_preDispatch($action);
  1805. if(null === $action)
  1806. {
  1807. $action = $request->getActionName();
  1808. }
  1809. // check for observer
  1810. $actionName = $this->toMethodName($action, 'page');
  1811. $ret = '';
  1812. if(!method_exists($this, $actionName)) // pre dispatch already switched to default action/not found page if needed
  1813. {
  1814. e107::getMessage()->add('Action '.$actionName.' no found!', E_MESSAGE_ERROR);
  1815. return $response;
  1816. }
  1817. ob_start(); //catch any output
  1818. $ret = $this->{$actionName}();
  1819. //Ajax XML/JSON communication
  1820. if(e_AJAX_REQUEST && is_array($ret))
  1821. {
  1822. $response_type = $this->getParam('ajax_response', 'xml');
  1823. ob_clean();
  1824. $js_helper = $response->getJsHelper();
  1825. foreach ($ret as $act => $data)
  1826. {
  1827. $js_helper->addResponse($data, $act);
  1828. }
  1829. $js_helper->sendResponse($response_type);
  1830. }
  1831. $ret .= ob_get_clean();
  1832. // Ajax text response
  1833. if(e_AJAX_REQUEST)
  1834. {
  1835. $response_type = 'text';
  1836. $response->getJsHelper()->addResponse($ret)->sendResponse($response_type);
  1837. }
  1838. else
  1839. {
  1840. $response->appendBody($ret);
  1841. }
  1842. return $response;
  1843. }
  1844. public function E404Observer()
  1845. {
  1846. $this->getResponse()->setTitle(LAN_UI_404_TITLE_ERROR);
  1847. }
  1848. public function E404Page()
  1849. {
  1850. return '<div class="center">'.LAN_UI_404_BODY_ERROR.'</div>'; // TODO - lan
  1851. }
  1852. public function E404AjaxPage()
  1853. {
  1854. exit;
  1855. }
  1856. public function E403Observer()
  1857. {
  1858. $this->getResponse()->setTitle(LAN_UI_403_TITLE_ERROR);
  1859. }
  1860. public function E403Page()
  1861. {
  1862. return '<div class="center">'.LAN_UI_403_BODY_ERROR.'</div>'; // TODO - lan
  1863. }
  1864. public function E403AjaxPage()
  1865. {
  1866. exit;
  1867. }
  1868. /**
  1869. * Generic redirect handler, it handles almost everything we would need.
  1870. * Additionally, it moves currently registered system messages to SESSION message stack
  1871. * In almost every case {@link redirectAction()} and {@link redirectMode()} are better solution
  1872. *
  1873. * @param string $action defaults to current action
  1874. * @param string $mode defaults to current mode
  1875. * @param string|array $exclude_query comma delimited variable names to be excluded from current query OR TRUE to exclude everything
  1876. * @param string|array $merge_query query string (&amp; delimiter) or associative array to be merged with current query
  1877. * @param string $path default to e_SELF
  1878. * @return void
  1879. */
  1880. public function redirect($action = null, $mode = null, $exclude_query = '', $merge_query = array(), $path = null)
  1881. {
  1882. $request = $this->getRequest();
  1883. if($mode) $request->setMode($mode);
  1884. if($action) $request->setAction($action);
  1885. if(!$path) $path = e_SELF;
  1886. //prevent cache
  1887. header('Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
  1888. // header('Pragma: no-cache');
  1889. $url = $path.'?'.$request->buildQueryString($merge_query, false, $exclude_query);
  1890. // Transfer all messages to session
  1891. e107::getMessage()->moveToSession();
  1892. // write session data
  1893. session_write_close();
  1894. // do redirect
  1895. header('Location: '.$url);
  1896. exit;
  1897. }
  1898. /**
  1899. * Convenient redirect() proxy method, make life easier when redirecting between actions
  1900. * in same mode.
  1901. *
  1902. * @param string $action [optional]
  1903. * @param string|array $exclude_query [optional]
  1904. * @param string|array $merge_query [optional]
  1905. * @return none
  1906. */
  1907. public function redirectAction($action = null, $exclude_query = '', $merge_query = array())
  1908. {
  1909. $this->redirect($action, null, $exclude_query, $merge_query);
  1910. }
  1911. /**
  1912. * Convenient redirect to another mode (doesn't use current Query state)
  1913. * If path is empty, it'll be auto-detected from modes (dispatcher) array
  1914. *
  1915. * @param string $mode
  1916. * @param string $action
  1917. * @param string|array $query [optional]
  1918. * @param string $path
  1919. * @return void
  1920. */
  1921. public function redirectMode($mode, $action, $query = array(), $path = null)
  1922. {
  1923. if(!$path && $this->getParam('modes'))
  1924. {
  1925. $modes = $this->getParam('modes');
  1926. if(vartue($modes[$mode]) && vartrue($modes[$mode]['url']))
  1927. {
  1928. $path = e107::getParser()->replaceConstants($modes[$mode]['url'], 'abs');
  1929. }
  1930. }
  1931. $this->redirect($action, $mode, true, $query, $path);
  1932. }
  1933. /**
  1934. * Convert action name to method name
  1935. *
  1936. * @param string $action_name formatted (e.g. request method getActionName()) action name
  1937. * @param string $type page|observer|header|trigger
  1938. * @param boolean $ajax force with true/false, if null will be auto-resolved
  1939. * @return string
  1940. */
  1941. public function toMethodName($action_name, $type= 'page', $ajax = null)
  1942. {
  1943. if(null === $ajax) $ajax = e_AJAX_REQUEST; //auto-resolving
  1944. return $action_name.($ajax ? 'Ajax' : '').ucfirst(strtolower($type));
  1945. }
  1946. /**
  1947. * Check if there is a trigger available in the posted data
  1948. * @param array $exclude
  1949. * @return boolean
  1950. */
  1951. public function hasTrigger($exclude = array())
  1952. {
  1953. $posted = array_keys($this->getPosted());
  1954. foreach ($posted as $key)
  1955. {
  1956. if(!in_array($key, $exclude) && strpos($key, 'etrigger_') === 0)
  1957. {
  1958. return true;
  1959. }
  1960. }
  1961. return false;
  1962. }
  1963. /**
  1964. * Get default action
  1965. * @return string action
  1966. */
  1967. public function getDefaultAction()
  1968. {
  1969. return $this->_default_action;
  1970. }
  1971. /**
  1972. * Set default action
  1973. * @param string $action_name
  1974. * @return e_admin_controller
  1975. */
  1976. public function setDefaultAction($action_name)
  1977. {
  1978. $this->_default_action = $action_name;
  1979. return $this;
  1980. }
  1981. /**
  1982. * @return boolean
  1983. */
  1984. public function triggersEnabled()
  1985. {
  1986. return $this->getParam('enable_triggers');
  1987. }
  1988. /**
  1989. * @param boolean $flag
  1990. * @return e_admin_controller
  1991. */
  1992. public function setTriggersEnabled($flag)
  1993. {
  1994. $this->setParam('enable_triggers', $flag);
  1995. return $this;
  1996. }
  1997. }
  1998. //FIXME - move everything from e_admin_ui except model auto-create related code
  1999. class e_admin_controller_ui extends e_admin_controller
  2000. {
  2001. /**
  2002. * @var array UI field data
  2003. */
  2004. protected $fields = array();
  2005. /**
  2006. * @var array default fields activated on List view
  2007. */
  2008. protected $fieldpref = array();
  2009. /**
  2010. * @var array Plugin Preference description array
  2011. */
  2012. protected $prefs = array();
  2013. /**
  2014. * Data required for _modifyListQry() to automate
  2015. * db query building
  2016. * @var array
  2017. */
  2018. protected $tableJoin = array();
  2019. /**
  2020. * Array of table names and their aliases. (detected from listQry)
  2021. * db query building
  2022. * @var array
  2023. */
  2024. protected $joinAlias = array();
  2025. /**
  2026. * Array of fields detected from listQry which are JOINs
  2027. * @example returns array('user_name'=>'u.user_name'); from $listQry = "SELECT n.*,u.user_name FROM #news...."etc.
  2028. */
  2029. protected $joinField = array();
  2030. /**
  2031. * Main model table alias
  2032. * @var string
  2033. */
  2034. protected $tableAlias;
  2035. /**
  2036. * @var string plugin name
  2037. */
  2038. protected $pluginName;
  2039. /**
  2040. * @var string
  2041. */
  2042. protected $defaultOrderField = null;
  2043. /**
  2044. * @var string
  2045. */
  2046. protected $defaultOrder = 'asc';
  2047. /**
  2048. * @var string SQL order, false to disable order, null is default order
  2049. */
  2050. protected $listOrder = null;
  2051. /**
  2052. * @var string field containing the order number
  2053. */
  2054. protected $sortField = null;
  2055. /**
  2056. * @var int reorder step
  2057. */
  2058. protected $orderStep = 1;
  2059. /**
  2060. * Example: array('0' => 'Tab label', '1' => 'Another label');
  2061. * Referenced from $field property per field - 'tab => xxx' where xxx is the tab key (identifier)
  2062. * @var array edit/create form tabs
  2063. */
  2064. protected $tabs = array();
  2065. /**
  2066. * Example: array('0' => 'Tab label', '1' => 'Another label');
  2067. * Referenced from $prefs property per field - 'tab => xxx' where xxx is the tab key (identifier)
  2068. * @var array edit/create form tabs
  2069. */
  2070. protected $preftabs = array();
  2071. /**
  2072. * TODO Example:
  2073. * Contains required data for auto-assembling URL from every record
  2074. * For greater control - override url() method
  2075. * @var array
  2076. */
  2077. protected $url = array();
  2078. /**
  2079. * TODO Example:
  2080. * Contains required data for mapping featurebox fields
  2081. * @var array
  2082. */
  2083. protected $featurebox = array();
  2084. /**
  2085. * Structure same as TreeModel parameters used for building the load() SQL
  2086. * @var additional SQL to be applied when auto-building the list query
  2087. */
  2088. protected $listQrySql = array();
  2089. /**
  2090. * @var Custom Filter SQL Query override.
  2091. */
  2092. protected $filterQry = null;
  2093. /**
  2094. * @var boolean
  2095. */
  2096. protected $batchDelete = true;
  2097. /**
  2098. * @var boolean
  2099. */
  2100. protected $batchCopy = false;
  2101. /**
  2102. * @var boolean
  2103. */
  2104. protected $batchLink = false;
  2105. /**
  2106. * @var boolean
  2107. */
  2108. protected $batchFeaturebox = false;
  2109. /**
  2110. * Could be LAN constant (mulit-language support)
  2111. *
  2112. * @var string plugin name
  2113. */
  2114. protected $pluginTitle;
  2115. /**
  2116. * Default (db) limit value
  2117. * @var integer
  2118. */
  2119. protected $perPage = 20;
  2120. /**
  2121. * @var e_admin_model
  2122. */
  2123. protected $formQuery = false; // custom form post query
  2124. /**
  2125. * @var e_admin_model
  2126. */
  2127. protected $_model = null;
  2128. /**
  2129. * @var e_admin_tree_model
  2130. */
  2131. protected $_tree_model = null;
  2132. /**
  2133. * @var e_admin_tree_model
  2134. */
  2135. protected $_ui = null;
  2136. /**
  2137. * @var e_plugin_pref|e_core_pref
  2138. */
  2139. protected $_pref = null;
  2140. /**
  2141. * Prevent parsing table aliases more than once
  2142. * @var boolean
  2143. */
  2144. protected $_alias_parsed = false;
  2145. public function getBatchDelete()
  2146. {
  2147. return $this->batchDelete;
  2148. }
  2149. public function getBatchCopy()
  2150. {
  2151. return $this->batchCopy;
  2152. }
  2153. public function getBatchLink()
  2154. {
  2155. return $this->batchLink;
  2156. }
  2157. public function getBatchFeaturebox()
  2158. {
  2159. return $this->batchFeaturebox;
  2160. }
  2161. /**
  2162. * @return string
  2163. */
  2164. public function getPluginName()
  2165. {
  2166. return $this->pluginName;
  2167. }
  2168. /**
  2169. * @return string
  2170. */
  2171. public function getPluginTitle()
  2172. {
  2173. return deftrue($this->pluginTitle, $this->pluginTitle);
  2174. }
  2175. /**
  2176. * Get Tab data
  2177. * @return array
  2178. */
  2179. public function getTabs()
  2180. {
  2181. return $this->tabs;
  2182. }
  2183. /**
  2184. * Get Tab data
  2185. * @return array
  2186. */
  2187. public function getPrefTabs()
  2188. {
  2189. return $this->preftabs;
  2190. }
  2191. /**
  2192. * Get URL profile
  2193. * @return array
  2194. */
  2195. public function getUrl()
  2196. {
  2197. return $this->url;
  2198. }
  2199. /**
  2200. * Get Featurebox Copy
  2201. * @return array
  2202. */
  2203. public function getFeaturebox()
  2204. {
  2205. return $this->featurebox;
  2206. }
  2207. /**
  2208. * Get all field data
  2209. * @return array
  2210. */
  2211. public function getFields()
  2212. {
  2213. return $this->fields;
  2214. }
  2215. /**
  2216. *
  2217. * @param string $field
  2218. * @param string $key attribute name
  2219. * @param mixed $default default value if not set, default is null
  2220. * @return mixed
  2221. */
  2222. public function getFieldAttr($field, $key = null, $default = null)
  2223. {
  2224. if(isset($this->fields[$field]))
  2225. {
  2226. if(null !== $key)
  2227. {
  2228. return isset($this->fields[$field][$key]) ? $this->fields[$field][$key] : $default;
  2229. }
  2230. return $this->fields[$field];
  2231. }
  2232. return $default;
  2233. }
  2234. /**
  2235. *
  2236. * @param string $field
  2237. * @param string $key attribute name
  2238. * @param mixed $value default value if not set, default is null
  2239. * @return e_admin_controller_ui
  2240. */
  2241. public function setFieldAttr($field, $key = null, $value = null)
  2242. {
  2243. // add field array
  2244. if(is_array($field))
  2245. {
  2246. foreach ($field as $f => $atts)
  2247. {
  2248. $this->setFieldAttr($f, $atts);
  2249. }
  2250. return $this;
  2251. }
  2252. // remove a field
  2253. if(null === $key)
  2254. {
  2255. unset($this->fields[$field]);
  2256. return $this;
  2257. }
  2258. // add to attribute array of a field
  2259. if(is_array($key))
  2260. {
  2261. foreach ($key as $k => $att)
  2262. {
  2263. $this->setFieldAttr($field, $k, $att);
  2264. }
  2265. return $this;
  2266. }
  2267. // remove attribute from field attribute set
  2268. if(null === $value && $key != 'type')
  2269. {
  2270. unset($this->fields[$field][$key]);
  2271. return $this;
  2272. }
  2273. // set attribute value
  2274. $this->fields[$field][$key] = $value;
  2275. return $this;
  2276. }
  2277. /**
  2278. * Get fields stored as user preferences
  2279. * @return array
  2280. */
  2281. public function getFieldPref()
  2282. {
  2283. return $this->fieldpref;
  2284. }
  2285. /**
  2286. * Get Config data array
  2287. * @return array
  2288. */
  2289. public function getPrefs()
  2290. {
  2291. return $this->prefs;
  2292. }
  2293. public function getPerPage()
  2294. {
  2295. return $this->perPage;
  2296. }
  2297. public function getFormQuery()
  2298. {
  2299. return $this->formQuery;
  2300. }
  2301. public function getPrimaryName()
  2302. {
  2303. return $this->getModel()->getFieldIdName();
  2304. }
  2305. public function getDefaultOrderField()
  2306. {
  2307. return ($this->defaultOrder ? $this->defaultOrderField : $this->getPrimaryName());
  2308. }
  2309. public function getDefaultOrder()
  2310. {
  2311. return ($this->defaultOrder ? $this->defaultOrder : 'asc');
  2312. }
  2313. /**
  2314. * Get column preference array
  2315. * @return array
  2316. */
  2317. public function getUserPref()
  2318. {
  2319. //global $user_pref;
  2320. // return vartrue($user_pref['admin_cols_'.$this->getTableName()], array());
  2321. return e107::getUser()->getPref('admin_cols_'.$this->getTableName(), array());
  2322. }
  2323. /**
  2324. * Set column preference array
  2325. * @return boolean success
  2326. */
  2327. public function setUserPref($new)
  2328. {
  2329. //global $user_pref;
  2330. //e107::getUser()->getConfig()->setData($new);
  2331. //$user_pref['admin_cols_'.$this->getTableName()] = $new;
  2332. //$this->fieldpref = $new;
  2333. //return save_prefs('user');
  2334. $this->fieldpref = $new;
  2335. return e107::getUser()->getConfig()
  2336. ->set('admin_cols_'.$this->getTableName(), $new)
  2337. ->save();
  2338. }
  2339. /**
  2340. * Get current model
  2341. *
  2342. * @return e_admin_model
  2343. */
  2344. public function getModel()
  2345. {
  2346. if(null === $this->_model)
  2347. {
  2348. $this->_setModel();
  2349. }
  2350. return $this->_model;
  2351. }
  2352. /**
  2353. * Set controller model
  2354. * @param e_admin_model $model
  2355. * @return e_admin_controller_ui
  2356. */
  2357. public function setModel($model)
  2358. {
  2359. $this->_model = $model;
  2360. return $this;
  2361. }
  2362. /**
  2363. * Get model validation array
  2364. * @return array
  2365. */
  2366. public function getValidationRules()
  2367. {
  2368. return $this->getModel()->getValidationRules();
  2369. }
  2370. /**
  2371. * Get model data field array
  2372. * @return array
  2373. */
  2374. public function getDataFields()
  2375. {
  2376. return $this->getModel()->getDataFields();
  2377. }
  2378. /**
  2379. * Get model table or alias
  2380. * @param boolean $alias get table alias on true, default false
  2381. * @param object $prefix add e107 special '#' prefix, default false
  2382. * @return string
  2383. */
  2384. public function getTableName($alias = false, $prefix = false)
  2385. {
  2386. if($alias) return ($this->tableAlias ? $this->tableAlias : '');
  2387. return ($prefix ? '#' : '').$this->getModel()->getModelTable();
  2388. }
  2389. public function getIfTableAlias($prefix = false, $quote = false) //XXX May no longer by useful. see joinAlias()
  2390. {
  2391. $alias = $this->getTableName(true);
  2392. if($alias)
  2393. {
  2394. return $alias;
  2395. }
  2396. return ( !$quote ? $this->getTableName(false, $prefix) : '`'.$this->getTableName(false, $prefix).'`' );
  2397. }
  2398. /**
  2399. * Get join table data - XXX DEPRECATE?
  2400. * @param string $table if null all data will be returned
  2401. * @param string $att_name search for specific attribute, default null (no search)
  2402. * @return mixed
  2403. */
  2404. public function getJoinData($table = null, $att_name = null, $default_att = null)
  2405. {
  2406. if(null === $table)
  2407. {
  2408. return $this->tableJoin;
  2409. }
  2410. if(null === $att_name)
  2411. {
  2412. return (isset($this->tableJoin[$table]) ? $this->tableJoin[$table] : array());
  2413. }
  2414. return (isset($this->tableJoin[$table][$att_name]) ? $this->tableJoin[$table][$att_name] : $default_att);
  2415. }
  2416. public function setJoinData($table, $data) //XXX - DEPRECATE?
  2417. {
  2418. if(null === $data)
  2419. {
  2420. unset($this->tableJoin[$table]);
  2421. return $this;
  2422. }
  2423. $this->tableJoin[$table] = (array) $data;
  2424. return $this;
  2425. }
  2426. /**
  2427. * User defined model setter
  2428. * @return e_admin_controller_ui
  2429. */
  2430. protected function _setModel()
  2431. {
  2432. return $this;
  2433. }
  2434. /**
  2435. * Get current tree model
  2436. * @return e_admin_tree_model
  2437. */
  2438. public function getTreeModel()
  2439. {
  2440. if(null === $this->_tree_model)
  2441. {
  2442. $this->_setTreeModel();
  2443. }
  2444. return $this->_tree_model;
  2445. }
  2446. /**
  2447. * Set controller tree model
  2448. * @param e_admin_tree_model $tree_model
  2449. * @return e_admin_controller_ui
  2450. */
  2451. public function setTreeModel($tree_model)
  2452. {
  2453. $this->_tree_model = $tree_model;
  2454. return $this;
  2455. }
  2456. /**
  2457. * Get currently parsed model while in list mode
  2458. * Model instance is registered by e_form::renderListForm()
  2459. *
  2460. * @return e_admin_model
  2461. */
  2462. public function getListModel()
  2463. {
  2464. return e107::getRegistry('core/adminUI/currentListModel');
  2465. }
  2466. public function setListModel($model)
  2467. {
  2468. e107::setRegistry('core/adminUI/currentListModel', $model);
  2469. return $this;
  2470. }
  2471. /**
  2472. * User defined tree model setter
  2473. * @return e_admin_controller_ui
  2474. */
  2475. protected function _setTreeModel()
  2476. {
  2477. return $this;
  2478. }
  2479. /**
  2480. * Get extended (UI) Form instance
  2481. *
  2482. * @return e_admin_form_ui
  2483. */
  2484. public function getUI()
  2485. {
  2486. if(null === $this->_ui)
  2487. {
  2488. $this->_setUI();
  2489. }
  2490. return $this->_ui;
  2491. }
  2492. /**
  2493. * Set controller UI form
  2494. * @param e_admin_form_ui $ui
  2495. * @return e_admin_controller_ui
  2496. */
  2497. public function setUI($ui)
  2498. {
  2499. $this->_ui = $ui;
  2500. return $this;
  2501. }
  2502. /**
  2503. * User defined UI form setter
  2504. * @return e_admin_controller_ui
  2505. */
  2506. protected function _setUI()
  2507. {
  2508. return $this;
  2509. }
  2510. /**
  2511. * Get Config object
  2512. * @return e_plugin_pref or e_core_pref when used in core areas
  2513. */
  2514. public function getConfig()
  2515. {
  2516. if(null === $this->_pref)
  2517. {
  2518. $this->_setConfig();
  2519. }
  2520. return $this->_pref;
  2521. }
  2522. /**
  2523. * Set Config object
  2524. * @return e_admin_controller_ui
  2525. */
  2526. public function setConfig($config)
  2527. {
  2528. $this->_prefs = $config;
  2529. return $this;
  2530. }
  2531. /**
  2532. * User defined config setter
  2533. * @return e_admin_controller_ui
  2534. */
  2535. protected function _setConfig()
  2536. {
  2537. return $this;
  2538. }
  2539. /**
  2540. * Manage column visibility
  2541. * @param string $batch_trigger
  2542. * @return none
  2543. */
  2544. public function manageColumns()
  2545. {
  2546. $cols = array();
  2547. $posted = $this->getPosted('e-columns', array());
  2548. foreach ($this->getFields() as $field => $attr)
  2549. {
  2550. if((vartrue($attr['forced']) || in_array($field, $posted)) && !vartrue($attr['nolist']))
  2551. {
  2552. $cols[] = $field;
  2553. continue;
  2554. }
  2555. }
  2556. if($cols)
  2557. {
  2558. $this->setUserPref($cols);
  2559. }
  2560. }
  2561. /**
  2562. * Handle posted batch options routine
  2563. * @param string $batch_trigger
  2564. * @return e_admin_controller_ui
  2565. */
  2566. protected function _handleListBatch($batch_trigger)
  2567. {
  2568. $tp = e107::getParser();
  2569. //$multi_name = vartrue($this->fields['checkboxes']['toggle'], 'multiselect');
  2570. $multi_name = $this->getFieldAttr('checkboxes', 'toggle', 'multiselect');
  2571. $selected = array_values($this->getPosted($multi_name, array()));
  2572. $trigger = $tp->toDB(explode('__', $batch_trigger));
  2573. if(empty($selected) && !$this->getPosted('etrigger_delete_confirm')) // it's a delete batch, confirm screen
  2574. {
  2575. $params = $this->getFieldAttr($trigger[1], 'writeParms', array());
  2576. if(!is_array($params)) parse_str($params, $params);
  2577. if(!vartrue($params['batchNoCheck']))
  2578. {
  2579. return $this;
  2580. }
  2581. }
  2582. if($selected)
  2583. {
  2584. foreach ($selected as $i => $_sel)
  2585. {
  2586. $selected[$i] = preg_replace('/[^\w-:.]/', '', $_sel);
  2587. }
  2588. }
  2589. $this->setTriggersEnabled(false); //disable further triggering
  2590. switch($trigger[0])
  2591. {
  2592. case 'delete': //FIXME - confirmation screen
  2593. //method handleListDeleteBatch(); for custom handling of 'delete' batch
  2594. // if(empty($selected)) return $this;
  2595. // don't check selected data - subclass need to check additional post variables(confirm screen)
  2596. $method = 'handle'.$this->getRequest()->getActionName().'DeleteBatch';
  2597. if(method_exists($this, $method)) // callback handling
  2598. {
  2599. $this->$method($selected);
  2600. }
  2601. break;
  2602. case 'bool':
  2603. if(empty($selected)) return $this;
  2604. $field = $trigger[1];
  2605. $value = $trigger[2] ? 1 : 0;
  2606. //something like handleListBoolBatch(); for custom handling of 'bool' batch
  2607. $method = 'handle'.$this->getRequest()->getActionName().'BoolBatch';
  2608. if(method_exists($this, $method)) // callback handling
  2609. {
  2610. $this->$method($selected, $field, $value);
  2611. }
  2612. break;
  2613. case 'boolreverse':
  2614. if(empty($selected)) return $this;
  2615. $field = $trigger[1];
  2616. //something like handleListBoolreverseBatch(); for custom handling of 'boolreverse' batch
  2617. $method = 'handle'.$this->getRequest()->getActionName().'BoolreverseBatch';
  2618. if(method_exists($this, $method)) // callback handling
  2619. {
  2620. $this->$method($selected, $field);
  2621. }
  2622. break;
  2623. // see commma, userclasses batch options
  2624. case 'attach':
  2625. case 'deattach':
  2626. case 'addAll':
  2627. case 'clearAll':
  2628. $field = $trigger[1];
  2629. $value = $trigger[2];
  2630. if($trigger[0] == 'addAll')
  2631. {
  2632. $parms = $this->getFieldAttr($field, 'writeParms', array());
  2633. if(!is_array($parms)) parse_str($parms, $parms);
  2634. unset($parms['__options']);
  2635. $value = $parms;
  2636. if(empty($value)) return $this;
  2637. if(!is_array($value)) $value = array_map('trim', explode(',', $value));
  2638. }
  2639. if(method_exists($this, 'handleCommaBatch'))
  2640. {
  2641. $this->handleCommaBatch($selected, $field, $value, $trigger[0]);
  2642. }
  2643. break;
  2644. // append to userclass list
  2645. case 'ucadd':
  2646. case 'ucremove':
  2647. //if(empty($selected)) return $this;
  2648. $field = $trigger[1];
  2649. $class = $trigger[2];
  2650. $user = e107::getUser();
  2651. $e_userclass = e107::getUserClass();
  2652. // check userclass manager class
  2653. if (!isset($e_userclass->class_tree[$class]) || !$user->checkClass($e_userclass->class_tree[$class]))
  2654. {
  2655. return $this;
  2656. }
  2657. if(method_exists($this, 'handleCommaBatch'))
  2658. {
  2659. $trigger[0] = $trigger[0] == 'ucadd' ? 'attach' : 'deattach';
  2660. $this->handleCommaBatch($selected, $field, $class, $trigger[0]);
  2661. }
  2662. break;
  2663. // add all to userclass list
  2664. // clear userclass list
  2665. case 'ucaddall':
  2666. case 'ucdelall':
  2667. $field = $trigger[1];
  2668. $user = e107::getUser();
  2669. $e_userclass = e107::getUserClass();
  2670. $parms = $this->getFieldAttr($field, 'writeParms', array());
  2671. if(!is_array($parms)) parse_str($parms, $parms);
  2672. if(!vartrue($parms['classlist'])) return $this;
  2673. $classes = $e_userclass->uc_required_class_list($parms['classlist']);
  2674. foreach ($classes as $id => $label)
  2675. {
  2676. // check userclass manager class
  2677. if (!isset($e_userclass->class_tree[$id]) || !$user->checkClass($e_userclass->class_tree[$id]))
  2678. {
  2679. // TODO lan
  2680. $msg = $tp->lanVars("You don't have management permissions on [x]",$label);
  2681. $this->getTreeModel()->addMessageWarning($msg);
  2682. unset($classes[$id],$msg);
  2683. }
  2684. }
  2685. $this->handleCommaBatch($selected, $field, array_keys($classes), $trigger[0] === 'ucdelall' ? 'clearAll' : 'addAll');
  2686. break;
  2687. default:
  2688. $field = $trigger[0];
  2689. $value = $trigger[1];
  2690. //something like handleListUrlTypeBatch(); for custom handling of 'url_type' field name
  2691. $method = 'handle'.$this->getRequest()->getActionName().$this->getRequest()->camelize($field).'Batch';
  2692. if(method_exists($this, $method)) // callback handling
  2693. {
  2694. $this->$method($selected, $value);
  2695. break;
  2696. }
  2697. //handleListBatch(); for custom handling of all field names
  2698. if(empty($selected)) return $this;
  2699. $method = 'handle'.$this->getRequest()->getActionName().'Batch';
  2700. if(method_exists($this, $method))
  2701. {
  2702. $this->$method($selected, $field, $value);
  2703. }
  2704. break;
  2705. }
  2706. return $this;
  2707. }
  2708. /**
  2709. * Handle requested filter dropdown value
  2710. * @param string $value
  2711. * @return array field -> value
  2712. */
  2713. protected function _parseFilterRequest($filter_value)
  2714. {
  2715. $tp = e107::getParser();
  2716. if(!$filter_value || $filter_value === '___reset___')
  2717. {
  2718. return array();
  2719. }
  2720. $filter = $tp->toDB(explode('__', $filter_value));
  2721. $res = array();
  2722. switch($filter[0])
  2723. {
  2724. case 'bool':
  2725. // direct query
  2726. $res = array($filter[1], $filter[2]);
  2727. break;
  2728. case 'datestamp':
  2729. $dateConvert = array(
  2730. "hour" => "1 hour ago",
  2731. "day" => "24 hours ago",
  2732. "week" => "1 week ago",
  2733. "month" => "1 month ago",
  2734. "year" => "1 year ago"
  2735. );
  2736. $ky = $filter[2];
  2737. $time = vartrue($dateConvert[$ky]);
  2738. $timeStamp = strtotime($time);
  2739. $res = array($filter[1], $timeStamp);
  2740. break;
  2741. default:
  2742. //something like handleListUrlTypeFilter(); for custom handling of 'url_type' field name filters
  2743. $method = 'handle'.$this->getRequest()->getActionName().$this->getRequest()->camelize($filter[0]).'Filter';
  2744. if(method_exists($this, $method)) // callback handling
  2745. {
  2746. //return $this->$method($filter[1], $selected); selected?
  2747. // better approach - pass all values as method arguments
  2748. // NOTE - callbacks are allowed to return QUERY as a string, it'll be added in the WHERE clause
  2749. $args = array_slice($filter, 1);
  2750. e107::getMessage()->addDebug('Executing filter callback <strong>'.get_class($this).'::'.$method.'('.implode(', ', $args).')</strong>');
  2751. return call_user_func_array(array($this, $method), $args);
  2752. }
  2753. else // default handling
  2754. {
  2755. $res = array($filter[0], $filter[1]);
  2756. }
  2757. break;
  2758. }
  2759. //print_a($res);
  2760. //exit;
  2761. return $res;
  2762. }
  2763. /**
  2764. * Convert posted to model values after submit (based on field type)
  2765. * @param array $data
  2766. * @return void
  2767. */
  2768. protected function convertToData(&$data)
  2769. {
  2770. $model = new e_model($data);
  2771. foreach ($this->getFields() as $key => $attributes)
  2772. {
  2773. $value = vartrue($attributes['dataPath']) ? $model->getData($attributes['dataPath']) : $model->get($key);
  2774. if(null === $value)
  2775. {
  2776. continue;
  2777. }
  2778. switch($attributes['type'])
  2779. {
  2780. case 'password': //TODO more encryption options.
  2781. if(strlen($value) < 30) // expect a non-md5 value if less than 32 chars.
  2782. {
  2783. $value = md5($value);
  2784. }
  2785. break;
  2786. case 'datestamp':
  2787. if(!is_numeric($value))
  2788. {
  2789. if(vartrue($attributes['writeParms']))
  2790. {
  2791. parse_str($attributes['writeParms'],$opt);
  2792. }
  2793. $format = $opt['type'] ? ('input'.$opt['type']) : 'inputdate';
  2794. $value = trim($value) ? e107::getDate()->toTime($value, $format) : 0;
  2795. }
  2796. break;
  2797. case 'ip': // TODO - ask Steve if this check is required
  2798. //if(strpos($value, '.') !== FALSE)
  2799. {
  2800. $value = trim($value) ? e107::getIPHandler()->ipEncode($value) : '';
  2801. }
  2802. break;
  2803. case 'dropdown': // TODO - ask Steve if this check is required
  2804. case 'lanlist':
  2805. case 'userclasses':
  2806. case 'comma':
  2807. if(is_array($value))
  2808. {
  2809. // no sanitize here - data is added to model posted stack
  2810. // and validated & sanitized before sent to db
  2811. //$value = array_map(array(e107::getParser(), 'toDB'), $value);
  2812. $value = implode(',', $value);
  2813. }
  2814. break;
  2815. case 'images':
  2816. //XXX FIXME - entities in stored result.
  2817. break;
  2818. }
  2819. if(vartrue($attributes['dataPath']))
  2820. {
  2821. $model->setData($attributes['dataPath'], $value);
  2822. }
  2823. else
  2824. {
  2825. $model->set($key, $value);
  2826. }
  2827. }
  2828. $data = $model->getData();
  2829. unset($model);
  2830. $this->toData($data);
  2831. }
  2832. /**
  2833. * User defined method for converting POSTED to MODEL data
  2834. * @param array $data posted data
  2835. * @param string $type current action type - edit, create, list or user defined
  2836. * @return void
  2837. */
  2838. protected function toData(&$data, $type = '')
  2839. {
  2840. }
  2841. /**
  2842. * Take approproate action after successfull submit
  2843. *
  2844. * @param integer $id optional, needed only if redirect action is 'edit'
  2845. * @param string $noredirect_for don't redirect if action equals to its value
  2846. */
  2847. protected function doAfterSubmit($id = 0, $noredirect_for = '')
  2848. {
  2849. if(e_AJAX_REQUEST) return;
  2850. if($noredirect_for && $noredirect_for == $this->getPosted('__after_submit_action') && $noredirect_for == $this->getAction())
  2851. {
  2852. return;
  2853. }
  2854. $choice = $this->getPosted('__after_submit_action', 0);
  2855. switch ($choice) {
  2856. case 'create': // create
  2857. $this->redirectAction('create', 'id');
  2858. break;
  2859. case 'edit': // edit
  2860. $this->redirectAction('edit', '', 'id='.$id);
  2861. break;
  2862. case 'list': // list
  2863. $this->redirectAction('list', 'id');
  2864. break;
  2865. default:
  2866. $choice = explode('|', str_replace('{ID}', $id, $choice), 3);
  2867. $this->redirectAction(preg_replace('/[^\w-:.]/', '', $choice[0]), vartrue($choice[1]), vartrue($choice[2]));
  2868. break;
  2869. }
  2870. return;
  2871. }
  2872. /**
  2873. * Build ajax auto-complete filter response
  2874. * @return string response markup
  2875. */
  2876. protected function renderAjaxFilterResponse($listQry = '')
  2877. {
  2878. $debug = false;
  2879. $srch = $this->getPosted('searchquery');
  2880. $this->getRequest()->setQuery('searchquery', $srch); //_modifyListQry() is requiring GET String
  2881. $ret = '<ul>';
  2882. $ret .= '<li>'.$srch.'<span class="informal warning"> '.LAN_FILTER_LABEL_TYPED.'</span></li>'; // fix Enter - search for typed word only
  2883. $reswords = array();
  2884. if(trim($srch) !== '')
  2885. {
  2886. // Build query
  2887. $qry = $this->_modifyListQry(false, true, 0, 20, $listQry);
  2888. //file_put_contents(e_LOG.'uiAjaxResponseSQL.log', $qry."\n\n", FILE_APPEND);
  2889. // Make query
  2890. $sql = e107::getDb();
  2891. if($qry && $sql->db_Select_gen($qry, $debug))
  2892. {
  2893. while ($res = $sql->db_Fetch())
  2894. {
  2895. $tmp1 = array();
  2896. $tmp = array_values(preg_grep('#'.$srch.'#i', $res));
  2897. foreach ($tmp as $w)
  2898. {
  2899. if($w == $srch)
  2900. {
  2901. array_unshift($reswords, $w); //exact match
  2902. continue;
  2903. }
  2904. preg_match('#[\S]*('.$srch.')[\S]*#i', $w, $tmp1);
  2905. if($tmp1[0]) $reswords[] = $tmp1[0];
  2906. }
  2907. }
  2908. }
  2909. // Build response
  2910. $reswords = array_unique($reswords);
  2911. if($reswords)
  2912. {
  2913. $ret .= '<li>'.implode("</li>\n\t<li>", $reswords).'</li>';
  2914. }
  2915. }
  2916. $ret .= '<li><span class="informal warning"> '.LAN_FILTER_LABEL_CLEAR.' </span></li>'; // clear filter option
  2917. $ret .= '</ul>';
  2918. return $ret;
  2919. }
  2920. /**
  2921. * Given an alias such as 'u' or 'n.news_datestamp' - will return the associated table such as 'user' or 'news'
  2922. */
  2923. function getTableFromAlias($alias)
  2924. {
  2925. if(strpos($alias,".")!==false)
  2926. {
  2927. list($alias,$tmp) = explode(".",$alias,2);
  2928. }
  2929. $tmp = array_flip($this->joinAlias);
  2930. return vartrue($tmp[$alias]);
  2931. }
  2932. function getJoinField($field)
  2933. {
  2934. return vartrue($this->joinField[$field],false);
  2935. }
  2936. /**
  2937. * Parses all available field data, adds internal attributes for handling join requests
  2938. * @return e_admin_controller_ui
  2939. */
  2940. protected function parseAliases()
  2941. {
  2942. if($this->_alias_parsed) return $this; // already parsed!!!
  2943. $this->joinAlias(); // generate Table Aliases from listQry
  2944. if($this->getJoinData())
  2945. {
  2946. foreach ($this->getJoinData() as $table => $att)
  2947. {
  2948. if(strpos($table, '.') !== false)
  2949. {
  2950. $tmp = explode('.', $table, 2);
  2951. $this->setJoinData($table, null);
  2952. $att['alias'] = $tmp[0];
  2953. $att['table'] = $tmp[1];
  2954. $att['__tablePath'] = $att['alias'].'.';
  2955. $att['__tableFrom'] = '`#'.$att['table'].'` AS '.$att['alias'];
  2956. $this->setJoinData($att['alias'], $att);
  2957. unset($tmp);
  2958. continue;
  2959. }
  2960. $att['table'] = $table;
  2961. $att['alias'] = '';
  2962. $att['__tablePath'] = '`#'.$att['table'].'`.';
  2963. $att['__tableFrom'] = '`#'.$att['table'].'`';
  2964. $this->setJoinData($table, $att);
  2965. }
  2966. }
  2967. // check for table & field aliases
  2968. $fields = array(); // preserve order
  2969. foreach ($this->fields as $field => $att)
  2970. {
  2971. // fieldAlias.fieldName // table name no longer required as it's included in listQry. (see joinAlias() )
  2972. if(strpos($field, '.') !== false) // manually entered alias.
  2973. {
  2974. $tmp = explode('.', $field, 2);
  2975. $table = $this->getTableFromAlias($tmp[0]);
  2976. $att['table'] = $table;
  2977. $att['alias'] = $tmp[0];
  2978. $att['field'] = $tmp[1];
  2979. $att['__tableField'] = $field;
  2980. $att['__tablePath'] = $att['alias'].'.';
  2981. $att['__tableFrom'] = "`#".$table."`.".$tmp[1];//." AS ".$att['alias'];
  2982. $field = $att['alias'] ? $tmp[1] : $tmp[0];
  2983. $fields[$field] = $att;
  2984. unset($tmp);
  2985. }
  2986. else
  2987. {
  2988. $att['table'] = $this->getIfTableAlias(false);
  2989. if($newField = $this->getJoinField($field)) // Auto-Detect.
  2990. {
  2991. $table = $this->getTableFromAlias($newField); // Auto-Detect.
  2992. $att['table'] = $table;
  2993. $att['alias'] = $newField;
  2994. $att['__tableField'] = $newField;
  2995. // $att['__tablePath'] = $newField; ????!!!!!
  2996. $att['__tableFrom'] = "`#".$table."`.".$field;//." AS ".$newField;
  2997. }
  2998. elseif(isset($this->joinAlias[$this->table]) && $field !='checkboxes' && $field !='options')
  2999. {
  3000. $att['alias'] = $this->joinAlias[$this->table].".".$field;
  3001. }
  3002. else
  3003. {
  3004. $att['alias'] = "";
  3005. }
  3006. $att['field'] = $field;
  3007. $fields[$field] = $att;
  3008. }
  3009. if($fields[$field]['table'] == $this->getIfTableAlias(false))
  3010. {
  3011. $fields[$field]['__tableField'] = $att['alias'] ? $att['alias'] : $this->getIfTableAlias(true, true).'.'.$att['field'];
  3012. $fields[$field]['__tableFrom'] = $this->getIfTableAlias(true, true).'.'.$att['field'].($att['alias'] ? ' AS '.$att['alias'] : '');
  3013. }
  3014. else
  3015. {
  3016. // $fields[$field]['__tableField'] = $this->getJoinData($fields[$field]['table'], '__tablePath').$field;
  3017. }
  3018. /*
  3019. if($fields[$field]['table'])
  3020. {
  3021. if($fields[$field]['table'] == $this->getIfTableAlias(false))
  3022. {
  3023. $fields[$field]['__tableField'] = $att['alias'] ? $att['alias'] : $this->getIfTableAlias(true, true).'.'.$att['field'];
  3024. $fields[$field]['__tableFrom'] = $this->getIfTableAlias(true, true).'.'.$att['field'].($att['alias'] ? ' AS '.$att['alias'] : '');
  3025. }
  3026. else
  3027. {
  3028. $fields[$field]['__tableField'] = $this->getJoinData($fields[$field]['table'], '__tablePath').$field;
  3029. }
  3030. }
  3031. else
  3032. {
  3033. $fields[$field]['__tableField'] = '`'.$this->getTableName(false, true).'`.'.$field;
  3034. }
  3035. */
  3036. }
  3037. $this->fields = $fields;
  3038. $this->_alias_parsed = true;
  3039. return $this;
  3040. }
  3041. /**
  3042. * Intuitive LEFT JOIN Qry support. (preferred)
  3043. * Generate array of table names and their alias - auto-detected from listQry;
  3044. * eg. $listQry = "SELECT m.*, u.user_id,u.user_name FROM #core_media AS m LEFT JOIN #user AS u ON m.media_author = u.user_id";
  3045. */
  3046. protected function joinAlias()
  3047. {
  3048. if($this->listQry)
  3049. {
  3050. preg_match_all("/`?#([\w-]+)`?\s*(as|AS)\s*([\w-])/im",$this->listQry,$matches);
  3051. $keys = array();
  3052. foreach($matches[1] AS $k=>$v)
  3053. {
  3054. if(varset($matches[3][$k]))
  3055. {
  3056. $this->joinAlias[$v] = $matches[3][$k]; // array. eg $this->joinAlias['core_media'] = 'm';
  3057. }
  3058. $keys[] = $matches[3][$k];
  3059. }
  3060. foreach($keys as $alias)
  3061. {
  3062. preg_match_all("/".$alias."\.([\w]*)/i",$this->listQry,$match);
  3063. foreach($match[1] as $k=>$m)
  3064. {
  3065. $this->joinField[$m] = $match[0][$k];
  3066. }
  3067. }
  3068. }
  3069. elseif($this->tableJoin)
  3070. {
  3071. foreach ($this->tableJoin as $tbl => $data)
  3072. {
  3073. $matches = explode('.', $tbl, 2);
  3074. $this->joinAlias[$matches[1]] = $matches[0]; // array. eg $this->joinAlias['core_media'] = 'm';
  3075. //'user_name'=>'u.user_name'
  3076. if(isset($data['fields']) && $data['fields'] !== '*')
  3077. {
  3078. $tmp = explode(',', $data['fields']);
  3079. foreach ($tmp as $field)
  3080. {
  3081. $this->joinField[$field] = $matches[0].'.'.$field;
  3082. }
  3083. }
  3084. }
  3085. }
  3086. }
  3087. // TODO - abstract, array return type, move to parent?
  3088. protected function _modifyListQry($raw = false, $isfilter = false, $forceFrom = false, $forceTo = false, $listQry = '')
  3089. {
  3090. $searchQry = array();
  3091. $filterFrom = array();
  3092. $request = $this->getRequest();
  3093. $tp = e107::getParser();
  3094. $tablePath = $this->getIfTableAlias(true, true).'.';
  3095. $tableFrom = '`'.$this->getTableName(false, true).'`'.($this->getTableName(true) ? ' AS '.$this->getTableName(true) : '');
  3096. $tableSFieldsArr = array(); // FROM for main table
  3097. $tableSJoinArr = array(); // FROM for join tables
  3098. $filter = array();
  3099. $searchQuery = $tp->toDB($request->getQuery('searchquery', ''));
  3100. $searchFilter = $this->_parseFilterRequest($request->getQuery('filter_options', ''));
  3101. if($searchFilter && is_array($searchFilter))
  3102. {
  3103. list($filterField, $filterValue) = $searchFilter;
  3104. if($filterField && $filterValue !== '' && isset($this->fields[$filterField]))
  3105. {
  3106. $_type = $this->fields[$filterField]['data'];
  3107. if($this->fields[$filterField]['type'] === 'comma') $_type = 'set';
  3108. switch ($_type)
  3109. {
  3110. case 'set':
  3111. $searchQry[] = "FIND_IN_SET('".$tp->toDB($filterValue)."',".$this->fields[$filterField]['__tableField'].")";
  3112. break;
  3113. case 'int':
  3114. case 'integer':
  3115. if($this->fields[$filterField]['type'] == 'datestamp') // Past Month, Past Year etc.
  3116. {
  3117. $searchQry[] = $this->fields[$filterField]['__tableField']." > ".intval($filterValue);
  3118. }
  3119. else
  3120. {
  3121. $searchQry[] = $this->fields[$filterField]['__tableField']." = ".intval($filterValue);
  3122. }
  3123. break;
  3124. default:
  3125. if($this->fields[$filterField]['type'] == 'method') // More flexible filtering.
  3126. {
  3127. $searchQry[] = $this->fields[$filterField]['__tableField']." LIKE \"%".$tp->toDB($filterValue)."%\"";
  3128. }
  3129. else
  3130. {
  3131. $searchQry[] = $this->fields[$filterField]['__tableField']." = '".$tp->toDB($filterValue)."'";
  3132. }
  3133. //exit;
  3134. break;
  3135. }
  3136. }
  3137. //echo 'type= '. $this->fields[$filterField]['data'];
  3138. // print_a($this->fields[$filterField]);
  3139. }
  3140. elseif($searchFilter && is_string($searchFilter))
  3141. {
  3142. // filter callbacks could add to WHERE clause
  3143. $searchQry[] = $searchFilter;
  3144. }
  3145. // main table should select everything
  3146. $tableSFieldsArr[] = $tablePath.'*';
  3147. foreach($this->getFields() as $key => $var)
  3148. {
  3149. // disabled or system
  3150. if((vartrue($var['nolist']) && !vartrue($var['filter'])) || !vartrue($var['type']))
  3151. {
  3152. continue;
  3153. }
  3154. // select FROM... for main table
  3155. if(vartrue($var['alias']) && vartrue($var['__tableField']))
  3156. {
  3157. $tableSFieldsArr[] = $var['__tableField'];
  3158. }
  3159. // filter for WHERE and FROM clauses
  3160. $searchable_types = array('text', 'textarea', 'bbarea', 'email', 'int', 'integer', 'str', 'string'); //method? 'user',
  3161. if($var['type'] == 'method' && ($var['data'] == 'string' || $var['data'] == 'str'))
  3162. {
  3163. $searchable_types[] = 'method';
  3164. }
  3165. if(trim($searchQuery) !== '' && in_array($var['type'], $searchable_types) && $var['__tableField'])
  3166. {
  3167. if($var['type'] == 'int' || $var['type'] == 'integer')
  3168. {
  3169. if(is_numeric($searchQuery))
  3170. {
  3171. $filter[] = $var['__tableField']."=".$searchQuery;
  3172. }
  3173. continue;
  3174. }
  3175. $filter[] = $var['__tableField']." LIKE '%".$searchQuery."%'";
  3176. if($isfilter)
  3177. {
  3178. $filterFrom[] = $var['__tableField'];
  3179. }
  3180. }
  3181. }
  3182. if($isfilter)
  3183. {
  3184. if(!$filterFrom) return false;
  3185. $tableSFields = implode(', ', $filterFrom);
  3186. }
  3187. else
  3188. {
  3189. $tableSFields = $tableSFieldsArr ? implode(', ', $tableSFieldsArr) : $tablePath.'*';
  3190. }
  3191. $jwhere = array();
  3192. $joins = array();
  3193. //file_put_contents(e_LOG.'uiAjaxResponseSFields.log', $tableSFields."\n\n", FILE_APPEND);
  3194. //file_put_contents(e_LOG.'uiAjaxResponseFields.log', print_r($this->getFields(), true)."\n\n", FILE_APPEND);
  3195. if($this->getJoinData())
  3196. {
  3197. $qry = "SELECT SQL_CALC_FOUND_ROWS ".$tableSFields;
  3198. foreach ($this->getJoinData() as $jtable => $tparams)
  3199. {
  3200. // Select fields
  3201. if(!$isfilter)
  3202. {
  3203. $fields = vartrue($tparams['fields']);
  3204. if('*' === $fields)
  3205. {
  3206. $tableSJoinArr[] = "{$tparams['__tablePath']}*";
  3207. }
  3208. elseif($fields)
  3209. {
  3210. $tableSJoinArr[] = $fields;
  3211. /*$fields = explode(',', $fields);
  3212. foreach ($fields as $field)
  3213. {
  3214. $qry .= ", {$tparams['__tablePath']}`".trim($field).'`';
  3215. }*/
  3216. }
  3217. }
  3218. // Prepare Joins
  3219. $joins[] = "
  3220. ".vartrue($tparams['joinType'], 'LEFT JOIN')." {$tparams['__tableFrom']} ON ".(vartrue($tparams['leftTable']) ? $tparams['leftTable'].'.' : $tablePath)."`".vartrue($tparams['leftField'])."` = {$tparams['__tablePath']}`".vartrue($tparams['rightField'])."`".(vartrue($tparams['whereJoin']) ? ' '.$tparams['whereJoin'] : '');
  3221. // Prepare Where
  3222. if(vartrue($tparams['where']))
  3223. {
  3224. $jwhere[] = $tparams['where'];
  3225. }
  3226. }
  3227. //From
  3228. $qry .= $tableSJoinArr ? ', '.implode(', ', $tableSJoinArr)." FROM ".$tableFrom : " FROM ".$tableFrom;
  3229. // Joins
  3230. if(count($joins) > 0)
  3231. {
  3232. $qry .= "\n".implode("\n", $joins);
  3233. }
  3234. }
  3235. else
  3236. {
  3237. $qry = $listQry ? $listQry : "SELECT SQL_CALC_FOUND_ROWS ".$tableSFields." FROM ".$tableFrom;
  3238. }
  3239. // group field - currently auto-added only if there are joins
  3240. // TODO - groupField property
  3241. $groupField = '';
  3242. if($joins && $this->getPrimaryName())
  3243. {
  3244. $groupField = $tablePath.$this->getPrimaryName();
  3245. }
  3246. if($raw)
  3247. {
  3248. $rawData = array(
  3249. 'joinWhere' => $jwhere,
  3250. 'filter' => $filter,
  3251. 'listQrySql' => $this->listQrySql,
  3252. 'filterFrom' => $filterFrom,
  3253. 'search' => $searchQry,
  3254. 'tableFromName' => $tableFrom,
  3255. );
  3256. $rawData['tableFrom'] = $tableSFieldsArr;
  3257. $rawData['joinsFrom'] = $tableSJoinArr;
  3258. $rawData['joins'] = $joins;
  3259. $rawData['groupField'] = $groupField;
  3260. $rawData['orderField'] = isset($this->fields[$orderField]) ? $this->fields[$orderField]['__tableField'] : '';
  3261. $rawData['orderType'] = $request->getQuery('asc') == 'desc' ? 'DESC' : 'ASC';
  3262. $rawData['limitFrom'] = false === $forceFrom ? intval($request->getQuery('from', 0)) : intval($forceFrom);
  3263. $rawData['limitTo'] = false === $forceTo ? intval($this->getPerPage()) : intval($forceTo);
  3264. return $rawData;
  3265. }
  3266. // join where
  3267. if(count($jwhere) > 0)
  3268. {
  3269. $searchQry[] = " (".implode(" AND ",$jwhere)." )";
  3270. }
  3271. // filter where
  3272. if(count($filter) > 0)
  3273. {
  3274. $searchQry[] = " ( ".implode(" OR ",$filter)." ) ";
  3275. }
  3276. // more user added sql
  3277. if(isset($this->listQrySql['db_where']) && $this->listQrySql['db_where'])
  3278. {
  3279. if(is_array($this->listQrySql['db_where']))
  3280. {
  3281. $searchQry[] = implode(" AND ", $this->listQrySql['db_where']);
  3282. }
  3283. else
  3284. {
  3285. $searchQry[] = $this->listQrySql['db_where'];
  3286. }
  3287. }
  3288. // where query
  3289. if(count($searchQry) > 0)
  3290. {
  3291. // add more where details on the fly via $this->listQrySql['db_where'];
  3292. $qry .= (strripos($qry, 'where')==FALSE) ? " WHERE " : " AND "; // Allow 'where' in custom listqry
  3293. $qry .= implode(" AND ", $searchQry);
  3294. }
  3295. // GROUP BY if needed
  3296. if($groupField)
  3297. {
  3298. $qry .= ' GROUP BY '.$groupField;
  3299. }
  3300. // only when no custom order is required
  3301. if($this->listOrder && !$request->getQuery('field') && !$request->getQuery('asc'))
  3302. {
  3303. $qry .= ' ORDER BY '.$this->listOrder;
  3304. }
  3305. elseif(false !== $this->listOrder)
  3306. {
  3307. $orderField = $request->getQuery('field', $this->getDefaultOrderField());
  3308. $orderDef = (null === $request->getQuery('asc', null) ? $this->getDefaultOrder() : $request->getQuery('asc'));
  3309. if(isset($this->fields[$orderField]) && strpos($this->listQry,'ORDER BY')==FALSE) //override ORDER using listQry (admin->sitelinks)
  3310. {
  3311. // no need of sanitize - it's found in field array
  3312. $qry .= ' ORDER BY '.$this->fields[$orderField]['__tableField'].' '.(strtolower($orderDef) == 'desc' ? 'DESC' : 'ASC');
  3313. }
  3314. }
  3315. if(isset($this->filterQry)) // custom query on filter. (see downloads plugin)
  3316. {
  3317. $qry = $this->filterQry;
  3318. }
  3319. if($this->getPerPage() || false !== $forceTo)
  3320. {
  3321. $from = false === $forceFrom ? intval($request->getQuery('from', 0)) : intval($forceFrom);
  3322. if(false === $forceTo) $forceTo = $this->getPerPage();
  3323. $qry .= ' LIMIT '.$from.', '.intval($forceTo);
  3324. }
  3325. // Debug Filter Query.
  3326. // echo $qry.'<br />';
  3327. // print_a($_GET);
  3328. return $qry;
  3329. }
  3330. /**
  3331. * Manage submit item
  3332. * Note: $callbackBefore will break submission if returns false
  3333. *
  3334. * @param string $callbackBefore existing method from $this scope to be called before submit
  3335. * @param string $callbackAfter existing method from $this scope to be called after successfull submit
  3336. * @param string $noredirectAction passed to doAfterSubmit()
  3337. * @return boolean
  3338. */
  3339. protected function _manageSubmit($callbackBefore = '', $callbackAfter = '', $callbackError = '', $noredirectAction = '')
  3340. {
  3341. $model = $this->getModel();
  3342. $old_data = $model->getData();
  3343. $_posted = $this->getPosted();
  3344. $this->convertToData($_posted);
  3345. if($callbackBefore && method_exists($this, $callbackBefore))
  3346. {
  3347. $data = $this->$callbackBefore($_posted, $old_data, $model->getId());
  3348. if(false === $data)
  3349. {
  3350. // we don't wanna loose posted data
  3351. $model->setPostedData($_posted, null, false, false);
  3352. return false;
  3353. }
  3354. if($data && is_array($data))
  3355. {
  3356. // add to model data fields array if required
  3357. foreach ($data as $f => $val)
  3358. {
  3359. if($this->getFieldAttr($f, 'data'))
  3360. {
  3361. $model->setDataField($f, $this->getFieldAttr($f, 'data'));
  3362. }
  3363. }
  3364. $_posted = array_merge($_posted, $data);
  3365. }
  3366. }
  3367. // Scenario I - use request owned POST data - toForm already executed
  3368. $model->setPostedData($_posted, null, false, false)
  3369. ->save(true);
  3370. // Scenario II - inner model sanitize
  3371. //$this->getModel()->setPosted($this->convertToData($_POST, null, false, true);
  3372. // Take action based on use choice after success
  3373. if(!$this->getModel()->hasError())
  3374. {
  3375. // callback (if any)
  3376. if($callbackAfter && method_exists($this, $callbackAfter))
  3377. {
  3378. $this->$callbackAfter($model->getData(), $old_data, $model->getId());
  3379. }
  3380. $model->setMessages(true); //FIX - move messages (and session messages) to the default stack
  3381. $this->doAfterSubmit($model->getId(), $noredirectAction);
  3382. return true;
  3383. }
  3384. elseif($callbackError && method_exists($this, $callbackError))
  3385. {
  3386. // suppress messages if callback returns TRUE
  3387. if(true !== $this->$callbackError($_posted, $old_data, $model->getId()))
  3388. {
  3389. // Copy model messages to the default message stack
  3390. $model->setMessages();
  3391. }
  3392. return false;
  3393. }
  3394. // Copy model messages to the default message stack
  3395. $model->setMessages();
  3396. return false;
  3397. }
  3398. }
  3399. class e_admin_ui extends e_admin_controller_ui
  3400. {
  3401. protected $fieldTypes = array();
  3402. protected $dataFields = array();
  3403. protected $validationRules = array();
  3404. protected $table;
  3405. protected $pid;
  3406. protected $listQry;
  3407. protected $editQry;
  3408. /**
  3409. * Markup to be auto-inserted before List filter
  3410. * @var string
  3411. */
  3412. public $preFiliterMarkup = '';
  3413. /**
  3414. * Markup to be auto-inserted after List filter
  3415. * @var string
  3416. */
  3417. public $postFiliterMarkup = '';
  3418. /**
  3419. * Markup to be auto-inserted at the top of Create form
  3420. * @var string
  3421. */
  3422. public $headerCreateMarkup = '';
  3423. /**
  3424. * Markup to be auto-inserted at the bottom of Create form
  3425. * @var string
  3426. */
  3427. public $footerCreateMarkup = '';
  3428. /**
  3429. * Markup to be auto-inserted at the top of Update form
  3430. * @var string
  3431. */
  3432. public $headerUpdateMarkup = '';
  3433. /**
  3434. * Markup to be auto-inserted at the bottom of Update form
  3435. * @var string
  3436. */
  3437. public $footerUpdateMarkup = '';
  3438. /**
  3439. * Show confirm screen before (batch/single) delete
  3440. * @var boolean
  3441. */
  3442. public $deleteConfirmScreen = false;
  3443. /**
  3444. * Confirm screen custom message
  3445. * @var string
  3446. */
  3447. public $deleteConfirmMessage = null;
  3448. /**
  3449. * Constructor
  3450. * @param e_admin_request $request
  3451. * @param e_admin_response $response
  3452. * @param array $params [optional]
  3453. */
  3454. public function __construct($request, $response, $params = array())
  3455. {
  3456. $this->setDefaultAction($request->getDefaultAction());
  3457. $params['enable_triggers'] = true; // override
  3458. parent::__construct($request, $response, $params);
  3459. if(!$this->pluginName)
  3460. {
  3461. $this->pluginName = 'core';
  3462. }
  3463. $ufieldpref = $this->getUserPref();
  3464. if($ufieldpref)
  3465. {
  3466. $this->fieldpref = $ufieldpref;
  3467. }
  3468. $this->addTitle($this->pluginTitle, true)->parseAliases();
  3469. }
  3470. /**
  3471. * Catch fieldpref submit
  3472. * @return none
  3473. */
  3474. public function ListEcolumnsTrigger()
  3475. {
  3476. $this->setTriggersEnabled(false); //disable further triggering
  3477. parent::manageColumns();
  3478. }
  3479. /**
  3480. * Catch batch submit
  3481. * @param string $batch_trigger
  3482. * @return none
  3483. */
  3484. public function ListBatchTrigger($batch_trigger)
  3485. {
  3486. $this->setPosted('etrigger_batch', null);
  3487. if($this->getPosted('etrigger_cancel'))
  3488. {
  3489. $this->setPosted(array());
  3490. return; // always break on cancel!
  3491. }
  3492. $this->deleteConfirmScreen = true; // Confirm screen ALWAYS enabled when multi-deleting!
  3493. // proceed ONLY if there is no other trigger, except delete confirmation
  3494. if($batch_trigger && !$this->hasTrigger(array('etrigger_delete_confirm'))) $this->_handleListBatch($batch_trigger);
  3495. }
  3496. /**
  3497. * Batch delete trigger
  3498. * @param array $selected
  3499. * @return void
  3500. */
  3501. protected function handleListDeleteBatch($selected)
  3502. {
  3503. $tp = e107::getParser();
  3504. if(!$this->getBatchDelete())
  3505. {
  3506. e107::getMessage()->add(LAN_UI_BATCHDEL_ERROR, E_MESSAGE_WARNING);
  3507. return;
  3508. }
  3509. if($this->deleteConfirmScreen)
  3510. {
  3511. if(!$this->getPosted('etrigger_delete_confirm'))
  3512. {
  3513. // ListPage will show up confirmation screen
  3514. $this->setPosted('delete_confirm_value', implode(',', $selected));
  3515. return;
  3516. }
  3517. else
  3518. {
  3519. // already confirmed, resurrect selected values
  3520. $selected = explode(',', $this->getPosted('delete_confirm_value'));
  3521. foreach ($selected as $i => $_sel)
  3522. {
  3523. $selected[$i] = preg_replace('/[^\w-:.]/', '', $_sel);
  3524. }
  3525. }
  3526. }
  3527. // delete one by one - more control, less performance
  3528. // pass afterDelete() callback to tree delete method
  3529. $set_messages = true;
  3530. $delcount = 0;
  3531. $nfcount = 0;
  3532. foreach ($selected as $id)
  3533. {
  3534. $data = array();
  3535. $model = $this->getTreeModel()->getNode($id);
  3536. if($model)
  3537. {
  3538. $data = $model->getData();
  3539. if($this->beforeDelete($data, $id))
  3540. {
  3541. $check = $this->getTreeModel()->delete($id);
  3542. if($check) $delcount++;
  3543. if(!$this->afterDelete($data, $id, $check))
  3544. {
  3545. $set_messages = false;
  3546. }
  3547. }
  3548. }
  3549. else
  3550. {
  3551. $set_messages = true;
  3552. $nfcount++;
  3553. }
  3554. }
  3555. //$this->getTreeModel()->delete($selected);
  3556. if($set_messages)
  3557. {
  3558. $this->getTreeModel()->setMessages();
  3559. // FIXME lan
  3560. if($delcount) e107::getMessage()->addSuccess($tp->lanVars('[x] record(s) successfully deleted.', $delcount, true));
  3561. if($nfcount) e107::getMessage()->addError($tp->lanVars('[x] records not found and not deleted.', $nfcount,true));
  3562. }
  3563. //$this->redirect();
  3564. }
  3565. /** TODO
  3566. * Batch copy trigger
  3567. * @param array $selected
  3568. * @return void
  3569. */
  3570. protected function handleListCopyBatch($selected)
  3571. {
  3572. // Batch Copy
  3573. $res = $this->getTreeModel()->copy($selected);
  3574. // callback
  3575. $this->afterCopy($res, $selected);
  3576. // move messages to default stack
  3577. $this->getTreeModel()->setMessages();
  3578. // send messages to session
  3579. e107::getMessage()->moveToSession();
  3580. // redirect
  3581. $this->redirect();
  3582. }
  3583. /**
  3584. * Batch URL trigger
  3585. * @param array $selected
  3586. * @return void
  3587. */
  3588. protected function handleListUrlBatch($selected)
  3589. {
  3590. if($this->_add2nav($selected))
  3591. {
  3592. e107::getMessage()->moveToSession();
  3593. $this->redirect();
  3594. }
  3595. }
  3596. /** TODO
  3597. * Batch Featurebox Transfer
  3598. * @param array $selected
  3599. * @return void
  3600. */
  3601. protected function handleListFeatureboxBatch($selected)
  3602. {
  3603. if($this->_add2featurebox($selected))
  3604. {
  3605. e107::getMessage()->moveToSession();
  3606. $this->redirect();
  3607. }
  3608. }
  3609. protected function _add2nav($selected)
  3610. {
  3611. if(empty($selected)) return false;// TODO warning message
  3612. if(!is_array($selected)) $selected = array($selected);
  3613. $sql = e107::getDb();
  3614. $urlData = $this->getUrl();
  3615. $allData = $this->getTreeModel()->url($selected, array('sc' => true), true);
  3616. e107::getMessage()->addDebug('Using Url Route:'.$urlData['route']);
  3617. $scount = 0;
  3618. foreach($allData as $id => $data)
  3619. {
  3620. $name = $data['name'];
  3621. $desc = $data['description'];
  3622. $link = $data['url'];
  3623. $link = str_replace('{e_BASE}', "", $link); // TODO temporary here, discuss
  3624. // _FIELD_TYPES auto created inside mysql handler now
  3625. $linkArray = array(
  3626. 'link_name' => $name,
  3627. 'link_url' => $link,
  3628. 'link_description' => e107::getParser()->toDB($desc), // retrieved field type is string, we might need todb here
  3629. 'link_button' => '',
  3630. 'link_category' => 255, // Using an unassigned template rather than inactive link-class, since other inactive links may already exist.
  3631. 'link_order' => 0,
  3632. 'link_parent' => 0,
  3633. 'link_open' => '',
  3634. 'link_class' => 0,
  3635. 'link_sefurl' => e107::getParser()->toDB($urlData['route'].'?'.$id),
  3636. );
  3637. $res = $sql->insert('links', $linkArray);
  3638. // FIXME lans
  3639. if($res !== FALSE)
  3640. {
  3641. e107::getMessage()->addSuccess('Created Sitelink: <b>'.($name ? $name : 'n/a')."</b>");
  3642. $scount++;
  3643. }
  3644. else
  3645. {
  3646. if($sql->getLastErrorNumber())
  3647. {
  3648. e107::getMessage()->addError('SQL Link Creation Error'); //TODO - Lan
  3649. e107::getMessage()->addDebug('SQL Link Creation Error #'.$sql->getLastErrorNumber().': '.$sql->getLastErrorText());
  3650. }
  3651. else
  3652. {
  3653. e107::getMessage()->addError('Unknown error: <b>'.$name."</b> not added");
  3654. }
  3655. }
  3656. }
  3657. if($scount > 0)
  3658. {
  3659. e107::getMessage()->addSuccess("<br /><strong>{$scount}</strong> new sitelinks were added but are currently unassigned. You should now modify these links to your liking.<br /><br /><a class='btn btn-small btn-primary' href='".e_ADMIN_ABS."links.php?searchquery=&filter_options=link_category__255'>Modify Links</a>");
  3660. return $scount;
  3661. }
  3662. return false;
  3663. }
  3664. protected function _add2featurebox($selected)
  3665. {
  3666. // FIX - don't allow if plugin not installed
  3667. if(!e107::isInstalled('featurebox'))
  3668. {
  3669. return false;
  3670. }
  3671. if(empty($selected)) return false;// TODO warning message
  3672. if(!is_array($selected)) $selected = array($selected);
  3673. $sql = e107::getDb();
  3674. $tree = $this->getTreeModel();
  3675. $urlData = $this->getTreeModel()->url($selected, array('sc' => true), false);
  3676. $data = $this->featurebox;
  3677. $scount = 0;
  3678. foreach($selected as $id)
  3679. {
  3680. if(!$tree->hasNode($id))
  3681. {
  3682. e107::getMessage()->addError('Item #ID '.htmlspecialchars($id).' not found.');
  3683. continue; // TODO message
  3684. }
  3685. $model = $tree->getNode($id);
  3686. if($data['url'] === true)
  3687. {
  3688. $url = $urlData[$id];
  3689. }
  3690. else $url = $model->get($data['url']);
  3691. $name = $model->get($data['name']);
  3692. $category = e107::getDb()->retrieve('featurebox_category', 'fb_category_id', "fb_category_template='unassigned'");
  3693. $fbArray = array (
  3694. 'fb_title' => $name,
  3695. 'fb_text' => $model->get($data['description']),
  3696. 'fb_image' => vartrue($data['image']) ? $model->get($data['image']) : '',
  3697. 'fb_imageurl' => $url,
  3698. 'fb_class' => isset($data['visibility']) && $data['visibility'] !== false ? $model->get($data['visibility']) : e_UC_ADMIN,
  3699. 'fb_template' => 'default',
  3700. 'fb_category' => $category, // TODO popup - choose category
  3701. 'fb_order' => $scount,
  3702. );
  3703. $res = $sql->insert('featurebox', $fbArray);
  3704. // FIXME lans
  3705. if($res !== FALSE)
  3706. {
  3707. e107::getMessage()->addSuccess('Created Featurebox Item: <b>'.($name ? $name : 'n/a')."</b>");
  3708. $scount++;
  3709. }
  3710. else
  3711. {
  3712. if($sql->getLastErrorNumber())
  3713. {
  3714. e107::getMessage()->addError('SQL Featurebox Creation Error'); //TODO - Lan
  3715. e107::getMessage()->addDebug('SQL Featurebox Creation Error #'.$sql->getLastErrorNumber().': '.$sql->getLastErrorText());
  3716. }
  3717. else
  3718. {
  3719. e107::getMessage()->addError('Unknown error: <b>'.$name."</b> not added");
  3720. }
  3721. }
  3722. }
  3723. if($scount > 0)
  3724. {
  3725. e107::getMessage()->addSuccess("<br /><strong>{$scount}</strong> new featurebox items were added but are currently unassigned. You should now modify these items to your liking.<br /><br /><a class='btn btn-small btn-primary' href='".e_PLUGIN_ABS."featurebox/admin_config.php?searchquery=&filter_options=fb_category__{$category}'>Modify Featurebox Items</a>");
  3726. return $scount;
  3727. }
  3728. return false;
  3729. }
  3730. /**
  3731. * Batch boolean trigger
  3732. * @param array $selected
  3733. * @return void
  3734. */
  3735. protected function handleListBoolBatch($selected, $field, $value)
  3736. {
  3737. $cnt = $this->getTreeModel()->update($field, $value, $selected, $value, false);
  3738. if($cnt)
  3739. {
  3740. $caption = e107::getParser()->lanVars(LAN_UI_BATCH_BOOL_SUCCESS, $cnt, true);
  3741. $this->getTreeModel()->addMessageSuccess($caption);
  3742. }
  3743. $this->getTreeModel()->setMessages();
  3744. }
  3745. /**
  3746. * Batch boolean reverse trigger
  3747. * @param array $selected
  3748. * @return void
  3749. */
  3750. protected function handleListBoolreverseBatch($selected, $field, $value)
  3751. {
  3752. $tree = $this->getTreeModel();
  3753. $cnt = $tree->update($field, "1-{$field}", $selected, null, false);
  3754. if($cnt)
  3755. {
  3756. $caption = e107::getParser()->lanVars(LAN_UI_BATCH_REVERSED_SUCCESS, $cnt, true);
  3757. $tree->addMessageSuccess($caption);
  3758. //sync models
  3759. $tree->load(true);
  3760. }
  3761. $this->getTreeModel()->setMessages();
  3762. }
  3763. public function handleCommaBatch($selected, $field, $value, $type)
  3764. {
  3765. $tree = $this->getTreeModel();
  3766. $cnt = $rcnt = 0;
  3767. $value = e107::getParser()->toDb($value);
  3768. switch ($type)
  3769. {
  3770. case 'attach':
  3771. case 'deattach':
  3772. $this->_setModel();
  3773. foreach ($selected as $key => $id)
  3774. {
  3775. $node = $tree->getNode($id);
  3776. if(!$node) continue;
  3777. $val = $node->get($field);
  3778. if(empty($val)) $val = array();
  3779. elseif(!is_array($val)) $val = explode(',', $val);
  3780. if($type === 'deattach')
  3781. {
  3782. $search = array_search($value, $val);
  3783. if(false === $search) continue;
  3784. unset($val[$search]);
  3785. sort($val);
  3786. $val = implode(',', $val);
  3787. $node->set($field, $val);
  3788. $check = $this->getModel()->setData($node->getData())->save(false, true);
  3789. if(false === $check) $this->getModel()->setMessages();
  3790. else $rcnt++;
  3791. continue;
  3792. }
  3793. // attach it
  3794. if(false === in_array($value, $val))
  3795. {
  3796. $val[] = $value;
  3797. sort($val);
  3798. $val = implode(',', array_unique($val));
  3799. $node->set($field, $val);
  3800. $check = $this->getModel()->setData($node->getData())->save(false, true);
  3801. if(false === $check) $this->getModel()->setMessages();
  3802. else $cnt++;
  3803. }
  3804. }
  3805. $this->_model = null;
  3806. break;
  3807. case 'addAll':
  3808. if(!empty($value))
  3809. {
  3810. if(is_array($value))
  3811. {
  3812. sort($value);
  3813. $value = implode(',', array_map('trim', $value));
  3814. }
  3815. $cnt = $this->getTreeModel()->update($field, $value, $selected, true, true);
  3816. }
  3817. else
  3818. {
  3819. // TODO lan
  3820. $this->getTreeModel()->addMessageWarning("Comma list is empty, aborting.")->setMessages();
  3821. }
  3822. break;
  3823. case 'clearAll':
  3824. $allowed = !is_array($value) ? explode(',', $value) : $value;
  3825. if(!$allowed)
  3826. {
  3827. $rcnt = $this->getTreeModel()->update($field, '', $selected, '', true);
  3828. }
  3829. else
  3830. {
  3831. $this->_setModel();
  3832. foreach ($selected as $key => $id)
  3833. {
  3834. $node = $tree->getNode($id);
  3835. if(!$node) continue;
  3836. $val = $node->get($field);
  3837. // nothing to do
  3838. if(empty($val)) break;
  3839. elseif(!is_array($val)) $val = explode(',', $val);
  3840. // remove only allowed, see userclass
  3841. foreach ($val as $_k => $_v)
  3842. {
  3843. if(in_array($_v, $allowed))
  3844. {
  3845. unset($val[$_k]);
  3846. }
  3847. }
  3848. sort($val);
  3849. $val = !empty($val) ? implode(',', $val) : '';
  3850. $node->set($field, $val);
  3851. $check = $this->getModel()->setData($node->getData())->save(false, true);
  3852. if(false === $check) $this->getModel()->setMessages();
  3853. else $rcnt++;
  3854. }
  3855. $this->_model = null;
  3856. }
  3857. // format for proper message
  3858. $value = implode(',', $allowed);
  3859. break;
  3860. }
  3861. if($cnt)
  3862. {
  3863. $vttl = $this->getUI()->renderValue($field, $value, $this->getFieldAttr($field));
  3864. $caption = e107::getParser()->lanVars(LAN_UI_BATCH_UPDATE_SUCCESS, array('x'=>$vttl, 'y'=>$cnt), true);
  3865. $this->getTreeModel()->addMessageSuccess($caption);
  3866. }
  3867. elseif($rcnt)
  3868. {
  3869. $vttl = $this->getUI()->renderValue($field, $value, $this->getFieldAttr($field));
  3870. $caption = e107::getParser()->lanVars(LAN_UI_BATCH_DEATTACH_SUCCESS, array('x'=>$vttl, 'y'=>$cnt), true);
  3871. $this->getTreeModel()->addMessageSuccess($caption);
  3872. }
  3873. $this->getTreeModel()->setMessages();
  3874. }
  3875. /**
  3876. * Batch default (field) trigger
  3877. * @param array $selected
  3878. * @return void
  3879. */
  3880. protected function handleListBatch($selected, $field, $value)
  3881. {
  3882. // special exceptions
  3883. if($value == '#delete') // see admin->users
  3884. {
  3885. $val = "''";
  3886. $value = "(empty)";
  3887. }
  3888. elseif($value == "#null")
  3889. {
  3890. $val = null;
  3891. $value = "(empty)";
  3892. }
  3893. else
  3894. {
  3895. $val = "'".$value."'";
  3896. }
  3897. if($field == 'options') // reserved field type. see: admin -> media-manager - batch rotate image.
  3898. {
  3899. return;
  3900. }
  3901. $cnt = $this->getTreeModel()->update($field, $val, $selected, true, false);
  3902. if($cnt)
  3903. {
  3904. $vttl = $this->getUI()->renderValue($field, $value, $this->getFieldAttr($field));
  3905. $msg = e107::getParser()->lanVars(LAN_UI_BATCH_UPDATE_SUCCESS, array('x' => $vttl, 'y' => $cnt), true);
  3906. $this->getTreeModel()->addMessageSuccess($msg);
  3907. // force reload the collection from DB, fix some issues as 'observer' is executed before the batch handler
  3908. $this->getTreeModel()->setParam('db_query', $this->_modifyListQry(false, false, false, false, $this->listQry))->load(true);
  3909. }
  3910. $this->getTreeModel()->setMessages();
  3911. return $cnt;
  3912. }
  3913. /**
  3914. * Catch delete submit
  3915. * @param string $batch_trigger
  3916. * @return none
  3917. */
  3918. public function ListDeleteTrigger($posted)
  3919. {
  3920. if($this->getPosted('etrigger_cancel'))
  3921. {
  3922. $this->setPosted(array());
  3923. return; // always break on cancel!
  3924. }
  3925. $id = intval(key($posted));
  3926. if($this->deleteConfirmScreen && !$this->getPosted('etrigger_delete_confirm'))
  3927. {
  3928. // forward data to delete confirm screen
  3929. $this->setPosted('delete_confirm_value', $id);
  3930. return; // User confirmation expected
  3931. }
  3932. $this->setTriggersEnabled(false);
  3933. $data = array();
  3934. $model = $this->getTreeModel()->getNode($id); //FIXME - this has issues with being on a page other than the 1st.
  3935. if($model)
  3936. {
  3937. $data = $model->getData();
  3938. if($this->beforeDelete($data, $id))
  3939. {
  3940. $check = $this->getTreeModel()->delete($id);
  3941. if($this->afterDelete($data, $id, $check))
  3942. {
  3943. $this->getTreeModel()->setMessages();
  3944. }
  3945. }
  3946. else
  3947. {
  3948. $this->getTreeModel()->setMessages();// errors
  3949. }
  3950. }
  3951. else //FIXME - this is a fall-back for the BUG which causes model to fail on all list pages other than the 1st
  3952. {
  3953. //echo "Couldn't get Node for ID: ".$id;
  3954. // exit;
  3955. $check = $this->getTreeModel()->delete($id);
  3956. return;
  3957. }
  3958. }
  3959. /**
  3960. * User defined pre-delete logic
  3961. */
  3962. public function beforeDelete($data, $id)
  3963. {
  3964. return true;
  3965. }
  3966. /**
  3967. * User defined after-delete logic
  3968. */
  3969. public function afterDelete($deleted_data, $id, $deleted_check)
  3970. {
  3971. return true;
  3972. }
  3973. /**
  3974. * List action header
  3975. * @return void
  3976. */
  3977. public function ListHeader()
  3978. {
  3979. //e107::js('core','core/tabs.js','prototype');
  3980. //e107::js('core','core/admin.js','prototype');
  3981. }
  3982. /**
  3983. * List action observer
  3984. * @return void
  3985. */
  3986. public function ListObserver()
  3987. {
  3988. $this->getTreeModel()->setParam('db_query', $this->_modifyListQry(false, false, false, false, $this->listQry))->load();
  3989. $this->addTitle();
  3990. }
  3991. /**
  3992. * Filter response ajax page
  3993. * @return string
  3994. */
  3995. public function FilterAjaxPage()
  3996. {
  3997. return $this->renderAjaxFilterResponse($this->listQry); //listQry will be used only if available
  3998. }
  3999. /**
  4000. * Inline edit action
  4001. * @return void
  4002. */
  4003. public function InlineAjaxPage()
  4004. {
  4005. $this->logajax('Field not found');
  4006. $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
  4007. if(!vartrue($_POST['name']) || !vartrue($this->fields[$_POST['name']]))
  4008. {
  4009. header($protocol.': 404 Not Found', true, 404);
  4010. header("Status: 404 Not Found", true, 404);
  4011. echo 'Field not found'; // FIXME lan
  4012. $this->logajax('Field not found');
  4013. return;
  4014. }
  4015. $_name = $_POST['name'];
  4016. $_value = $_POST['value'];
  4017. $parms = $this->fields[$_name]['readParms'] ? $this->fields[$_name]['readParms'] : '';
  4018. if(!is_array($parms)) parse_str($parms, $parms);
  4019. if(vartrue($parms['editable'])) $this->fields[$_name]['inline'] = true;
  4020. if(vartrue($this->fields[$_name]['noedit']) || vartrue($this->fields[$_name]['nolist']) || !vartrue($this->fields[$_name]['inline']))
  4021. {
  4022. header($protocol.': 403 Forbidden', true, 403);
  4023. header("Status: 403 Forbidden", true, 403);
  4024. echo 'Forbidden'; // FIXME lan
  4025. $this->logajax("Forbidden");
  4026. return;
  4027. }
  4028. $this->logajax("OK?");
  4029. $model = $this->getModel()->load($this->getId());
  4030. $_POST = array(); //reset post
  4031. $_POST[$_name] = $_value; // set current field only
  4032. // generic handler - same as regular edit form submit
  4033. $this->convertToData($_POST);
  4034. $model->setPostedData($_POST, null, false, false)
  4035. ->setParam('validateAvailable', true) // new param to control validate of available data only, reset on validate event
  4036. ->update(true);
  4037. //$res = $this->_manageSubmit('beforeUpdate', 'afterUpdate', 'onUpdateError', 'edit');
  4038. if($model->hasError())
  4039. {
  4040. // using 400
  4041. header($protocol.': 400 Bad Request', true, 400);
  4042. header("Status: 400 Bad Request", true, 400);
  4043. $this->logajax("Bad Request");
  4044. // DEBUG e107::getMessage()->addError('Error test.', $model->getMessageStackName())->addError('Another error test.', $model->getMessageStackName());
  4045. if(E107_DEBUG_LEVEL) $message = e107::getMessage()->get('debug', $model->getMessageStackName(), true);
  4046. else $message = e107::getMessage()->get('error', $model->getMessageStackName(), true);
  4047. if(!empty($message)) echo implode(' ', $message);
  4048. return;
  4049. }
  4050. }
  4051. // Temporary - but useful. :-)
  4052. public function logajax($message)
  4053. {
  4054. return;
  4055. $message = date('r')."\n".$message."\n";
  4056. $message .= print_r($_POST,true);
  4057. $message .= print_r($_GET,true);
  4058. $message .= "---------------";
  4059. file_put_contents(e_LOG.'uiAjaxResponseInline.log', $message."\n\n", FILE_APPEND);
  4060. }
  4061. /**
  4062. * Drag-n-Drop sort action
  4063. * @return void
  4064. */
  4065. public function SortAjaxPage()
  4066. {
  4067. if(!isset($_POST['all']) || empty($_POST['all']))
  4068. {
  4069. return;
  4070. }
  4071. if(!$this->sortField)
  4072. {
  4073. echo 'Missing sort field value';
  4074. return;
  4075. }
  4076. $sql = e107::getDb();
  4077. $c = ($_GET['from']) ? intval($_GET['from']) : 0;
  4078. $updated = array();
  4079. $step = $this->orderStep ? intval($this->orderStep) : 1;
  4080. foreach($_POST['all'] as $row)
  4081. {
  4082. list($tmp,$id) = explode("-", $row, 2);
  4083. $id = preg_replace('/[^\w-:.]/', '', $id);
  4084. if(!is_numeric($id)) $id = "'{$id}'";
  4085. if($sql->db_Update($this->table, $this->sortField." = {$c} WHERE ".$this->pid." = ".$id))
  4086. {
  4087. $updated[] = $id;
  4088. }
  4089. // echo($sql->getLastQuery()."\n");
  4090. $c += $step;
  4091. }
  4092. //echo "Updated ".implode(",",$updated);
  4093. }
  4094. /**
  4095. * Generic List action page
  4096. * @return string
  4097. */
  4098. public function ListPage()
  4099. {
  4100. if($this->deleteConfirmScreen && !$this->getPosted('etrigger_delete_confirm') && $this->getPosted('delete_confirm_value'))
  4101. {
  4102. // 'edelete_confirm_data' set by single/batch delete trigger
  4103. return $this->getUI()->getConfirmDelete($this->getPosted('delete_confirm_value')); // User confirmation expected
  4104. }
  4105. return $this->getUI()->getList();
  4106. }
  4107. /**
  4108. * List action observer
  4109. * @return void
  4110. */
  4111. public function ListAjaxObserver()
  4112. {
  4113. $this->getTreeModel()->setParam('db_query', $this->_modifyListQry(false, false, 0, false, $this->listQry))->load();
  4114. }
  4115. /**
  4116. * Generic List action page (Ajax)
  4117. * @return string
  4118. */
  4119. public function ListAjaxPage()
  4120. {
  4121. return $this->getUI()->getList(true);
  4122. }
  4123. /**
  4124. * Generic Edit observer
  4125. */
  4126. public function EditObserver()
  4127. {
  4128. $this->getModel()->load($this->getId());
  4129. $this->addTitle();
  4130. }
  4131. /**
  4132. * Generic Create submit trigger
  4133. */
  4134. public function EditCancelTrigger()
  4135. {
  4136. $this->redirectAction('list', 'id');
  4137. }
  4138. /**
  4139. * Generic Edit submit trigger
  4140. */
  4141. public function EditSubmitTrigger()
  4142. {
  4143. $this->_manageSubmit('beforeUpdate', 'afterUpdate', 'onUpdateError', 'edit');
  4144. }
  4145. /**
  4146. * Edit - send JS to page Header
  4147. * @return none
  4148. */
  4149. function EditHeader()
  4150. {
  4151. // e107::getJs()->requireCoreLib('core/admin.js');
  4152. e107::js('core','core/admin.js','prototype');
  4153. }
  4154. /**
  4155. * Generic Edit page
  4156. * @return string
  4157. */
  4158. public function EditPage()
  4159. {
  4160. return $this->CreatePage();
  4161. }
  4162. /**
  4163. * Generic Create observer
  4164. * @return string
  4165. */
  4166. public function CreateObserver()
  4167. {
  4168. $this->setTriggersEnabled(true);
  4169. $this->addTitle();
  4170. }
  4171. /**
  4172. * Generic Create submit trigger
  4173. */
  4174. public function CreateCancelTrigger()
  4175. {
  4176. $this->redirectAction('list', 'id');
  4177. }
  4178. /**
  4179. * Generic Create submit trigger
  4180. */
  4181. public function CreateSubmitTrigger()
  4182. {
  4183. $this->_manageSubmit('beforeCreate', 'afterCreate', 'onCreateError');
  4184. }
  4185. /**
  4186. * User defined pre-create logic, return false to prevent DB query execution
  4187. * @param $new_data
  4188. * @param $old_data
  4189. */
  4190. public function beforeCreate($new_data, $old_data)
  4191. {
  4192. }
  4193. /**
  4194. * User defined after-create logic
  4195. * @param $new_data
  4196. * @param $old_data
  4197. * @param $id
  4198. */
  4199. public function afterCreate($new_data, $old_data, $id)
  4200. {
  4201. }
  4202. /**
  4203. * User defined error handling, return true to suppress model messages
  4204. * @param $new_data
  4205. * @param $old_data
  4206. */
  4207. public function onCreateError($new_data, $old_data)
  4208. {
  4209. }
  4210. /**
  4211. * User defined pre-update logic, return false to prevent DB query execution
  4212. * @param $new_data
  4213. * @param $old_data
  4214. */
  4215. public function beforeUpdate($new_data, $old_data, $id)
  4216. {
  4217. }
  4218. /**
  4219. * User defined after-update logic
  4220. * @param $new_data
  4221. * @param $old_data
  4222. */
  4223. public function afterUpdate($new_data, $old_data, $id)
  4224. {
  4225. }
  4226. /**
  4227. * User defined error handling, return true to suppress model messages
  4228. */
  4229. public function onUpdateError($new_data, $old_data, $id)
  4230. {
  4231. }
  4232. /**
  4233. * User defined after-update logic
  4234. * @param mixed $result
  4235. * @param array $selected
  4236. * @return void
  4237. */
  4238. public function afterCopy($result, $selected)
  4239. {
  4240. }
  4241. /**
  4242. * Create - send JS to page Header
  4243. * @return none
  4244. */
  4245. function CreateHeader()
  4246. {
  4247. // TODO - invoke it on className (not all textarea elements)
  4248. //e107::getJs()->requireCoreLib('core/admin.js');
  4249. e107::js('core','core/admin.js','prototype');
  4250. }
  4251. /**
  4252. *
  4253. * @return TBD
  4254. */
  4255. public function CreatePage()
  4256. {
  4257. return $this->getUI()->getCreate();
  4258. }
  4259. public function PrefsSaveTrigger()
  4260. {
  4261. $this->getConfig()
  4262. ->setPostedData($this->getPosted(), null, false, false)
  4263. //->setPosted('not_existing_pref_test', 1)
  4264. ->save(true);
  4265. $this->getConfig()->setMessages();
  4266. }
  4267. public function PrefsObserver()
  4268. {
  4269. $this->addTitle();
  4270. }
  4271. public function PrefsPage()
  4272. {
  4273. return $this->getUI()->getSettings();
  4274. }
  4275. /**
  4276. * Parent overload
  4277. * @return e_admin_ui
  4278. */
  4279. protected function parseAliases()
  4280. {
  4281. // parse table
  4282. if(strpos($this->table, '.') !== false)
  4283. {
  4284. $tmp = explode('.', $this->table, 2);
  4285. $this->table = $tmp[1];
  4286. $this->tableAlias = $tmp[0];
  4287. unset($tmp);
  4288. }
  4289. parent::parseAliases();
  4290. return $this;
  4291. }
  4292. public function getPrimaryName()
  4293. {
  4294. // Option for working with tables having no PID
  4295. if(!varset($this->pid) && vartrue($this->fields) && false !== $this->pid)
  4296. {
  4297. $message = e107::getParser()->toHtml(LAN_UI_NOPID_ERROR,true);
  4298. e107::getMessage()->add($message, E_MESSAGE_WARNING);
  4299. }
  4300. return $this->pid;
  4301. }
  4302. public function getTableName($alias = false, $prefix = false)
  4303. {
  4304. if($alias) return ($this->tableAlias ? $this->tableAlias : '');
  4305. return ($prefix ? '#' : '').$this->table;
  4306. }
  4307. /**
  4308. * Validation rules retrieved from controller object
  4309. * @return array
  4310. */
  4311. public function getValidationRules()
  4312. {
  4313. return $this->validationRules;
  4314. }
  4315. /**
  4316. * Data Field array retrieved from controller object
  4317. * @return array
  4318. */
  4319. public function getDataFields()
  4320. {
  4321. return $this->dataFields;
  4322. }
  4323. /**
  4324. * Set read and write parms with drop-down-list array data (ie. type='dropdown')
  4325. * @param str $field
  4326. * @param array $array [optional]
  4327. * @return none
  4328. */
  4329. public function setDropDown($field,$array) //TODO Have Miro check this.
  4330. {
  4331. $this->fields[$field]['readParms'] = $array;
  4332. $this->fields[$field]['writeParms'] = $array;
  4333. }
  4334. /**
  4335. * Set Config object
  4336. * @return e_admin_ui
  4337. */
  4338. protected function _setConfig()
  4339. {
  4340. $this->_pref = $this->pluginName == 'core' ? e107::getConfig() : e107::getPlugConfig($this->pluginName);
  4341. $dataFields = $validateRules = array();
  4342. foreach ($this->prefs as $key => $att)
  4343. {
  4344. // create dataFields array
  4345. $dataFields[$key] = vartrue($att['data'], 'string');
  4346. // create validation array
  4347. if(vartrue($att['validate']))
  4348. {
  4349. $validateRules[$key] = array((true === $att['validate'] ? 'required' : $att['validate']), varset($att['rule']), $att['title'], varset($att['error'], $att['help']));
  4350. }
  4351. /* Not implemented in e_model yet
  4352. elseif(vartrue($att['check']))
  4353. {
  4354. $validateRules[$key] = array($att['check'], varset($att['rule']), $att['title'], varset($att['error'], $att['help']));
  4355. }*/
  4356. }
  4357. $this->_pref->setDataFields($dataFields)->setValidationRules($validateRules);
  4358. return $this;
  4359. }
  4360. /**
  4361. * Set current model
  4362. *
  4363. * @return e_admin_ui
  4364. */
  4365. public function _setModel()
  4366. {
  4367. // try to create dataFields array if missing
  4368. if(!$this->dataFields)
  4369. {
  4370. $this->dataFields = array();
  4371. foreach ($this->fields as $key => $att)
  4372. {
  4373. if($att['type'] == 'comma' && (!vartrue($att['data']) || !vartrue($att['rule'])))
  4374. {
  4375. $att['data'] = 'set';
  4376. $att['validate'] = 'set';
  4377. $_parms = vartrue($att['writeParms'], array());
  4378. if(is_string($_parms)) parse_str($_parms, $_parms);
  4379. unset($_parms['__options']);
  4380. $att['rule'] = $_parms;
  4381. unset($_parms);
  4382. }
  4383. if(($key !== 'options' && false !== varset($att['data']) && null !== $att['type'] && !vartrue($att['noedit'])) || vartrue($att['forceSave']))
  4384. {
  4385. $this->dataFields[$key] = vartrue($att['data'], 'str');
  4386. }
  4387. }
  4388. }
  4389. // TODO - do it in one loop, or better - separate method(s) -> convertFields(validate), convertFields(data),...
  4390. if(!$this->validationRules)
  4391. {
  4392. $this->validationRules = array();
  4393. foreach ($this->fields as $key => $att)
  4394. {
  4395. if(null === $att['type'] || vartrue($att['noedit']))
  4396. {
  4397. continue;
  4398. }
  4399. if(vartrue($att['validate']))
  4400. {
  4401. $this->validationRules[$key] = array((true === $att['validate'] ? 'required' : $att['validate']), varset($att['rule']), $att['title'], varset($att['error'], vartrue($att['help'])));
  4402. }
  4403. /*elseif(vartrue($att['check'])) could go?
  4404. {
  4405. $this->checkRules[$key] = array($att['check'], varset($att['rule']), $att['title'], varset($att['error'], $att['help']));
  4406. }*/
  4407. }
  4408. }
  4409. // don't touch it if already exists
  4410. if($this->_model) return $this;
  4411. // default model
  4412. $this->_model = new e_admin_model();
  4413. $this->_model->setModelTable($this->table)
  4414. ->setFieldIdName($this->pid)
  4415. ->setUrl($this->url)
  4416. ->setValidationRules($this->validationRules)
  4417. ->setDbTypes($this->fieldTypes)
  4418. ->setDataFields($this->dataFields)
  4419. ->setMessageStackName('admin_ui_model_'.$this->table)
  4420. ->setParam('db_query', $this->editQry);
  4421. return $this;
  4422. }
  4423. /**
  4424. * Set current tree
  4425. * @return e_admin_ui
  4426. */
  4427. public function _setTreeModel()
  4428. {
  4429. // default tree model
  4430. $this->_tree_model = new e_admin_tree_model();
  4431. $this->_tree_model->setModelTable($this->table)
  4432. ->setFieldIdName($this->pid)
  4433. ->setUrl($this->url)
  4434. ->setMessageStackName('admin_ui_tree_'.$this->table)
  4435. ->setParams(array('model_class' => 'e_admin_model', 'model_message_stack' => 'admin_ui_model_'.$this->table ,'db_query' => $this->listQry));
  4436. return $this;
  4437. }
  4438. /**
  4439. * Get extended (UI) Form instance
  4440. *
  4441. * @return e_admin_ui
  4442. */
  4443. public function _setUI()
  4444. {
  4445. if($this->getParam('ui'))
  4446. {
  4447. $this->_ui = $this->getParam('ui');
  4448. $this->setParam('ui', null);
  4449. }
  4450. else// default ui
  4451. {
  4452. $this->_ui = new e_admin_form_ui($this);
  4453. }
  4454. return $this;
  4455. }
  4456. }
  4457. class e_admin_form_ui extends e_form
  4458. {
  4459. /**
  4460. * @var e_admin_ui
  4461. */
  4462. protected $_controller = null;
  4463. /**
  4464. * Constructor
  4465. * @param e_admin_ui $controller
  4466. * @param boolean $tabindex [optional] enable form element auto tab-indexing
  4467. */
  4468. function __construct($controller, $tabindex = false)
  4469. {
  4470. $this->_controller = $controller;
  4471. parent::__construct($tabindex);
  4472. // protect current methods from conflict.
  4473. $this->preventConflict();
  4474. // user constructor
  4475. $this->init();
  4476. }
  4477. protected function preventConflict()
  4478. {
  4479. $err = false;
  4480. $fields = $this->getController()->getFields();
  4481. foreach($fields as $field => $foptions)
  4482. {
  4483. // check form custom methods
  4484. if(vartrue($foptions['type']) === 'method' && method_exists('e_form', $field)) // check even if type is not method. - just in case of an upgrade later by 3rd-party.
  4485. {
  4486. $message = e107::getParser()->lanVars(LAN_UI_FORM_METHOD_ERROR, array('x'=>$field), true);
  4487. e107::getMessage()->addError($message);
  4488. $err = true;
  4489. }
  4490. }
  4491. /*if($err)
  4492. {
  4493. //echo $err;
  4494. //exit;
  4495. }*/
  4496. }
  4497. /**
  4498. * User defined init
  4499. */
  4500. public function init()
  4501. {
  4502. }
  4503. /**
  4504. * TODO - lans
  4505. * Generic DB Record Creation Form.
  4506. * @return string
  4507. */
  4508. function getCreate()
  4509. {
  4510. $controller = $this->getController();
  4511. $request = $controller->getRequest();
  4512. if($controller->getId())
  4513. {
  4514. $legend = e107::getParser()->lanVars(LAN_UI_EDIT_LABEL, $controller->getId()); // sprintXXX(LAN_UI_EDIT_LABEL, $controller->getId());
  4515. $form_start = vartrue($controller->headerUpdateMarkup);
  4516. $form_end = vartrue($controller->footerUpdateMarkup);
  4517. }
  4518. else
  4519. {
  4520. $legend = LAN_UI_CREATE_LABEL;
  4521. $form_start = vartrue($controller->headerCreateMarkup);
  4522. $form_end = vartrue($controller->footerCreateMarkup);
  4523. }
  4524. $forms = $models = array();
  4525. $forms[] = array(
  4526. 'id' => $this->getElementId(),
  4527. //'url' => e_SELF,
  4528. //'query' => 'self', or custom GET query, self is default
  4529. 'fieldsets' => array(
  4530. 'create' => array(
  4531. 'tabs' => $controller->getTabs(), //used within a single form.
  4532. 'legend' => $legend,
  4533. 'fields' => $controller->getFields(), //see e_admin_ui::$fields
  4534. 'header' => $form_start,
  4535. 'footer' => $form_end,
  4536. 'after_submit_options' => true, // or true for default redirect options
  4537. 'after_submit_default' => $request->getPosted('__after_submit_action', $controller->getDefaultAction()), // or true for default redirect options
  4538. 'triggers' => 'auto', // standard create/update-cancel triggers
  4539. )
  4540. )
  4541. );
  4542. $models[] = $controller->getModel();
  4543. return $this->renderCreateForm($forms, $models, e_AJAX_REQUEST);
  4544. }
  4545. /**
  4546. * TODO - lans
  4547. * Generic Settings Form.
  4548. * @return string
  4549. */
  4550. function getSettings()
  4551. {
  4552. $controller = $this->getController();
  4553. $request = $controller->getRequest();
  4554. $legend = LAN_UI_PREF_LABEL;
  4555. $forms = $models = array();
  4556. $forms[] = array(
  4557. 'id' => $this->getElementId(),
  4558. //'url' => e_SELF,
  4559. //'query' => 'self', or custom GET query, self is default
  4560. 'tabs' => false, // TODO - NOT IMPLEMENTED YET - enable tabs (only if fieldset count is > 1)
  4561. 'fieldsets' => array(
  4562. 'settings' => array(
  4563. 'tabs' => $controller->getPrefTabs(), //used within a single form.
  4564. 'legend' => $legend,
  4565. 'fields' => $controller->getPrefs(), //see e_admin_ui::$prefs
  4566. 'after_submit_options' => false,
  4567. 'after_submit_default' => false, // or true for default redirect options
  4568. 'triggers' => array('save' => array(LAN_SAVE, 'update')), // standard create/update-cancel triggers
  4569. )
  4570. )
  4571. );
  4572. $models[] = $controller->getConfig();
  4573. return $this->renderCreateForm($forms, $models, e_AJAX_REQUEST);
  4574. }
  4575. /**
  4576. * Create list view
  4577. * Search for the following GET variables:
  4578. * - from: integer, current page
  4579. *
  4580. * @return string
  4581. */
  4582. public function getList($ajax = false)
  4583. {
  4584. $tp = e107::getParser();
  4585. $controller = $this->getController();
  4586. $request = $controller->getRequest();
  4587. $id = $this->getElementId();
  4588. $tree = $options = array();
  4589. $tree[$id] = $controller->getTreeModel();
  4590. // if going through confirm screen - no JS confirm
  4591. $controller->setFieldAttr('options', 'noConfirm', $controller->deleteConfirmScreen);
  4592. $options[$id] = array(
  4593. 'id' => $this->getElementId(), // unique string used for building element ids, REQUIRED
  4594. 'pid' => $controller->getPrimaryName(), // primary field name, REQUIRED
  4595. //'url' => e_SELF, default
  4596. 'query' => $controller->getFormQuery(), // work around - see form in newspost.php (submitted news)
  4597. //'query' => $request->buildQueryString(array(), true, 'ajax_used'), - ajax_used is now removed from QUERY_STRING - class2
  4598. 'head_query' => $request->buildQueryString('field=[FIELD]&asc=[ASC]&from=[FROM]', false), // without field, asc and from vars, REQUIRED
  4599. 'np_query' => $request->buildQueryString(array(), false, 'from'), // without from var, REQUIRED for next/prev functionality
  4600. 'legend' => $controller->getPluginTitle(), // hidden by default
  4601. 'form_pre' => !$ajax ? $this->renderFilter($tp->post_toForm(array($controller->getQuery('searchquery'), $controller->getQuery('filter_options'))), $controller->getMode().'/'.$controller->getAction()) : '', // needs to be visible when a search returns nothing
  4602. 'form_post' => '', // markup to be added after closing form element
  4603. 'fields' => $controller->getFields(), // see e_admin_ui::$fields
  4604. 'fieldpref' => $controller->getFieldPref(), // see e_admin_ui::$fieldpref
  4605. 'table_pre' => '', // markup to be added before opening table element
  4606. 'table_post' => !$tree[$id]->isEmpty() ? $this->renderBatch($controller->getBatchDelete(),$controller->getBatchCopy(),$controller->getBatchLink(),$controller->getBatchFeaturebox()) : '',
  4607. 'fieldset_pre' => '', // markup to be added before opening fieldset element
  4608. 'fieldset_post' => '', // markup to be added after closing fieldset element
  4609. 'perPage' => $controller->getPerPage(), // if 0 - no next/prev navigation
  4610. 'from' => $controller->getQuery('from', 0), // current page, default 0
  4611. 'field' => $controller->getQuery('field'), //current order field name, default - primary field
  4612. 'asc' => $controller->getQuery('asc', 'desc'), //current 'order by' rule, default 'asc'
  4613. );
  4614. return $this->renderListForm($options, $tree, $ajax);
  4615. }
  4616. public function getConfirmDelete($ids, $ajax = false)
  4617. {
  4618. $controller = $this->getController();
  4619. $request = $controller->getRequest();
  4620. $fieldsets = array();
  4621. $forms = array();
  4622. $id_array = explode(',', $ids);
  4623. $delcount = count($id_array);
  4624. if(!empty($controller->deleteConfirmMessage))
  4625. {
  4626. e107::getMessage()->addWarning(str_replace("[x]","<b>".$delcount."</b>", $controller->deleteConfirmMessage));
  4627. }
  4628. else
  4629. {
  4630. e107::getMessage()->addWarning(str_replace("[x]","<b>".$delcount."</b>",LAN_UI_DELETE_WARNING));
  4631. }
  4632. $fieldsets['confirm'] = array(
  4633. 'fieldset_pre' => '', // markup to be added before opening fieldset element
  4634. 'fieldset_post' => '', // markup to be added after closing fieldset element
  4635. 'table_head' => '', // markup between <thead> tag
  4636. // Colgroup Example: array(0 => array('class' => 'label', 'style' => 'text-align: left'), 1 => array('class' => 'control', 'style' => 'text-align: left'));
  4637. 'table_colgroup' => '', // array to be used for creating markup between <colgroup> tag (<col> list)
  4638. 'table_pre' => '', // markup to be added before opening table element
  4639. 'table_post' => '', // markup to be added after closing table element
  4640. 'table_rows' => '', // rows array (<td> tags)
  4641. 'table_body' => '', // string body - used only if rows empty
  4642. 'pre_triggers' => '',
  4643. 'triggers' => array('hidden' => $this->hidden('etrigger_delete['.$ids.']', $ids), 'delete_confirm' => array(LAN_CONFDELETE, 'confirm', $ids), 'cancel' => array(LAN_CANCEL, 'cancel')),
  4644. );
  4645. if($delcount > 1)
  4646. {
  4647. $fieldsets['confirm']['triggers']['hidden'] = $this->hidden('etrigger_batch', 'delete');
  4648. }
  4649. $forms[$id] = array(
  4650. 'id' => $this->getElementId(), // unique string used for building element ids, REQUIRED
  4651. 'url' => e_SELF, // default
  4652. 'query' => $request->buildQueryString(array(), true, 'ajax_used'), // - ajax_used is now removed from QUERY_STRING - class2
  4653. 'legend' => $controller->addTitle(LAN_UI_DELETE_LABEL), // hidden by default
  4654. 'form_pre' => '', // markup to be added before opening form element
  4655. 'form_post' => '', // markup to be added after closing form element
  4656. 'header' => '', // markup to be added after opening form element
  4657. 'footer' => '', // markup to be added before closing form element
  4658. 'fieldsets' => $fieldsets,
  4659. );
  4660. return $this->renderForm($forms, $ajax);
  4661. }
  4662. function renderFilter($current_query = array(), $location = '', $input_options = array())
  4663. {
  4664. if(!$input_options) $input_options = array('size' => 20);
  4665. if(!$location)
  4666. {
  4667. $location = 'main/list'; //default location
  4668. }
  4669. $l = e107::getParser()->post_toForm(explode('/', $location));
  4670. if(!is_array($input_options))
  4671. {
  4672. parse_str($input_options, $input_options);
  4673. }
  4674. $input_options['id'] = false;
  4675. $input_options['class'] = 'tbox input-text filter ';
  4676. $controller = $this->getController();
  4677. $filter_pre = vartrue($controller->preFiliterMarkup);
  4678. $filter_post = vartrue($controller->postFiliterMarkup);
  4679. $filter_preserve_var = array();
  4680. // method requires controller - stanalone advanced usage not possible
  4681. if($this->getController())
  4682. {
  4683. $get = $this->getController()->getQuery();
  4684. foreach ($get as $key => $value)
  4685. {
  4686. if($key == 'searchquery' || $key == 'filter_options' || $key == 'etrigger_filter') continue;
  4687. $key = preg_replace('/[^\w]/', '', $key);
  4688. $filter_preserve_var[] = $this->hidden($key, rawurlencode($value));
  4689. }
  4690. }
  4691. else
  4692. {
  4693. $filter_preserve_var[] = $this->hidden('mode', $l[0]);
  4694. $filter_preserve_var[] = $this->hidden('action', $l[1]);
  4695. }
  4696. $text = "
  4697. <form method='get' action='".e_SELF."'>
  4698. <fieldset class='e-filter'>
  4699. <legend class='e-hideme'>".LAN_LABEL_LABEL_SELECTED."</legend>
  4700. ".$filter_pre."
  4701. <div class='row-fluid'>
  4702. <div class='left form-inline span8' style='margin-top:10px;margin-bottom:-10px;'>
  4703. ".$this->text('searchquery', $current_query[0], 50, $input_options)."<i class='fa fa-search searchquery'></i>
  4704. ".$this->select_open('filter_options', array('class' => 'e-tip tbox select filter', 'id' => false, 'title'=>'Filter the results below'))."
  4705. ".$this->option(LAN_FILTER_LABEL_DISPLAYALL, '')."
  4706. ".$this->option(LAN_FILTER_LABEL_CLEAR, '___reset___')."
  4707. ".$this->renderBatchFilter('filter', $current_query[1])."
  4708. ".$this->select_close()."
  4709. <div class='e-autocomplete'></div>
  4710. ".implode("\n", $filter_preserve_var)."
  4711. ".$this->admin_button('etrigger_filter', 'etrigger_filter', 'filter e-hide-if-js', LAN_FILTER, array('id' => false))."
  4712. <span class='indicator' style='display: none;'>
  4713. <img src='".e_IMAGE_ABS."generic/loading_16.gif' class='icon action S16' alt='".LAN_LOADING."' />
  4714. </span>
  4715. </div>
  4716. <div class='span4 text-right' style='margin-top:10px;margin-bottom:-10px;padding-top:15px;padding-right:5px'>";
  4717. // Let Admin know which language table is being saved to. (avoid default table overwrites)
  4718. if(e107::getConfig()->get('multilanguage'))
  4719. {
  4720. $curTable = $controller->getTableName();
  4721. if($curTable != e107::getDb()->db_IsLang($curTable))
  4722. {
  4723. $lang = e107::getDb()->mySQLlanguage;
  4724. }
  4725. else
  4726. {
  4727. $lang = e107::getConfig()->get('sitelanguage');
  4728. }
  4729. $def = deftrue('LAN_UI_USING_DATABASE_TABLE','Using [x] database table');
  4730. $diz = e107::getParser()->lanVars($def, $lang); // "Using ".$lang." database table";
  4731. $text .= "<span class='e-tip' title=\"".$diz."\">";
  4732. $text .= e107::getParser()->toGlyph('fa-hdd-o'); // '<i class="icon-hdd"></i> ';
  4733. $text .= e107::getLanguage()->toNative($lang)."</span>";
  4734. }
  4735. $text .= "
  4736. </div>
  4737. </div>
  4738. ".$filter_post."
  4739. </fieldset>
  4740. </form>
  4741. ";
  4742. e107::js('core','scriptaculous/controls.js','prototype', 2);
  4743. //TODO - external JS
  4744. e107::js('footer-inline',"
  4745. //autocomplete fields
  4746. \$\$('input[name=searchquery]').each(function(el, cnt) {
  4747. if(!cnt) el.activate();
  4748. else return;
  4749. new Ajax.Autocompleter(el, el.next('div.e-autocomplete'), '".e_SELF."?mode=".$l[0]."&action=filter', {
  4750. paramName: 'searchquery',
  4751. minChars: 2,
  4752. frequency: 0.5,
  4753. afterUpdateElement: function(txt, li) {
  4754. var cfrm = el.up('form'), cont = cfrm.next('.e-container');
  4755. if(!cont) {
  4756. return;
  4757. }
  4758. cfrm.submitForm(cont);
  4759. },
  4760. indicator: el.next('span.indicator'),
  4761. parameters: 'ajax_used=1'
  4762. });
  4763. var sel = el.next('select.filter');
  4764. if(sel) {
  4765. sel.observe('change', function (e) {
  4766. var cfrm = e.element().up('form'), cont = cfrm.next('.e-container');
  4767. if(cfrm && cont && e.element().value != '___reset___') {
  4768. e.stop();
  4769. cfrm.submitForm(cont);
  4770. return;
  4771. }
  4772. e107Helper.selectAutoSubmit(e.element());
  4773. });
  4774. }
  4775. });
  4776. ",'prototype');
  4777. // TODO implement ajax queue
  4778. // FIXME - dirty way to register events after ajax update - DO IT RIGHT - see all.jquery, create object
  4779. // and use handler, re-register them global after ajax update (context)
  4780. e107::js('footer-inline',"
  4781. var filterRunning = false, request;
  4782. var applyAfterAjax = function(context) {
  4783. \$('.e-hideme', context).hide();
  4784. \$('.e-expandit', context).show();
  4785. \$('.e-expandit', context).click(function () {
  4786. var href = (\$(this).is('a')) ? \$(this).attr('href') : '';
  4787. if(href == '' && \$(this).attr('data-target'))
  4788. {
  4789. href = '#' + \$(this).attr('data-target');
  4790. }
  4791. if(href === '#' || href == '')
  4792. {
  4793. idt = \$(this).nextAll('div');
  4794. \$(idt).toggle('slow');
  4795. return true;
  4796. }
  4797. //var id = $(this).attr('href');
  4798. \$(href).toggle('slow');
  4799. return false;
  4800. });
  4801. \$('input.toggle-all', context).click(function(evt) {
  4802. var selector = 'input[type=\"checkbox\"].checkbox';
  4803. if(\$(this).val().startsWith('jstarget:')) {
  4804. selector = 'input[type=\"checkbox\"][name^=\"' + \$(this).val().split(/jstarget\:/)[1] + '\"]';
  4805. }
  4806. if(\$(this).is(':checked')){
  4807. \$(selector).attr('checked', 'checked');
  4808. }
  4809. else{
  4810. \$(selector).removeAttr('checked');
  4811. }
  4812. });
  4813. };
  4814. var searchQueryHandler = function (e) {
  4815. var el = \$(this), frm = el.parents('form'), cont = frm.nextAll('.e-container');
  4816. if(cont.length < 1 || frm.length < 1 || (el.val().length > 0 && el.val().length < 3)) return;
  4817. e.preventDefault();
  4818. if(filterRunning && request) request.abort();
  4819. filterRunning = true;
  4820. cont.css({ opacity: 0.5 });
  4821. request = \$.get(frm.attr('action'), frm.serialize(), function(data){
  4822. filterRunning = false;
  4823. setTimeout(function() {
  4824. if(filterRunning) {
  4825. //cont.css({ opacity: 1 });
  4826. return;
  4827. }
  4828. cont.html(data).css({ opacity: 1 });
  4829. applyAfterAjax(cont);
  4830. }, 700);
  4831. }, 'html')
  4832. .error(function() {
  4833. filterRunning = false;
  4834. cont.css({ opacity: 1 });
  4835. });
  4836. };
  4837. \$('#searchquery').on('keyup', searchQueryHandler);
  4838. ", 'jquery');
  4839. return $text;
  4840. }
  4841. // FIXME - use e_form::batchoptions(), nice way of buildig batch dropdown - news administration show_batch_options()
  4842. function renderBatch($allow_delete = false,$allow_copy= false, $allow_url=false, $allow_featurebox=false)
  4843. {
  4844. // $allow_copy = TRUE;
  4845. $fields = $this->getController()->getFields();
  4846. if(!varset($fields['checkboxes']))
  4847. {
  4848. $mes = e107::getMessage();
  4849. $mes->add("Cannot display Batch drop-down as 'checkboxes' was not found in \$fields array.", E_MESSAGE_DEBUG);
  4850. return '';
  4851. }
  4852. // FIX - don't show FB option if plugin not installed
  4853. if(!e107::isInstalled('featurebox'))
  4854. {
  4855. $allow_featurebox = false;
  4856. }
  4857. // TODO - core ui-batch-option class!!! REMOVE INLINE STYLE!
  4858. // XXX Quick Fix for styling - correct.
  4859. $text = "
  4860. <div class='navbar navbar-inner left' style='padding-left:30px; padding-top:6px; margin-top:-20px;border-top:0px'>
  4861. <img src='".e_IMAGE_ABS."generic/branchbottom.gif' alt='' class='icon action' />
  4862. ".$this->select_open('etrigger_batch', array('class' => 'tbox select batch e-autosubmit reset', 'id' => false))."
  4863. ".$this->option(LAN_BATCH_LABEL_SELECTED, '', false)."
  4864. ".($allow_copy ? $this->option(LAN_COPY, 'copy', false, array('class' => 'ui-batch-option class', 'other' => 'style="padding-left: 15px"')) : '')."
  4865. ".($allow_delete ? $this->option(LAN_DELETE, 'delete', false, array('class' => 'ui-batch-option class', 'other' => 'style="padding-left: 15px"')) : '')."
  4866. ".($allow_url ? $this->option(LAN_UI_BATCH_CREATELINK, 'url', false, array('class' => 'ui-batch-option class', 'other' => 'style="padding-left: 15px"')) : '')."
  4867. ".($allow_featurebox ? $this->option(LAN_PLUGIN_FEATUREBOX_BATCH, 'featurebox', false, array('class' => 'ui-batch-option class', 'other' => 'style="padding-left: 15px"')) : '')."
  4868. ".$this->renderBatchFilter('batch')."
  4869. ".$this->select_close()."
  4870. ".$this->admin_button('e__execute_batch', 'e__execute_batch', 'batch e-hide-if-js', LAN_GO, array('id' => false))."
  4871. </div>
  4872. ";
  4873. return $text;
  4874. }
  4875. // TODO - do more
  4876. function renderBatchFilter($type='batch', $selected = '') // Common function used for both batches and filters.
  4877. {
  4878. $optdiz = array('batch' => LAN_BATCH_LABEL_PREFIX.'&nbsp;', 'filter'=> LAN_FILTER_LABEL_PREFIX.'&nbsp;');
  4879. $table = $this->getController()->getTableName();
  4880. $text = '';
  4881. $textsingle = '';
  4882. foreach($this->getController()->getFields() as $key=>$val)
  4883. {
  4884. if(!varset($val[$type]))
  4885. {
  4886. continue;
  4887. }
  4888. $option = array();
  4889. $parms = vartrue($val['writeParms'], array());
  4890. if(is_string($parms)) parse_str($parms, $parms);
  4891. switch($val['type'])
  4892. {
  4893. case 'bool':
  4894. case 'boolean': //TODO modify description based on $val['parm]
  4895. if(vartrue($parms['reverse'])) // reverse true/false values;
  4896. {
  4897. $option['bool__'.$key.'__0'] = LAN_YES; // see newspost.php : news_allow_comments for an example.
  4898. $option['bool__'.$key.'__1'] = LAN_NO;
  4899. }
  4900. else
  4901. {
  4902. $option['bool__'.$key.'__1'] = LAN_YES;
  4903. $option['bool__'.$key.'__0'] = LAN_NO;
  4904. }
  4905. if($type == 'batch')
  4906. {
  4907. $option['boolreverse__'.$key] = LAN_BOOL_REVERSE;
  4908. }
  4909. break;
  4910. case 'comma':
  4911. // TODO lan
  4912. if(!isset($parms['__options'])) $parms['__options'] = array();
  4913. if(!is_array($parms['__options'])) parse_str($parms['__options'], $parms['__options']);
  4914. $opts = $parms['__options'];
  4915. unset($parms['__options']); //remove element options if any
  4916. $options = $parms ? $parms : array();
  4917. if(empty($options)) continue;
  4918. if($type == 'batch')
  4919. {
  4920. $_option = array();
  4921. if(isset($options['addAll']))
  4922. {
  4923. $option['attach_all__'.$key] = vartrue($options['addAll'], '(add all)');
  4924. unset($options['addAll']);
  4925. }
  4926. if(isset($options['clearAll']))
  4927. {
  4928. $_option['deattach_all__'.$key] = vartrue($options['clearAll'], '(clear all)');
  4929. unset($options['clearAll']);
  4930. }
  4931. if(vartrue($opts['simple']))
  4932. {
  4933. foreach ($options as $value)
  4934. {
  4935. $option['attach__'.$key.'__'.$value] = 'Add '.$value;
  4936. $_option['deattach__'.$key.'__'.$value] = 'Remove '.$value;
  4937. }
  4938. }
  4939. else
  4940. {
  4941. foreach ($options as $value => $label)
  4942. {
  4943. $option['attach__'.$key.'__'.$value] = 'Add '.$label;
  4944. $_option['deattach__'.$key.'__'.$value] = 'Remove '.$label;
  4945. }
  4946. }
  4947. $option = array_merge($option, $_option);
  4948. unset($_option);
  4949. }
  4950. else
  4951. {
  4952. unset($options['addAll'], $options['clearAll']);
  4953. if(vartrue($opts['simple']))
  4954. {
  4955. foreach($options as $k)
  4956. {
  4957. $option[$key.'__'.$k] = $k;
  4958. }
  4959. }
  4960. else
  4961. {
  4962. foreach($options as $k => $name)
  4963. {
  4964. $option[$key.'__'.$k] = $name;
  4965. }
  4966. }
  4967. }
  4968. break;
  4969. case 'templates':
  4970. case 'layouts':
  4971. $parms['raw'] = true;
  4972. $val['writeParms'] = $parms;
  4973. $tmp = $this->renderElement($key, '', $val);
  4974. if(is_array($tmp))
  4975. {
  4976. foreach ($tmp as $k => $name)
  4977. {
  4978. $option[$key.'__'.$k] = $name;
  4979. }
  4980. }
  4981. break;
  4982. case 'dropdown': // use the array $parm;
  4983. if(!is_array(varset($parms['__options']))) parse_str($parms['__options'], $parms['__options']);
  4984. $opts = $parms['__options'];
  4985. if(vartrue($opts['multiple']))
  4986. {
  4987. // no batch support for multiple, should have some for filters soon
  4988. continue;
  4989. }
  4990. unset($parms['__options']); //remove element options if any
  4991. foreach($parms as $k => $name)
  4992. {
  4993. $option[$key.'__'.$k] = $name;
  4994. }
  4995. break;
  4996. case 'lanlist': // use the array $parm;
  4997. if(!is_array(varset($parms['__options']))) parse_str($parms['__options'], $parms['__options']);
  4998. $opts = $parms['__options'];
  4999. if(vartrue($opts['multiple']))
  5000. {
  5001. // no batch support for multiple, should have some for filters soon
  5002. continue;
  5003. }
  5004. $options = e107::getLanguage()->getLanSelectArray();
  5005. foreach($options as $code => $name)
  5006. {
  5007. $option[$key.'__'.$code] = $name;
  5008. }
  5009. break;
  5010. case 'datestamp':
  5011. //TODO today, yesterday, this-month, last-month .
  5012. $dateFilters = array (
  5013. 'hour' => "Past Hour",
  5014. "day" => "Past 24 hours",
  5015. "week" => "Past Week",
  5016. "month" => "Past Month",
  5017. "year" => "Past Year"
  5018. );
  5019. foreach($dateFilters as $k => $name)
  5020. {
  5021. $option['datestamp__'.$key.'__'.$k] = $name;
  5022. // $option['bool__'.$key.'__0'] = LAN_NO;
  5023. // $option[$key.'__'.$k] = $name;
  5024. }
  5025. break;
  5026. case 'userclass':
  5027. $classes = e107::getUserClass()->uc_required_class_list(vartrue($parms['classlist'], 'public,nobody,guest,admin,main,classes'));
  5028. foreach($classes as $k => $name)
  5029. {
  5030. $option[$key.'__'.$k] = $name;
  5031. }
  5032. break;
  5033. case 'userclasses':
  5034. $classes = e107::getUserClass()->uc_required_class_list(vartrue($parms['classlist'], 'public,nobody,guest,admin,main,classes'));
  5035. $_option = array();
  5036. if($type == 'batch')
  5037. {
  5038. // FIXME Lan
  5039. foreach ($classes as $k => $v)
  5040. {
  5041. $option['ucadd__'.$key.'__'.$k] = LAN_ADD.' '.$v;
  5042. $_option['ucremove__'.$key.'__'.$k] = 'Remove '.$v;
  5043. }
  5044. $option['ucaddall__'.$key] = '(add all)';
  5045. $_option['ucdelall__'.$key] = '(clear all)';
  5046. $option = array_merge($option, $_option);
  5047. }
  5048. else
  5049. {
  5050. foreach ($classes as $k => $v)
  5051. {
  5052. $option[$key.'__'.$k] = $v;
  5053. }
  5054. }
  5055. unset($_option);
  5056. break;
  5057. case 'method':
  5058. $method = $key;
  5059. $list = call_user_func_array(array($this, $method), array('', $type, $parms));
  5060. if(is_array($list))
  5061. {
  5062. //check for single option
  5063. if(isset($list['singleOption']))
  5064. {
  5065. $textsingle .= $list['singleOption'];
  5066. continue;
  5067. }
  5068. // non rendered options array
  5069. foreach($list as $k => $name)
  5070. {
  5071. $option[$key.'__'.$k] = $name;
  5072. }
  5073. }
  5074. elseif(!empty($list)) //optgroup, continue
  5075. {
  5076. $text .= $list;
  5077. continue;
  5078. }
  5079. break;
  5080. case 'user': // TODO - User Filter
  5081. $sql = e107::getDb();
  5082. $field = $val['field'];
  5083. $query = "SELECT d.".$field.", u.user_name FROM #".$val['table']." AS d LEFT JOIN #user AS u ON d.".$field." = u.user_id GROUP BY d.".$field." ORDER BY u.user_name";
  5084. $row = $sql->retrieve($query,true);
  5085. foreach($row as $data)
  5086. {
  5087. $k = $data[$field];
  5088. if($k == 0)
  5089. {
  5090. $option[$key.'__'.$k] = "(".LAN_ANONYMOUS.")";
  5091. }
  5092. else
  5093. {
  5094. $option[$key.'__'.$k] = vartrue($data['user_name'],'Unknown');
  5095. }
  5096. }
  5097. break;
  5098. }
  5099. if(count($option) > 0)
  5100. {
  5101. $text .= "\t".$this->optgroup_open($optdiz[$type].defset($val['title'], $val['title']), varset($disabled))."\n";
  5102. foreach($option as $okey=>$oval)
  5103. {
  5104. $text .= $this->option($oval, $okey, $selected == $okey)."\n";
  5105. }
  5106. $text .= "\t".$this->optgroup_close()."\n";
  5107. }
  5108. }
  5109. return $textsingle.$text;
  5110. }
  5111. public function getElementId()
  5112. {
  5113. $controller = $this->getController();
  5114. return str_replace('_', '-', ($controller->getPluginName() == 'core' ? 'core-'.$controller->getTableName() : 'plugin-'.$controller->getPluginName()));
  5115. }
  5116. /**
  5117. * @return e_admin_ui
  5118. */
  5119. public function getController()
  5120. {
  5121. return $this->_controller;
  5122. }
  5123. }
  5124. include_once(e107::coreTemplatePath('admin_icons'));
  5125. /**
  5126. * TODO:
  5127. * 1. [DONE - a good start] move abstract peaces of code to the proper classes
  5128. * 2. [DONE - at least for alpha release] remove duplicated code (e_form & e_admin_form_ui), refactoring
  5129. * 3. make JS Manager handle Styles (.css files and inline CSS)
  5130. * 4. [DONE] e_form is missing some methods used in e_admin_form_ui
  5131. * 5. [DONE] date convert needs string-to-datestamp auto parsing, strptime() is the solution but needs support for
  5132. * Windows and PHP < 5.1.0 - build custom strptime() function (php_compatibility_handler.php) on this -
  5133. * http://sauron.lionel.free.fr/?page=php_lib_strptime (bad license so no copy/paste is allowed!)
  5134. * 6. [DONE - read/writeParms introduced ] $fields[parms] mess - fix it, separate list/edit mode parms somehow
  5135. * 7. clean up/document all object vars (e_admin_ui, e_admin_dispatcher)
  5136. * 8. [DONE hopefully] clean up/document all parameters (get/setParm()) in controller and model classes
  5137. * 9. [DONE] 'ip' field type - convert to human readable format while showing/editing record
  5138. * 10. draggable (or not?) ordering (list view)
  5139. * 11. [DONE] realtime search filter (typing text) - like downloads currently
  5140. * 12. [DONE] autosubmit when 'filter' dropdown is changed (quick fix?)
  5141. * 13. tablerender captions
  5142. * 14. [DONE] textareas auto-height
  5143. * 15. [DONE] multi JOIN table support (optional), aliases
  5144. * 16. tabs support (create/edit view)
  5145. * 17. tree list view (should handle cases like Site Links admin page)
  5146. */
  5147. ?>