PageRenderTime 59ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/core/libraries/component.php

http://rapyd-framework.googlecode.com/
PHP | 567 lines | 310 code | 45 blank | 212 comment | 43 complexity | b8abcc5e2a637bcf0f848528397ce979 MD5 | raw file
  1. <?php
  2. if (!defined('RAPYD_PATH'))
  3. exit('No direct script access allowed');
  4. /**
  5. * Component library, is the ancestor of all widgets
  6. *
  7. *
  8. * @package Core
  9. * @author Felice Ostuni
  10. * @copyright (c) 2011 Rapyd Team
  11. * @license http://www.rapyd.com/license
  12. */
  13. class rpd_component_library
  14. {
  15. public static $identifier = 0;
  16. public $db;
  17. public $process_status = "idle";
  18. public $status = "idle";
  19. public $action = "idle";
  20. public $label = "";
  21. public $output = "";
  22. public $built = FALSE;
  23. public $button_container = array("TR" => array(), "BL" => array(), "BR" => array());
  24. public $buttons = array();
  25. public $url;
  26. public function __construct($config = array())
  27. {
  28. if (is_string($config))
  29. {
  30. $ext = end(explode('.', $config));
  31. switch ($ext)
  32. {
  33. case "php":
  34. include($config);
  35. break;
  36. default:
  37. $config = array();
  38. }
  39. }
  40. if (count($config) > 0)
  41. {
  42. $this->initialize($config);
  43. }
  44. }
  45. /**
  46. * called by constructor, a widget can be built using an associative array to prefill each widget property
  47. * it works calling $widget->set_$key($val) for each item in config array.
  48. * since each widget is initialized this way, you can for example build a complex "dataedit"
  49. * using a sigle multi-dimensional config array, defining form properties and fields properties
  50. * (so this configuration can be stored or manipulated in an easy way)
  51. *  
  52. * <code>
  53. * //edit
  54. * $config = array (
  55. * 'label' => 'Manage Article',
  56. * 'source' => 'articles',
  57. * 'back_url' => $this->url('filtered_grid'),
  58. * 'fields' => array (
  59. * array (
  60. * 'type' => 'input',
  61. * 'name' => 'title',
  62. * 'label' => 'Title',
  63. * 'rule' => 'trim|required',
  64. * ),
  65. *
  66. * ),
  67. * 'buttons' => array('modify','save','undo','back'),
  68. * );
  69. * $edit = new dataedit_library($config);
  70. * $edit->build();
  71.  * </code>
  72. *
  73. * @param array $config
  74. */
  75. public function initialize($config = array())
  76. {
  77. $this->clear();
  78. foreach ($config as $key => $val)
  79. {
  80. $method = 'set_' . $key;
  81. $this->$method($val);
  82. }
  83. }
  84. /**
  85. * connect to the database and fill $widget->db with database "active record" reference
  86. */
  87. public function connect()
  88. {
  89. rpd::connect();
  90. $this->db = rpd::$db;
  91. }
  92. /**
  93. * reset each property to default values
  94. */
  95. public function clear()
  96. {
  97. $vars = get_class_vars(get_class($this));
  98. foreach ($vars as $property => $value)
  99. {
  100. if (!in_array($property, array("cid", "built")))
  101. {
  102. $this->$property = $value;
  103. }
  104. }
  105. }
  106. /**
  107. * identifier is empty or a numeric value, it "identify" a single object instance.
  108. * by default if you build 3 widget in a single controller/page their id will be:
  109. * "" for ther first one
  110. * 1 for the second one
  111. * 2 for the third
  112. * .. etcetera
  113. *
  114. * identifiers are used to preserve uri/url segments consistence/isolation
  115. * so for example you can build 2 datagrid on a single controller without problem because uri-semantic will be:
  116. * /controller/method/[[orderby/article_id/desc/pag/1]]/[[orderby1/anotherfield/desc/pag1/2]]
  117. *
  118. * @return string identifier
  119. */
  120. protected function get_identifier()
  121. {
  122. if (self::$identifier < 1)
  123. {
  124. self::$identifier++;
  125. return "";
  126. }
  127. return (string) self::$identifier++;
  128. }
  129. /**
  130. * dynamic getter & setter
  131. *
  132. * it's used basically to ensure method chaining and to get & set widget's properties
  133. * it also enable "short array" syntax so you can use $widget->method('param|param') and it will call
  134. * $widget->set_method('param','param')
  135. *
  136. * @param string $method
  137. * @param array $arguments
  138. * @return object $this
  139. */
  140. public function __call($method, $arguments)
  141. {
  142. $prefix = strtolower(substr($method, 0, 4));
  143. $property = strtolower(substr($method, 4));
  144. if (method_exists($this, 'set_' . $method))
  145. {
  146. return call_user_func_array(array($this, 'set_' . $method), $arguments);
  147. }
  148. if (empty($prefix) || empty($property))
  149. {
  150. return;
  151. }
  152. if ($prefix == "get_" && isset($this->$property))
  153. {
  154. return $this->$property;
  155. }
  156. if ($prefix == "set_")
  157. {
  158. if
  159. (
  160. !in_array($property, array('cell_template', 'pattern'))
  161. AND is_string($arguments[0])
  162. AND strpos($arguments[0], '|')
  163. )
  164. {
  165. //die ($property);
  166. $this->$property = explode('|', $arguments[0]);
  167. } else
  168. {
  169. $this->$property = $arguments[0];
  170. }
  171. return $this;
  172. }
  173. }
  174. /**
  175. * shortcut for $widget->status == $status OR ....
  176. * so you can use $widget->status_is($status)
  177. * where $status can be an array of valid status
  178. * not very useful
  179. *
  180. * @todo merge all "property_is/on" in one call
  181. * @param string $status
  182. * @return bool
  183. */
  184. public function status_is($status = "false")
  185. {
  186. if (is_array($status))
  187. return (bool) in_array($this->status, $status);
  188. return ($this->status == $status);
  189. }
  190. /**
  191. * shortcut for $widget->process_status == $process_status OR ....
  192. * so you can use $widget->on($process_status)
  193. * where $process_status can be an array of valid status
  194. *
  195. * @todo merge all "property_is/on" in one call
  196. * @param string $process_status
  197. * @return bool
  198. */
  199. public function on($process_status = "false")
  200. {
  201. if (is_array($process_status))
  202. return (bool) in_array($this->process_status, $process_status);
  203. return ($this->process_status == $process_status);
  204. }
  205. /**
  206. * shortcut for $widget->action == $action OR ....
  207. * so you can use $widget->action_is($action)
  208. * where $process_status can be an array of valid actions
  209. * not very useful
  210. *
  211. * @todo merge all "property_is/on" in one call
  212. * @param string $process_status
  213. * @return bool
  214. */
  215. public function action_is($action = "false")
  216. {
  217. if (is_array($action))
  218. return (bool) in_array($this->action, $action);
  219. return ($this->action == $action);
  220. }
  221. /**
  222. * alias for action_is
  223. *
  224. * @param string $action
  225. * @return bool
  226. */
  227. public function when($action = "false")
  228. {
  229. return $this->action_is($action);
  230. }
  231. /**
  232. * important stuff, widgets support placeholders like: {placeholder}
  233. * parse_pattern find all occurences and return a simple array of matches
  234. * it's used for example to find "field" placeholders inside a datagrid column pattern
  235. *
  236. * @param string $pattern
  237. * @return array of matches {placeholders}
  238. */
  239. public static function parse_pattern($pattern)
  240. {
  241. if (preg_match_all('/\{(\w+)\}/is', $pattern, $matches))
  242. {
  243. return $matches[1];
  244. }
  245. }
  246. /**
  247. * replace placeholders in a pattern like "bla bla.. {placeholder}"
  248. *
  249. * @param string $pattern a string containing {placeholder}s
  250. * @param array $values associative array like array('placeholder'=>'value', 'placeholder2'=>'value2')
  251. * @return string parsed output
  252. */
  253. public static function replace_pattern($pattern, $values)
  254. {
  255. $output = $pattern;
  256. $output = str_replace('|', 'Ł|Ł', $output); //fix for params separator
  257. $matches = self::parse_pattern($pattern);
  258. if ($matches)
  259. {
  260. foreach ($matches as $field)
  261. {
  262. if (isset($values[$field]) or is_null($values[$field]))
  263. $output = str_replace('{' . $field . '}', $values[$field], $output);
  264. }
  265. }
  266. return $output;
  267. }
  268. /**
  269. * important stuff, widgets support function patterns like
  270. * <function_name>param|param|{placeholder}</function_name>
  271. * so if "function_name" is a valid "formatting_function" the widget will execute
  272. * a callback splitting parameters using | (pipe)
  273. * it's used for example to let you format datagrid columns in really easy way:
  274. *
  275. * <code>
  276. * $grid->column('<substr>0|30|{name}</substr>',"Name"); //will output a column with first 30 chars of field "name"
  277. * </code>
  278. *
  279. * @param string $content
  280. * @param type $functions
  281. * @return string
  282. */
  283. public function replace_functions($content, $functions='ALL')
  284. {
  285. $formatting_functions = array("rpd[^>]+",
  286. "htmlspecialchars", "htmlentities", "utf8_encode",
  287. "strtolower", "strtoupper","str_replace",
  288. "substr", "strpos", "nl2br", "number_format",
  289. "dropdown", "radiogroup", "date", "strtotime"
  290. );
  291. if (is_string($functions) AND $functions == 'ALL')
  292. {
  293. $arr = get_defined_functions();
  294. $functions = array_merge($formatting_functions, $arr["user"]);
  295. } elseif (is_array($functions))
  296. {
  297. $functions = array_merge($formatting_functions, $functions);
  298. } elseif (!isset($functions))
  299. {
  300. $functions = $formatting_functions;
  301. }
  302. $tags = join('|', $functions);
  303. while (preg_match_all("/(<($tags)>(.*)<\/\\2>)/isU", $content, $matches, PREG_SET_ORDER))
  304. {
  305. foreach ($matches as $found)
  306. {
  307. $params = $found[3];
  308. //check if a recustion is needed
  309. if (preg_match("/<\/$tags>/is", $params))
  310. $params = self::replace_functions($params, $functions);
  311. $toreplace = $found[0];
  312. $function = $found[2];
  313. $arguments = explode("Ł|Ł", $params);
  314. if (in_array($function, array('dropdown', 'radiogroup')))
  315. {
  316. $replace = call_user_func_array(array($this, $function), $arguments);
  317. }
  318. //static class/metdod
  319. elseif (strpos($function, "::") !== FALSE)
  320. {
  321. list($static_class, $static_method) = explode("::", $function);
  322. $replace = call_user_func_array(array($static_class, $static_method), $arguments);
  323. }
  324. //dynamic object/method
  325. elseif (strpos($function, ".") !== FALSE)
  326. {
  327. list($class, $method) = explode(".", $function);
  328. $replace = call_user_func_array(array($$class, $method), $arguments);
  329. } else
  330. {
  331. $replace = @call_user_func_array($function, $arguments);
  332. }
  333. $content = str_replace($toreplace, $replace, $content);
  334. }
  335. }
  336. return $content;
  337. }
  338. /**
  339. * old stuff, however.. untested on new versions..
  340. * basically let you use a dropdown instance as pattern for datagrid columns
  341. * may be useful in datafilter+datagrid context where you already buit a dropdown..
  342. * it can be uset directly as datagrid column (it work comparind all dropdown loaded options)
  343. * not very useful, it's is a lot more clean perform joins
  344. *
  345. * <code>
  346. * $filter->field('dropdown','author_id','Author')->options('SELECT ....
  347. * ...
  348. * $grid->column($filter->fields['author_id'],"Name"); //if $dropdown is a a column with first 30 chars of field "name"
  349. * </code>
  350. *
  351. * @param object $field
  352. * @param string $id
  353. * @return string
  354. */
  355. public function dropdown($field, $id)
  356. {
  357. if (is_object($this->source))
  358. {
  359. return $this->source->fields[$field]->options[$id];
  360. }
  361. }
  362. /**
  363. * same concept of dropdown, deprecaded too
  364. *
  365. * @param object $field
  366. * @param string $id
  367. * @return string
  368. */
  369. public function radiogroup($field, $id)
  370. {
  371. return $this->dropdown($field, $id);
  372. }
  373. /**
  374. * basically it "flat" buttons array for each zone
  375. * used on build() to prepare button output
  376. * zones are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right)
  377. * see button()
  378. *
  379. * @todo i think it must be improved..
  380. * @param string $container
  381. * @return array
  382. */
  383. public function button_containers($container = null)
  384. {
  385. if (isset($container))
  386. {
  387. return join("&nbsp;", $this->button_container[$container]);
  388. } else
  389. {
  390. foreach ($this->button_container as $container => $content)
  391. {
  392. $containers[$container] = join("&nbsp;", $this->button_container[$container]);
  393. }
  394. return $containers;
  395. }
  396. }
  397. /**
  398. * add a button to the button container in a given position
  399. * positions are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right)
  400. *
  401. * @param string $name button name attribute
  402. * @param string $caption button label
  403. * @param string $action the onclick event (all buttons currently works using window.location.href=...)
  404. * @param string $position "TL","TR","BL","BR"
  405. * @param string $class css class
  406. */
  407. function button($name, $caption, $action, $position="BL", $class="button")
  408. {
  409. $this->button_container[$position][] = rpd_html_helper::button($name, $caption, $action, "button", $class);
  410. }
  411. /**
  412. * same of button() but it make a submit
  413. *
  414. * @param string $name
  415. * @param string $caption
  416. * @param string $position
  417. */
  418. function submit($name, $caption, $position="BL")
  419. {
  420. $this->button_container[$position][] = rpd_html_helper::button($name, $caption, "", "submit", "button");
  421. }
  422. /**
  423. *
  424. * @param type $config
  425. */
  426. function action_button($config)
  427. {
  428. $caption = (isset($config['caption'])) ? $config['caption'] : "Azione";
  429. if (isset($config['popup']) || isset($config['target']))
  430. {
  431. $config['popup'] = (isset($config['popup'])) ? ",'mywin','" . $config['popup'] . "'" : "";
  432. $action = "javascript:window.open('" . $config['url'] . "'" . $config['popup'] . ")";
  433. } else
  434. {
  435. if (isset($config['confirm']))
  436. {
  437. $action = "javascript:if (confirm('" . addslashes($config['confirm']) . "')) { window.location='" . $config['url'] . "' }";
  438. } else
  439. {
  440. $action = "javascript:window.location='" . $config['url'] . "'";
  441. }
  442. }
  443. $position = (isset($config['position'])) ? $config['position'] : "TR";
  444. $class = (isset($config['class'])) ? $config['class'] : "button";
  445. $this->button("btn_act", $caption, $action, $position, $class);
  446. }
  447. /**
  448. * it exppect parameters like a string, an array, a serialized array 'save|delete|undo...'
  449. * or an associative array, and fill the "$widget->buttons" array
  450. * then buil_buttons will cycle this array to make buttons
  451. *
  452. */
  453. public function set_buttons()
  454. {
  455. $buttons = func_get_args();
  456. //catch buttons => 'save|delete|undo...';
  457. if (is_string($buttons[0]) AND strpos($buttons[0], '|'))
  458. {
  459. $buttons = explode('|', $buttons[0]);
  460. }
  461. if (func_num_args() == 1 AND is_array($buttons[0]))
  462. {
  463. $buttons = $buttons[0];
  464. }
  465. foreach ($buttons as $name => $button)
  466. {
  467. if (is_numeric($name) and is_string($button))
  468. {
  469. if (strpos($button, '|'))
  470. {
  471. list($button['type'], $button['caption']) = explode('|', $button);
  472. } else
  473. {
  474. $button = array('type' => $button);
  475. }
  476. } else
  477. {
  478. if (is_string($button) AND strpos($button, '|'))
  479. {
  480. $btn = array();
  481. @list($btn['name'], $btn['caption'], $btn['url'], $btn['position'], $btn['class']) = explode('|', $button);
  482. $button = $btn;
  483. }
  484. if (!isset($button['type']))
  485. $button['type'] = $name;
  486. }
  487. $this->buttons[] = $button;
  488. }
  489. }
  490. /**
  491. * this function cycle $widget->buttons array and call all "xxx_button()" methods
  492. *
  493. * @todo I should really consider moving it from here
  494. */
  495. function build_buttons()
  496. {
  497. foreach ($this->buttons as $button)
  498. {
  499. $build_button = $button['type'] . "_button";
  500. if (count($button) < 2)
  501. {
  502. $this->$build_button();
  503. } else
  504. {
  505. $this->$build_button($button);
  506. }
  507. }
  508. $this->buttons = array();
  509. }
  510. /**
  511. * "echo $widget" automatically call build() it and display $widget->output
  512. * however explicit build is preferred for a clean code
  513. *
  514. * @return string
  515. */
  516. function __toString()
  517. {
  518. if ($this->output == "")
  519. $this->build();
  520. return $this->output;
  521. }
  522. }