PageRenderTime 65ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/sally/core/lib/sly/Layout.php

https://bitbucket.org/mediastuttgart/sallycms-0.7
PHP | 451 lines | 157 code | 48 blank | 246 comment | 11 complexity | 6178a70d46d5c6f4548111ea8acb84ab MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright (c) 2013, webvariants GbR, http://www.webvariants.de
  4. *
  5. * This file is released under the terms of the MIT license. You can find the
  6. * complete text in the attached LICENSE file or online at:
  7. *
  8. * http://www.opensource.org/licenses/mit-license.php
  9. */
  10. /**
  11. * Base class for layouts
  12. *
  13. * Layouts are responsible for handling and rendering the HTML head and footer.
  14. * This class lays out the general API for all concrete layouts (like XHTML
  15. * or XHTML5).
  16. *
  17. * @ingroup layout
  18. * @author Zozi
  19. */
  20. abstract class sly_Layout extends sly_Viewable {
  21. protected $title = ''; ///< string
  22. protected $cssCode = ''; ///< string
  23. protected $javaScriptCode = ''; ///< string
  24. protected $favIcon = null; ///< string
  25. protected $cssFiles = array(); ///< array
  26. protected $javaScriptFiles = array(); ///< array
  27. protected $feedFiles = array(); ///< array
  28. protected $bodyAttrs = array(); ///< array
  29. protected $httpMetas = array(); ///< array
  30. protected $metas = array(); ///< array
  31. protected $links = array(); ///< array
  32. protected $content = ''; ///< string
  33. protected $base = ''; ///< string
  34. /**
  35. * Open a new buffer
  36. *
  37. * This method is just a wrapper for ob_start().
  38. */
  39. public function openBuffer() {
  40. ob_start();
  41. }
  42. /**
  43. * Close the buffer
  44. *
  45. * This method closes the buffer and stores the output inside the content
  46. * field of this instance.
  47. */
  48. public function closeBuffer() {
  49. $this->content = ob_get_clean();
  50. }
  51. /**
  52. * Close all buffers
  53. */
  54. public function closeAllBuffers() {
  55. while (ob_get_level()) ob_end_clean();
  56. }
  57. /**
  58. * Set the page content directly
  59. *
  60. * @param string $content
  61. */
  62. public function setContent($content) {
  63. $this->content = trim($content);
  64. }
  65. /**
  66. * Render the page
  67. *
  68. * This method starts a buffer, prints the header, content and footer and
  69. * then returns the complete page's content.
  70. *
  71. * @return string
  72. */
  73. public function render() {
  74. ob_start();
  75. $this->printHeader();
  76. print $this->content;
  77. $this->printFooter();
  78. return ob_get_clean();
  79. }
  80. /**
  81. * Set the page title
  82. *
  83. * @param string $title the new title
  84. */
  85. public function setTitle($title) {
  86. $this->title = $title;
  87. }
  88. /**
  89. * Append something to the title
  90. *
  91. * @param string $title the string to append to the current title
  92. */
  93. public function appendToTitle($title) {
  94. $this->title .= $title;
  95. }
  96. /**
  97. * Set the fav icon
  98. *
  99. * @param string $iconPath the full URI to the favicon
  100. */
  101. public function setFavIcon($iconPath) {
  102. $this->favIcon = trim($iconPath);
  103. }
  104. /**
  105. * Set the base URI
  106. *
  107. * @param string $base the base URI
  108. */
  109. public function setBase($base) {
  110. $this->base = trim($base);
  111. }
  112. /**
  113. * Add inline CSS to the page
  114. *
  115. * Use this method if you have to generate dynamic CSS and add it directly to
  116. * the page, using a <style> tag. All added inline CSS will be printed in a
  117. * single <style> tag.
  118. *
  119. * @param string $css the inline CSS code
  120. */
  121. public function addCSS($css) {
  122. $css = trim($css);
  123. $this->cssCode .= "\n$css";
  124. }
  125. /**
  126. * Add CSS file
  127. *
  128. * This method adds a new CSS file to the layout. Files will be put into
  129. * groups, so that addOns can partially access them. Files must be unique
  130. * (or else the method returns false).
  131. *
  132. * @param string $cssFile path to css file
  133. * @param string $media media attribute fĂźr den CSS link
  134. * @param string $group group files by this param
  135. * @return boolean true if the file was added, false if it already existed
  136. */
  137. public function addCSSFile($cssFile, $media = 'all', $group = 'default') {
  138. $cssFile = trim($cssFile);
  139. foreach ($this->cssFiles as $files) {
  140. foreach ($files as $list) {
  141. foreach ($list as $file) {
  142. if ($file['src'] == $cssFile) return false;
  143. }
  144. }
  145. }
  146. $this->cssFiles[trim($group)][trim($media)][] = array('src' => $cssFile);
  147. return true;
  148. }
  149. /**
  150. * Add inline JavaScript to the page
  151. *
  152. * Use this method if you have to generate dynamic JS and add it directly to
  153. * the page, using a <script> tag. All added inline JS will be printed in a
  154. * single <script> tag.
  155. *
  156. * @param string $javascript the inline JavaScript code
  157. */
  158. public function addJavaScript($javascript) {
  159. $javascript = trim($javascript);
  160. $this->javaScriptCode .= "\n$javascript";
  161. }
  162. /**
  163. * Add JavaScript file
  164. *
  165. * This method adds a new JS file to the layout. Files will be put into
  166. * groups, so that addOns can partially access them. Files must be unique
  167. * (or else the method returns false).
  168. *
  169. * @param string $cssFile path to js file
  170. * @param string $group group files by this param
  171. * @return boolean true if the file was added, false if it already existed
  172. */
  173. public function addJavaScriptFile($javascriptFile, $group = 'default') {
  174. $javascriptFile = trim($javascriptFile);
  175. foreach ($this->javaScriptFiles as $files) {
  176. if (in_array($javascriptFile, $files)) return false;
  177. }
  178. $this->javaScriptFiles[trim($group)][] = $javascriptFile;
  179. return true;
  180. }
  181. /**
  182. * Add an attribute to the body tag
  183. *
  184. * Attributes beginning with 'on' will not be added to the tag, but rather
  185. * as JavaScript event handler using inline JavaScript.
  186. *
  187. * @param string $name attribute name
  188. * @param string $value attribute value
  189. */
  190. public function setBodyAttr($name, $value) {
  191. $name = trim($name);
  192. $value = trim($value);
  193. if (sly_Util_String::startsWith($name, 'on')) {
  194. $this->addJavaScript('window.'.$name.' = function() { '.$value.' }');
  195. }
  196. else {
  197. $this->bodyAttrs[$name] = $value;
  198. }
  199. }
  200. /**
  201. * Get body attribute(s)
  202. *
  203. * @param string $name the attribute name or null for 'all'
  204. * @return mixed either an array or a string
  205. */
  206. public function getBodyAttr($name = null) {
  207. return ($name && isset($this->bodyAttrs[$name])) ? $this->bodyAttrs[$name] : $this->bodyAttrs;
  208. }
  209. /**
  210. * Appends a class name to the body
  211. *
  212. * @param string $class a single or multiple classes as a string (like 'foo bar')
  213. */
  214. public function appendBodyClass($class) {
  215. $classes = $this->getBodyAttr('class');
  216. $classes = $classes ? explode(' ', $classes) : array();
  217. foreach (explode(' ', $class) as $cl) {
  218. $classes[] = $cl;
  219. }
  220. $this->setBodyAttr('class', implode(' ', array_unique($classes)));
  221. }
  222. /**
  223. * Add meta tag
  224. *
  225. * Adds a regular meta tag to the page header.
  226. *
  227. * @param string $name meta name
  228. * @param string $content content attribute of the tag
  229. */
  230. public function addMeta($name, $content) {
  231. $this->metas[trim($name)] = trim($content);
  232. }
  233. /**
  234. * Add a http-equiv meta tag
  235. *
  236. * Adds a meta tag for HTTP equivalents to the page header. Use this to
  237. * specify the content-type.
  238. *
  239. * @param string $name meta name
  240. * @param string $content content attribute of the tag
  241. */
  242. public function addHttpMeta($name, $content) {
  243. $this->httpMetas[trim($name)] = trim($content);
  244. }
  245. /**
  246. * Add generic link
  247. *
  248. * This methods adds a generic <link> tag to the head. Use specialized
  249. * methods (like addCSSFile) whenever possible. Note that the links are not
  250. * made unique!
  251. *
  252. * @param string $rel rel attribute value
  253. * @param string $href href attribute value
  254. * @param string $type type attribute value
  255. * @param string $title title attribute value
  256. */
  257. public function addLink($rel, $href, $type = '', $title= '') {
  258. $this->links[] = array('rel' => trim($rel), 'href' => trim($href), 'type' => trim($type), 'title' => trim($title));
  259. }
  260. /**
  261. * Add a feed
  262. *
  263. * This method is a specialized version of addLink() and adds a RSS/Atom link
  264. * to the page header, automatically setting the title and type.
  265. *
  266. * @param string $feedFile the URL to the feed
  267. * @param string $type the type (rss, rss1, rss2 or atom)
  268. */
  269. public function addFeedFile($feedFile, $type = '') {
  270. if (!in_array($type, array('rss', 'rss1', 'rss2', 'atom'))) {
  271. $type = 'rss';
  272. }
  273. static $types = array('rss' => 'rss', 'rss1' => 'rss', 'rss2' => 'rss', 'atom' => 'atom');
  274. static $titles = array('rss' => 'RSS-Feed', 'rss1' => 'RSS-Feed 1.0', 'rss2' => 'RSS-Feed 2.0', 'atom' => 'Atom-Feed');
  275. $title = $titles[$type];
  276. $type = 'application/'.$types[$type].'+xml';
  277. $this->addLink('alternate', $feedFile, $type, $title);
  278. }
  279. /**
  280. * Write the inline CSS
  281. *
  282. * This method will filter the inline CSS with the event HEADER_CSS and, if
  283. * it's not empty, writes it by calling the layout specific
  284. * printCSSConcrete() method.
  285. */
  286. protected function printCSS() {
  287. $this->cssCode = sly_Core::dispatcher()->filter('HEADER_CSS', $this->cssCode);
  288. if (!empty($this->cssCode)) $this->printCSSConcrete();
  289. }
  290. /**
  291. * Write the CSS files
  292. *
  293. * This method will filter the CSS files with the event HEADER_CSS_FILES and,
  294. * if they're not empty, write them by calling the layout specific
  295. * printCSSFilesConcrete() method.
  296. */
  297. protected function printCSSFiles() {
  298. $this->cssFiles = sly_Core::dispatcher()->filter('HEADER_CSS_FILES', $this->cssFiles);
  299. $this->printCSSFilesConcrete();
  300. }
  301. /**
  302. * Write the inline JavaScript
  303. *
  304. * This method will filter the inline JavaScript with the event
  305. * HEADER_JAVASCRIPT and, if it's not empty, writes it by calling the layout
  306. * specific printJavaScriptConcrete() method.
  307. */
  308. protected function printJavaScript() {
  309. $this->javaScriptCode = sly_Core::dispatcher()->filter('HEADER_JAVASCRIPT', $this->javaScriptCode);
  310. if (!empty($this->javaScriptCode)) $this->printJavaScriptConcrete();
  311. }
  312. /**
  313. * Write the JavaScript files
  314. *
  315. * This method will filter the JS files with the event
  316. * HEADER_JAVASCRIPT_FILES and, if they're not empty, write them by calling
  317. * the layout specific printJavaScriptFilesConcrete() method.
  318. */
  319. protected function printJavaScriptFiles() {
  320. $this->javaScriptFiles = sly_Core::dispatcher()->filter('HEADER_JAVASCRIPT_FILES', $this->javaScriptFiles);
  321. $this->printJavaScriptFilesConcrete();
  322. }
  323. /**
  324. * Print all links
  325. *
  326. * This function only loops over all links and calls printLink() for each
  327. * one.
  328. */
  329. protected function printLinks() {
  330. foreach ($this->links as $link) {
  331. $this->printLink($link);
  332. }
  333. }
  334. /**
  335. * Print the inline CSS code
  336. */
  337. protected abstract function printCSSConcrete();
  338. /**
  339. * Print the list of CSS files
  340. */
  341. protected abstract function printCSSFilesConcrete();
  342. /**
  343. * Print the inline JavaScript code
  344. */
  345. protected abstract function printJavaScriptConcrete();
  346. /**
  347. * Print the list of JS files
  348. */
  349. protected abstract function printJavaScriptFilesConcrete();
  350. /**
  351. * Print the body attributes
  352. */
  353. protected abstract function printBodyAttrs();
  354. /**
  355. * Print regular meta tag
  356. */
  357. protected abstract function printMetas();
  358. /**
  359. * Print HTTP meta tag
  360. */
  361. protected abstract function printHttpMetas();
  362. /**
  363. * Prints a <link> tag
  364. *
  365. * @param array $attributes a hash with all attributes (name => value)
  366. */
  367. protected abstract function printLink($attributes);
  368. /**
  369. * Print the header
  370. *
  371. * Starts the page by writing the html, head, title and body tag (no meta,
  372. * no doctype, no links, no script, ...). Most layouts will override this
  373. * method.
  374. */
  375. public function printHeader() {
  376. print '<html><head><title>'.sly_html(trim($this->title)).'</title></head><body>';
  377. }
  378. /**
  379. * Print the footer
  380. *
  381. * Prints the closing body and html tags.
  382. */
  383. public function printFooter() {
  384. $this->printJavaScriptFiles();
  385. $this->printJavaScript();
  386. print '</body></html>';
  387. }
  388. /**
  389. * Get the full path for a view
  390. *
  391. * This methods prepends the filename of a specific view with its path. If
  392. * the view is not found inside the core, an exception is thrown.
  393. *
  394. * @throws sly_Exception if the view could not be found
  395. * @param string $file the relative filename
  396. * @return string the full path to the view file
  397. */
  398. protected function getViewFile($file) {
  399. $full = SLY_COREFOLDER.'/views/'.$file;
  400. if (file_exists($full)) return $full;
  401. throw new sly_Exceptiont(t('view_not_found', $file));
  402. }
  403. }