PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/Template.php

https://github.com/jayalfredprufrock/CodeIgniter-Template-Library
PHP | 760 lines | 397 code | 151 blank | 212 comment | 90 complexity | 2b407428a04b47ecd4b8a7f89dba0642 MD5 | raw file
  1. <?php
  2. /**
  3. * @name CodeIgniter Template Library
  4. * @author Jens Segers
  5. * @link http://www.jenssegers.be
  6. * @license MIT License Copyright (c) 2012 Jens Segers
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. if (!defined("BASEPATH"))
  27. exit("No direct script access allowed");
  28. class Template {
  29. /* default values */
  30. private $_template = 'template';
  31. private $_parser = FALSE;
  32. private $_cache_ttl = 0;
  33. private $_widget_path = '';
  34. private $_autoload_view_css = FALSE;
  35. private $_autoload_view_css_path = '';
  36. private $_autoload_view_js = FALSE;
  37. private $_autoload_view_js_path = '';
  38. private $_ci;
  39. private $_partials = array();
  40. /**
  41. * Construct with configuration array. Codeigniter will use the config file otherwise
  42. * @param array $config
  43. */
  44. public function __construct($config = array()) {
  45. $this->_ci = & get_instance();
  46. if (!empty($config)) {
  47. $this->initialize($config);
  48. }
  49. log_message('debug', 'Template library initialized');
  50. }
  51. /**
  52. * Initialize with configuration array
  53. * @param array $config
  54. * @return Template
  55. */
  56. public function initialize($config = array()) {
  57. foreach ($config as $key => $val) {
  58. $this->{'_' . $key} = $val;
  59. }
  60. if ($this->_widget_path == '') {
  61. $this->_widget_path = APPPATH . 'widgets/';
  62. }
  63. if ($this->_parser && !class_exists('CI_Parser')) {
  64. $this->_ci->load->library('parser');
  65. }
  66. return $this;
  67. }
  68. /**
  69. * Set a partial's content. This will create a new partial when not existing
  70. * @param string $index
  71. * @param mixed $value
  72. */
  73. public function __set($name, $value) {
  74. $this->partial($name)->set($value);
  75. }
  76. /**
  77. * Access to partials for method chaining
  78. * @param string $name
  79. * @return mixed
  80. */
  81. public function __get($name) {
  82. return $this->partial($name);
  83. }
  84. /**
  85. * Check if a partial exists
  86. * @param string $index
  87. * @return boolean
  88. */
  89. public function exists($index) {
  90. return array_key_exists($index, $this->_partials);
  91. }
  92. /**
  93. * Set the template file
  94. * @param string $template
  95. */
  96. public function set_template($template) {
  97. $this->_template = $template;
  98. }
  99. /*
  100. * Returns the currently set template.
  101. * return string template
  102. */
  103. public function get_template(){
  104. return $this->_template;
  105. }
  106. /*
  107. * Returns an array of all the templates partials
  108. * @return partial array
  109. */
  110. public function get_partials(){
  111. return $this->_partials;
  112. }
  113. /**
  114. * Publish the template with the current partials
  115. * You can manually pass a template file with extra data, or use the default template from the config file
  116. * @param string $template
  117. * @param array $data
  118. */
  119. public function publish($template = FALSE, $data = array(), $return = FALSE) {
  120. if (is_array($template) || is_object($template)) {
  121. $data = $template;
  122. } else if ($template) {
  123. $this->_template = $template;
  124. }
  125. if (!$this->_template) {
  126. show_error('There was no template file selected for the current template');
  127. }
  128. if (is_array($data) || is_object($data)) {
  129. foreach ($data as $name => $content) {
  130. $this->partial($name)->set($content);
  131. }
  132. }
  133. unset($data);
  134. //autoload view assets
  135. if ($this->_autoload_view_css || $this->_autoload_view_js){
  136. foreach($this->_partials as $partial){
  137. if ($views = $partial->get_views()){
  138. foreach($views as $view){
  139. if ($this->_autoload_view_css){
  140. $css_file = $this->_autoload_view_css_path . $view . '.css';
  141. //try to load css and js with same name
  142. if (file_exists($css_file)){
  143. $this->stylesheet->add(file_get_contents($css_file), FALSE, TRUE);
  144. }
  145. }
  146. if ($this->_autoload_view_js){
  147. $js_file = $this->_autoload_view_js_path . $view . '.js';
  148. if (file_exists($js_file)){
  149. $this->javascript->add(file_get_contents($js_file), TRUE);
  150. }
  151. }
  152. }
  153. }
  154. }
  155. }
  156. if ($this->_parser) {
  157. return $this->_ci->parser->parse($this->_template, $this->_partials, $return);
  158. } else {
  159. return $this->_ci->load->view($this->_template, $this->_partials, $return);
  160. }
  161. }
  162. /**
  163. * Create a partial object with an optional default content
  164. * Can be usefull to use straight from the template file
  165. * @param string $name
  166. * @param string $default
  167. * @return Partial
  168. */
  169. public function partial($name, $default = FALSE) {
  170. if ($this->exists($name)) {
  171. $partial = $this->_partials[$name];
  172. } else {
  173. // create new partial
  174. $partial = new Partial($name);
  175. if ($this->_cache_ttl) {
  176. $partial->cache($this->_cache_ttl);
  177. }
  178. // detect local triggers
  179. if (method_exists($this, 'trigger_' . $name)) {
  180. $partial->bind($this, 'trigger_' . $name);
  181. }
  182. $this->_partials[$name] = $partial;
  183. }
  184. if ($partial->content() === FALSE && $default !== FALSE) {
  185. $partial->set($default);
  186. }
  187. return $partial;
  188. }
  189. /**
  190. * Create a widget object with optional parameters
  191. * Can be usefull to use straight from the template file
  192. * @param string $name
  193. * @param array $data
  194. * @return Widget
  195. */
  196. public function widget($name, $data = array()) {
  197. $class = str_replace('.php', '', trim($name, '/'));
  198. // determine path and widget class name
  199. $path = $this->_widget_path;
  200. if (($last_slash = strrpos($class, '/')) !== FALSE) {
  201. $path += substr($class, 0, $last_slash);
  202. $class = substr($class, $last_slash + 1);
  203. }
  204. // new widget
  205. if(!class_exists($class)) {
  206. // try both lowercase and capitalized versions
  207. foreach (array(ucfirst($class), strtolower($class)) as $class) {
  208. if (file_exists($path . $class . '.php')) {
  209. include_once ($path . $class . '.php');
  210. // found the file, stop looking
  211. break;
  212. }
  213. }
  214. }
  215. if (!class_exists($class)) {
  216. show_error("Widget '" . $class . "' was not found.");
  217. }
  218. return new $class($class, $data);
  219. }
  220. /**
  221. * Enable cache for all partials with TTL, default TTL is 60
  222. * @param int $ttl
  223. * @param mixed $identifier
  224. */
  225. public function cache($ttl = 60, $identifier = '') {
  226. foreach ($this->_partials as $partial) {
  227. $partial->cache($ttl, $identifier);
  228. }
  229. $this->_cache_ttl = $ttl;
  230. }
  231. // ---- TRIGGERS -----------------------------------------------------------------
  232. /**
  233. * Stylesheet trigger
  234. * @param string $source
  235. */
  236. public function trigger_stylesheet($url, $attributes = FALSE, $embed = FALSE) {
  237. $attributesString = "";
  238. if ($attributes !== FALSE){
  239. // legacy support for media
  240. if (is_string($attributes)) {
  241. $attributesString = array('media' => $attributes);
  242. }
  243. if (is_array($attributes)) {
  244. $attributeString = "";
  245. foreach ($attributes as $key => $value) {
  246. $attributeString .= $key . '="' . $value . '" ';
  247. }
  248. }
  249. }
  250. if ($embed){
  251. return '<style type="text/css" ' . $attributesString . '>' . $url . '</style>' . "\n\t";
  252. }
  253. else {
  254. // array support
  255. if (is_array($url)) {
  256. $return = '';
  257. foreach ($url as $u) {
  258. $return .= $this->trigger_stylesheet($u, $attributes);
  259. }
  260. return $return;
  261. }
  262. if (!stristr($url, 'http://') && !stristr($url, 'https://') && substr($url, 0, 2) != '//') {
  263. $url = $this->_ci->config->item('base_url') . $url;
  264. }
  265. return '<link rel="stylesheet" href="' . htmlspecialchars(strip_tags($url)) . '" ' . $attributeString . '>' . "\n\t";
  266. }
  267. }
  268. /**
  269. * Javascript trigger
  270. * @param string $source
  271. */
  272. public function trigger_javascript($url, $embed = FALSE) {
  273. if ($embed){
  274. return '<script type="text/javascript">' . $url . '</script>' . "\n\t";
  275. }
  276. else {
  277. // array support
  278. if (is_array($url)) {
  279. $return = '';
  280. foreach ($url as $u) {
  281. $return .= $this->trigger_javascript($u);
  282. }
  283. return $return;
  284. }
  285. if (!stristr($url, 'http://') && !stristr($url, 'https://') && substr($url, 0, 2) != '//') {
  286. $url = $this->_ci->config->item('base_url') . $url;
  287. }
  288. return '<script src="' . htmlspecialchars(strip_tags($url)) . '"></script>' . "\n\t";
  289. }
  290. }
  291. /**
  292. * Meta trigger
  293. * @param string $name
  294. * @param mixed $value
  295. * @param enum $type
  296. */
  297. public function trigger_meta($name, $value, $type = 'meta') {
  298. $name = htmlspecialchars(strip_tags($name));
  299. $value = htmlspecialchars(strip_tags($value));
  300. if ($name == 'keywords' and !strpos($value, ',')) {
  301. $content = preg_replace('/[\s]+/', ', ', trim($value));
  302. }
  303. switch ($type) {
  304. case 'meta' :
  305. $content = '<meta name="' . $name . '" content="' . $value . '">' . "\n\t";
  306. break;
  307. case 'link' :
  308. $content = '<link rel="' . $name . '" href="' . $value . '">' . "\n\t";
  309. break;
  310. }
  311. return $content;
  312. }
  313. /**
  314. * Title trigger, keeps it clean
  315. * @param string $name
  316. * @param mixed $value
  317. * @param enum $type
  318. */
  319. public function trigger_title($title) {
  320. return htmlspecialchars(strip_tags($title));
  321. }
  322. /**
  323. * Title trigger, keeps it clean
  324. * @param string $name
  325. * @param mixed $value
  326. * @param enum $type
  327. */
  328. public function trigger_description($description) {
  329. return htmlspecialchars(strip_tags($description));
  330. }
  331. }
  332. class Partial {
  333. protected $_ci, $_content, $_name, $_cache_ttl = 0, $_cached = false, $_identifier, $_trigger;
  334. protected $_args = array();
  335. protected $_views = array();
  336. /**
  337. * Construct with optional parameters
  338. * @param array $args
  339. */
  340. public function __construct($name, $args = array()) {
  341. $this->_ci = &get_instance();
  342. $this->_args = $args;
  343. $this->_name = $name;
  344. $this->_content = FALSE;
  345. }
  346. /**
  347. * Gives access to codeigniter's functions from this class if needed
  348. * This will be handy in extending classes
  349. * @param string $index
  350. */
  351. function __get($name) {
  352. return $this->_ci->$name;
  353. }
  354. function get_views(){
  355. return $this->_views();
  356. }
  357. /**
  358. * Alias methods
  359. */
  360. function __call($name, $args) {
  361. switch ($name) {
  362. case 'default' :
  363. return call_user_func_array(array($this, 'set_default'), $args);
  364. break;
  365. case 'default_view' :
  366. return call_user_func_array(array($this, 'set_default_view'), $args);
  367. break;
  368. case 'add' :
  369. return call_user_func_array(array($this, 'append'), $args);
  370. break;
  371. }
  372. }
  373. /**
  374. * Returns the content when converted to a string
  375. * @return string
  376. */
  377. public function __toString() {
  378. return (string) $this->content();
  379. }
  380. /**
  381. * Returns the content
  382. * @return string
  383. */
  384. public function content() {
  385. if ($this->_cache_ttl && !$this->_cached) {
  386. $this->cache->save($this->cache_id(), $this->_content, $this->_cache_ttl);
  387. }
  388. return $this->_content;
  389. }
  390. /**
  391. * Overwrite the content
  392. * @param mixed $content
  393. * @return Partial
  394. */
  395. public function set() {
  396. if (!$this->_cached) {
  397. $this->_content = (string) $this->trigger(func_get_args());
  398. }
  399. return $this;
  400. }
  401. /**
  402. * Append something to the content
  403. * @param mixed $content
  404. * @return Partial
  405. */
  406. public function append() {
  407. if (!$this->_cached) {
  408. $this->_content .= (string) $this->trigger(func_get_args());
  409. }
  410. return $this;
  411. }
  412. /**
  413. * Prepend something to the content
  414. * @param mixed $content
  415. * @return Partial
  416. */
  417. public function prepend() {
  418. if (!$this->_cached) {
  419. $this->_content = (string) $this->trigger(func_get_args()) . $this->_content;
  420. }
  421. return $this;
  422. }
  423. /**
  424. * Set content if partial is empty
  425. * @param mixed $default
  426. * @return Partial
  427. */
  428. public function set_default($default) {
  429. if (!$this->_cached) {
  430. if ($this->_content === FALSE) {
  431. $this->_content = $default;
  432. }
  433. }
  434. return $this;
  435. }
  436. public function set_default_view($view, $data = array(), $parse = FALSE){
  437. if (!$this->_cached) {
  438. if ($this->_content === FALSE) {
  439. if ($parse){
  440. $this->parse($view, $data, TRUE);
  441. }
  442. else {
  443. $this->view($view, $data, TRUE);
  444. }
  445. }
  446. }
  447. return $this;
  448. }
  449. /**
  450. * Load a view inside this partial, overwrite if wanted
  451. * @param string $view
  452. * @param array $data
  453. * @param bool $overwrite
  454. * @return Partial
  455. */
  456. public function view($view, $data = array(), $overwrite = false) {
  457. if (!$this->_cached) {
  458. // better object to array
  459. if (is_object($data)) {
  460. $array = array();
  461. foreach ($data as $k => $v) {
  462. $array[$k] = $v;
  463. }
  464. $data = $array;
  465. }
  466. $content = $this->_ci->load->view($view, $data, true);
  467. if ($overwrite) {
  468. $this->_views = array($view);
  469. $this->set($content);
  470. } else {
  471. $this->_views[] = $view;
  472. $this->append($content);
  473. }
  474. }
  475. return $this;
  476. }
  477. /**
  478. * Parses a view inside this partial, overwrite if wanted
  479. * @param string $view
  480. * @param array $data
  481. * @param bool $overwrite
  482. * @return Partial
  483. */
  484. public function parse($view, $data = array(), $overwrite = false) {
  485. if (!$this->_cached) {
  486. if (!class_exists('CI_Parser')) {
  487. $this->_ci->load->library('parser');
  488. }
  489. // better object to array
  490. if (is_object($data)) {
  491. $array = array();
  492. foreach ($data as $k => $v) {
  493. $array[$k] = $v;
  494. }
  495. $data = $array;
  496. }
  497. $content = $this->_ci->parser->parse($view, $data, true);
  498. if ($overwrite) {
  499. $this->_views = array($view);
  500. $this->set($content);
  501. } else {
  502. $this->_views[] = $view;
  503. $this->append($content);
  504. }
  505. }
  506. return $this;
  507. }
  508. /**
  509. * Loads a widget inside this partial, overwrite if wanted
  510. * @param string $name
  511. * @param array $data
  512. * @param bool $overwrite
  513. * @return Partial
  514. */
  515. public function widget($name, $data = array(), $overwrite = false) {
  516. if (!$this->_cached) {
  517. $widget = $this->template->widget($name, $data);
  518. if ($overwrite) {
  519. $this->set($widget->content());
  520. } else {
  521. $this->append($widget->content());
  522. }
  523. }
  524. return $this;
  525. }
  526. /**
  527. * Enable cache with TTL, default TTL is 60
  528. * @param int $ttl
  529. * @param mixed $identifier
  530. */
  531. public function cache($ttl = 60, $identifier = '') {
  532. if (!class_exists('CI_Cache')) {
  533. $this->_ci->load->driver('cache', array('adapter' => 'file'));
  534. }
  535. $this->_cache_ttl = $ttl;
  536. $this->_identifier = $identifier;
  537. if ($cached = $this->_ci->cache->get($this->cache_id())) {
  538. $this->_cached = true;
  539. $this->_content = $cached;
  540. }
  541. return $this;
  542. }
  543. /**
  544. * Used for cache identification
  545. * @return string
  546. */
  547. private function cache_id() {
  548. if ($this->_identifier) {
  549. return $this->_name . '_' . $this->_identifier . '_' . md5(get_class($this) . implode('', $this->_args));
  550. } else {
  551. return $this->_name . '_' . md5(get_class($this) . implode('', $this->_args));
  552. }
  553. }
  554. /**
  555. * Trigger returns the result if a trigger is set
  556. * @param array $args
  557. * @return string
  558. */
  559. public function trigger($args) {
  560. if (!$this->_trigger) {
  561. return implode('', $args);
  562. } else {
  563. return call_user_func_array($this->_trigger, $args);
  564. }
  565. }
  566. /**
  567. * Bind a trigger function
  568. * Can be used like bind($this, "function") or bind("function")
  569. * @param mixed $arg
  570. */
  571. public function bind() {
  572. if ($count = func_num_args()) {
  573. $args = func_get_args();
  574. if ($count >= 2) {
  575. $obj = array_shift($args);
  576. $func = array_pop($args);
  577. foreach ($args as $trigger) {
  578. $obj = $obj->$trigger;
  579. }
  580. $this->_trigger = array($obj, $func);
  581. } else {
  582. $this->_trigger = reset($args);
  583. }
  584. } else {
  585. $this->_trigger = FALSE;
  586. }
  587. }
  588. }
  589. class Widget extends Partial {
  590. /* (non-PHPdoc)
  591. * @see Partial::content()
  592. */
  593. public function content() {
  594. if (!$this->_cached) {
  595. if (method_exists($this, 'display')) {
  596. // capture output
  597. ob_start();
  598. $this->display($this->_args);
  599. $buffer = ob_get_clean();
  600. // if no content is produced but there was direct ouput we set
  601. // that output as content
  602. if (!$this->_content && $buffer) {
  603. $this->set($buffer);
  604. }
  605. }
  606. }
  607. return parent::content();
  608. }
  609. }