PageRenderTime 95ms CodeModel.GetById 40ms app.highlight 13ms RepoModel.GetById 37ms app.codeStats 0ms

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

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