PageRenderTime 127ms CodeModel.GetById 60ms app.highlight 29ms RepoModel.GetById 32ms app.codeStats 0ms

/libraries/joomla/document/html/html.php

http://github.com/joomla/joomla-platform
PHP | 710 lines | 480 code | 58 blank | 172 comment | 47 complexity | cfaa3b0b6e242e96f5a78a633cf55168 MD5 | raw file
  1<?php
  2/**
  3 * @package     Joomla.Platform
  4 * @subpackage  Document
  5 *
  6 * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7 * @license     GNU General Public License version 2 or later; see LICENSE
  8 */
  9
 10defined('JPATH_PLATFORM') or die;
 11
 12jimport('joomla.utilities.utility');
 13
 14/**
 15 * DocumentHTML class, provides an easy interface to parse and display a HTML document
 16 *
 17 * @package     Joomla.Platform
 18 * @subpackage  Document
 19 * @since       11.1
 20 */
 21class JDocumentHTML extends JDocument
 22{
 23	/**
 24	 * Array of Header <link> tags
 25	 *
 26	 * @var    array
 27	 * @since  11.1
 28	 */
 29	public $_links = array();
 30
 31	/**
 32	 * Array of custom tags
 33	 *
 34	 * @var    array
 35	 * @since  11.1
 36	 */
 37	public $_custom = array();
 38
 39	/**
 40	 * Name of the template
 41	 *
 42	 * @var    string
 43	 * @since  11.1
 44	 */
 45	public $template = null;
 46
 47	/**
 48	 * Base url
 49	 *
 50	 * @var    string
 51	 * @since  11.1
 52	 */
 53	public $baseurl = null;
 54
 55	/**
 56	 * Array of template parameters
 57	 *
 58	 * @var    array
 59	 * @since  11.1
 60	 */
 61	public $params = null;
 62
 63	/**
 64	 * File name
 65	 *
 66	 * @var    array
 67	 * @since  11.1
 68	 */
 69	public $_file = null;
 70
 71	/**
 72	 * String holding parsed template
 73	 *
 74	 * @var    string
 75	 * @since  11.1
 76	 */
 77	protected $_template = '';
 78
 79	/**
 80	 * Array of parsed template JDoc tags
 81	 *
 82	 * @var    array
 83	 * @since  11.1
 84	 */
 85	protected $_template_tags = array();
 86
 87	/**
 88	 * Integer with caching setting
 89	 *
 90	 * @var    integer
 91	 * @since  11.1
 92	 */
 93	protected $_caching = null;
 94
 95	/**
 96	 * Set to true when the document should be output as HTML5
 97	 *
 98	 * @var    boolean
 99	 * @since  12.1
100	 */
101	private $_html5 = null;
102
103	/**
104	 * Class constructor
105	 *
106	 * @param   array  $options  Associative array of options
107	 *
108	 * @since   11.1
109	 */
110	public function __construct($options = array())
111	{
112		parent::__construct($options);
113
114		// Set document type
115		$this->_type = 'html';
116
117		// Set default mime type and document metadata (meta data syncs with mime type by default)
118		$this->setMimeEncoding('text/html');
119	}
120
121	/**
122	 * Get the HTML document head data
123	 *
124	 * @return  array  The document head data in array form
125	 *
126	 * @since   11.1
127	 */
128	public function getHeadData()
129	{
130		$data = array();
131		$data['title']       = $this->title;
132		$data['description'] = $this->description;
133		$data['link']        = $this->link;
134		$data['metaTags']    = $this->_metaTags;
135		$data['links']       = $this->_links;
136		$data['styleSheets'] = $this->_styleSheets;
137		$data['style']       = $this->_style;
138		$data['scripts']     = $this->_scripts;
139		$data['script']      = $this->_script;
140		$data['custom']      = $this->_custom;
141
142		return $data;
143	}
144
145	/**
146	 * Set the HTML document head data
147	 *
148	 * @param   array  $data  The document head data in array form
149	 *
150	 * @return  JDocumentHTML instance of $this to allow chaining
151	 *
152	 * @since   11.1
153	 */
154	public function setHeadData($data)
155	{
156		if (empty($data) || !is_array($data))
157		{
158			return;
159		}
160
161		$this->title = (isset($data['title']) && !empty($data['title'])) ? $data['title'] : $this->title;
162		$this->description = (isset($data['description']) && !empty($data['description'])) ? $data['description'] : $this->description;
163		$this->link = (isset($data['link']) && !empty($data['link'])) ? $data['link'] : $this->link;
164		$this->_metaTags = (isset($data['metaTags']) && !empty($data['metaTags'])) ? $data['metaTags'] : $this->_metaTags;
165		$this->_links = (isset($data['links']) && !empty($data['links'])) ? $data['links'] : $this->_links;
166		$this->_styleSheets = (isset($data['styleSheets']) && !empty($data['styleSheets'])) ? $data['styleSheets'] : $this->_styleSheets;
167		$this->_style = (isset($data['style']) && !empty($data['style'])) ? $data['style'] : $this->_style;
168		$this->_scripts = (isset($data['scripts']) && !empty($data['scripts'])) ? $data['scripts'] : $this->_scripts;
169		$this->_script = (isset($data['script']) && !empty($data['script'])) ? $data['script'] : $this->_script;
170		$this->_custom = (isset($data['custom']) && !empty($data['custom'])) ? $data['custom'] : $this->_custom;
171
172		return $this;
173	}
174
175	/**
176	 * Merge the HTML document head data
177	 *
178	 * @param   array  $data  The document head data in array form
179	 *
180	 * @return  JDocumentHTML instance of $this to allow chaining
181	 *
182	 * @since   11.1
183	 */
184	public function mergeHeadData($data)
185	{
186
187		if (empty($data) || !is_array($data))
188		{
189			return;
190		}
191
192		$this->title = (isset($data['title']) && !empty($data['title']) && !stristr($this->title, $data['title']))
193			? $this->title . $data['title']
194			: $this->title;
195		$this->description = (isset($data['description']) && !empty($data['description']) && !stristr($this->description, $data['description']))
196			? $this->description . $data['description']
197			: $this->description;
198		$this->link = (isset($data['link'])) ? $data['link'] : $this->link;
199
200		if (isset($data['metaTags']))
201		{
202			foreach ($data['metaTags'] as $type1 => $data1)
203			{
204				$booldog = $type1 == 'http-equiv' ? true : false;
205
206				foreach ($data1 as $name2 => $data2)
207				{
208					$this->setMetaData($name2, $data2, $booldog);
209				}
210			}
211		}
212
213		$this->_links = (isset($data['links']) && !empty($data['links']) && is_array($data['links']))
214			? array_unique(array_merge($this->_links, $data['links']))
215			: $this->_links;
216		$this->_styleSheets = (isset($data['styleSheets']) && !empty($data['styleSheets']) && is_array($data['styleSheets']))
217			? array_merge($this->_styleSheets, $data['styleSheets'])
218			: $this->_styleSheets;
219
220		if (isset($data['style']))
221		{
222			foreach ($data['style'] as $type => $stdata)
223			{
224				if (!isset($this->_style[strtolower($type)]) || !stristr($this->_style[strtolower($type)], $stdata))
225				{
226					$this->addStyleDeclaration($stdata, $type);
227				}
228			}
229		}
230
231		$this->_scripts = (isset($data['scripts']) && !empty($data['scripts']) && is_array($data['scripts']))
232			? array_merge($this->_scripts, $data['scripts'])
233			: $this->_scripts;
234
235		if (isset($data['script']))
236		{
237			foreach ($data['script'] as $type => $sdata)
238			{
239				if (!isset($this->_script[strtolower($type)]) || !stristr($this->_script[strtolower($type)], $sdata))
240				{
241					$this->addScriptDeclaration($sdata, $type);
242				}
243			}
244		}
245
246		$this->_custom = (isset($data['custom']) && !empty($data['custom']) && is_array($data['custom']))
247			? array_unique(array_merge($this->_custom, $data['custom']))
248			: $this->_custom;
249
250		return $this;
251	}
252
253	/**
254	 * Adds <link> tags to the head of the document
255	 *
256	 * $relType defaults to 'rel' as it is the most common relation type used.
257	 * ('rev' refers to reverse relation, 'rel' indicates normal, forward relation.)
258	 * Typical tag: <link href="index.php" rel="Start">
259	 *
260	 * @param   string  $href      The link that is being related.
261	 * @param   string  $relation  Relation of link.
262	 * @param   string  $relType   Relation type attribute.  Either rel or rev (default: 'rel').
263	 * @param   array   $attribs   Associative array of remaining attributes.
264	 *
265	 * @return  JDocumentHTML instance of $this to allow chaining
266	 *
267	 * @since   11.1
268	 */
269	public function addHeadLink($href, $relation, $relType = 'rel', $attribs = array())
270	{
271		$this->_links[$href]['relation'] = $relation;
272		$this->_links[$href]['relType'] = $relType;
273		$this->_links[$href]['attribs'] = $attribs;
274
275		return $this;
276	}
277
278	/**
279	 * Adds a shortcut icon (favicon)
280	 *
281	 * This adds a link to the icon shown in the favorites list or on
282	 * the left of the url in the address bar. Some browsers display
283	 * it on the tab, as well.
284	 *
285	 * @param   string  $href      The link that is being related.
286	 * @param   string  $type      File type
287	 * @param   string  $relation  Relation of link
288	 *
289	 * @return  JDocumentHTML instance of $this to allow chaining
290	 *
291	 * @since   11.1
292	 */
293	public function addFavicon($href, $type = 'image/vnd.microsoft.icon', $relation = 'shortcut icon')
294	{
295		$href = str_replace('\\', '/', $href);
296		$this->addHeadLink($href, $relation, 'rel', array('type' => $type));
297
298		return $this;
299	}
300
301	/**
302	 * Adds a custom HTML string to the head block
303	 *
304	 * @param   string  $html  The HTML to add to the head
305	 *
306	 * @return  JDocumentHTML instance of $this to allow chaining
307	 *
308	 * @since   11.1
309	 */
310	public function addCustomTag($html)
311	{
312		$this->_custom[] = trim($html);
313
314		return $this;
315	}
316
317	/**
318	 * Returns whether the document is set up to be output as HTML5
319	 *
320	 * @return  Boolean true when HTML5 is used
321	 *
322	 * @since   12.1
323	 */
324	public function isHtml5()
325	{
326		return $this->_html5;
327	}
328
329	/**
330	 * Sets whether the document should be output as HTML5
331	 *
332	 * @param   bool  $state  True when HTML5 should be output
333	 *
334	 * @return  JDocumentHTML instance of $this to allow chaining
335	 *
336	 * @since   12.1
337	 */
338	public function setHtml5($state)
339	{
340		if (is_bool($state))
341		{
342			$this->_html5 = $state;
343		}
344
345		return $this;
346	}
347
348	/**
349	 * Get the contents of a document include
350	 *
351	 * @param   string  $type     The type of renderer
352	 * @param   string  $name     The name of the element to render
353	 * @param   array   $attribs  Associative array of remaining attributes.
354	 *
355	 * @return  The output of the renderer
356	 *
357	 * @since   11.1
358	 */
359	public function getBuffer($type = null, $name = null, $attribs = array())
360	{
361		// If no type is specified, return the whole buffer
362		if ($type === null)
363		{
364			return parent::$_buffer;
365		}
366
367		$title = (isset($attribs['title'])) ? $attribs['title'] : null;
368
369		if (isset(parent::$_buffer[$type][$name][$title]))
370		{
371			return parent::$_buffer[$type][$name][$title];
372		}
373
374		$renderer = $this->loadRenderer($type);
375
376		if ($this->_caching == true && $type == 'modules')
377		{
378			$cache = JFactory::getCache('com_modules', '');
379			$hash = md5(serialize(array($name, $attribs, null, $renderer)));
380			$cbuffer = $cache->get('cbuffer_' . $type);
381
382			if (isset($cbuffer[$hash]))
383			{
384				return JCache::getWorkarounds($cbuffer[$hash], array('mergehead' => 1));
385			}
386			else
387			{
388				$options = array();
389				$options['nopathway'] = 1;
390				$options['nomodules'] = 1;
391				$options['modulemode'] = 1;
392
393				$this->setBuffer($renderer->render($name, $attribs, null), $type, $name);
394				$data = parent::$_buffer[$type][$name][$title];
395
396				$tmpdata = JCache::setWorkarounds($data, $options);
397
398				$cbuffer[$hash] = $tmpdata;
399
400				$cache->store($cbuffer, 'cbuffer_' . $type);
401			}
402		}
403		else
404		{
405			$this->setBuffer($renderer->render($name, $attribs, null), $type, $name, $title);
406		}
407
408		return parent::$_buffer[$type][$name][$title];
409	}
410
411	/**
412	 * Set the contents a document includes
413	 *
414	 * @param   string  $content  The content to be set in the buffer.
415	 * @param   array   $options  Array of optional elements.
416	 *
417	 * @return  JDocumentHTML instance of $this to allow chaining
418	 *
419	 * @since   11.1
420	 */
421	public function setBuffer($content, $options = array())
422	{
423		// The following code is just for backward compatibility.
424		if (func_num_args() > 1 && !is_array($options))
425		{
426			$args = func_get_args();
427			$options = array();
428			$options['type'] = $args[1];
429			$options['name'] = (isset($args[2])) ? $args[2] : null;
430			$options['title'] = (isset($args[3])) ? $args[3] : null;
431		}
432
433		parent::$_buffer[$options['type']][$options['name']][$options['title']] = $content;
434
435		return $this;
436	}
437
438	/**
439	 * Parses the template and populates the buffer
440	 *
441	 * @param   array  $params  Parameters for fetching the template
442	 *
443	 * @return  JDocumentHTML instance of $this to allow chaining
444	 *
445	 * @since   11.1
446	 */
447	public function parse($params = array())
448	{
449		return $this->_fetchTemplate($params)->_parseTemplate();
450	}
451
452	/**
453	 * Outputs the template to the browser.
454	 *
455	 * @param   boolean  $caching  If true, cache the output
456	 * @param   array    $params   Associative array of attributes
457	 *
458	 * @return  The rendered data
459	 *
460	 * @since   11.1
461	 */
462	public function render($caching = false, $params = array())
463	{
464		$this->_caching = $caching;
465
466		if (!empty($this->_template))
467		{
468			$data = $this->_renderTemplate();
469		}
470		else
471		{
472			$this->parse($params);
473			$data = $this->_renderTemplate();
474		}
475
476		parent::render();
477
478		return $data;
479	}
480
481	/**
482	 * Count the modules based on the given condition
483	 *
484	 * @param   string  $condition  The condition to use
485	 *
486	 * @return  integer  Number of modules found
487	 *
488	 * @since   11.1
489	 */
490	public function countModules($condition)
491	{
492		$operators = '(\+|\-|\*|\/|==|\!=|\<\>|\<|\>|\<=|\>=|and|or|xor)';
493		$words = preg_split('# ' . $operators . ' #', $condition, null, PREG_SPLIT_DELIM_CAPTURE);
494
495		if (count($words) === 1)
496		{
497			$name = strtolower($words[0]);
498			$result = ((isset(parent::$_buffer['modules'][$name])) && (parent::$_buffer['modules'][$name] === false))
499						? 0 : count(JModuleHelper::getModules($name));
500
501			return $result;
502		}
503
504		JLog::add('Using an expression in JDocumentHtml::countModules() is deprecated.', JLog::WARNING, 'deprecated');
505
506		for ($i = 0, $n = count($words); $i < $n; $i += 2)
507		{
508			// Odd parts (modules)
509			$name = strtolower($words[$i]);
510			$words[$i] = ((isset(parent::$_buffer['modules'][$name])) && (parent::$_buffer['modules'][$name] === false))
511				? 0
512				: count(JModuleHelper::getModules($name));
513		}
514
515		$str = 'return ' . implode(' ', $words) . ';';
516
517		return eval($str);
518	}
519
520	/**
521	 * Count the number of child menu items
522	 *
523	 * @return  integer  Number of child menu items
524	 *
525	 * @since   11.1
526	 */
527	public function countMenuChildren()
528	{
529		static $children;
530
531		if (!isset($children))
532		{
533			$dbo = JFactory::getDbo();
534			$app = JFactory::getApplication();
535			$menu = $app->getMenu();
536			$active = $menu->getActive();
537
538			if ($active)
539			{
540				$query = $dbo->getQuery(true);
541				$query->select('COUNT(*)');
542				$query->from('#__menu');
543				$query->where('parent_id = ' . $active->id);
544				$query->where('published = 1');
545				$dbo->setQuery($query);
546				$children = $dbo->loadResult();
547			}
548			else
549			{
550				$children = 0;
551			}
552		}
553
554		return $children;
555	}
556
557	/**
558	 * Load a template file
559	 *
560	 * @param   string  $directory  The name of the template
561	 * @param   string  $filename   The actual filename
562	 *
563	 * @return  string  The contents of the template
564	 *
565	 * @since   11.1
566	 */
567	protected function _loadTemplate($directory, $filename)
568	{
569		// @todo remove code: $component	= JApplicationHelper::getComponentName();
570
571		$contents = '';
572
573		// Check to see if we have a valid template file
574		if (file_exists($directory . '/' . $filename))
575		{
576			// Store the file path
577			$this->_file = $directory . '/' . $filename;
578
579			// Get the file content
580			ob_start();
581			require $directory . '/' . $filename;
582			$contents = ob_get_contents();
583			ob_end_clean();
584		}
585
586		// Try to find a favicon by checking the template and root folder
587		$path = $directory . '/';
588		$dirs = array($path, JPATH_BASE . '/');
589
590		foreach ($dirs as $dir)
591		{
592			$icon = $dir . 'favicon.ico';
593
594			if (file_exists($icon))
595			{
596				$path = str_replace(JPATH_BASE . '/', '', $dir);
597				$path = str_replace('\\', '/', $path);
598				$this->addFavicon(JURI::base(true) . '/' . $path . 'favicon.ico');
599				break;
600			}
601		}
602
603		return $contents;
604	}
605
606	/**
607	 * Fetch the template, and initialise the params
608	 *
609	 * @param   array  $params  Parameters to determine the template
610	 *
611	 * @return  JDocumentHTML instance of $this to allow chaining
612	 *
613	 * @since   11.1
614	 */
615	protected function _fetchTemplate($params = array())
616	{
617		// Check
618		$directory = isset($params['directory']) ? $params['directory'] : 'templates';
619		$filter = JFilterInput::getInstance();
620		$template = $filter->clean($params['template'], 'cmd');
621		$file = $filter->clean($params['file'], 'cmd');
622
623		if (!file_exists($directory . '/' . $template . '/' . $file))
624		{
625			$template = 'system';
626		}
627
628		// Load the language file for the template
629		$lang = JFactory::getLanguage();
630
631		// 1.5 or core then 1.6
632		$lang->load('tpl_' . $template, JPATH_BASE, null, false, false)
633			|| $lang->load('tpl_' . $template, $directory . '/' . $template, null, false, false)
634			|| $lang->load('tpl_' . $template, JPATH_BASE, $lang->getDefault(), false, false)
635			|| $lang->load('tpl_' . $template, $directory . '/' . $template, $lang->getDefault(), false, false);
636
637		// Assign the variables
638		$this->template = $template;
639		$this->baseurl = JURI::base(true);
640		$this->params = isset($params['params']) ? $params['params'] : new JRegistry;
641
642		// Load
643		$this->_template = $this->_loadTemplate($directory . '/' . $template, $file);
644
645		return $this;
646	}
647
648	/**
649	 * Parse a document template
650	 *
651	 * @return  JDocumentHTML  instance of $this to allow chaining
652	 *
653	 * @since   11.1
654	 */
655	protected function _parseTemplate()
656	{
657		$matches = array();
658
659		if (preg_match_all('#<jdoc:include\ type="([^"]+)" (.*)\/>#iU', $this->_template, $matches))
660		{
661			$template_tags_first = array();
662			$template_tags_last = array();
663
664			// Step through the jdocs in reverse order.
665			for ($i = count($matches[0]) - 1; $i >= 0; $i--)
666			{
667				$type = $matches[1][$i];
668				$attribs = empty($matches[2][$i]) ? array() : JUtility::parseAttributes($matches[2][$i]);
669				$name = isset($attribs['name']) ? $attribs['name'] : null;
670
671				// Separate buffers to be executed first and last
672				if ($type == 'module' || $type == 'modules')
673				{
674					$template_tags_first[$matches[0][$i]] = array('type' => $type, 'name' => $name, 'attribs' => $attribs);
675				}
676				else
677				{
678					$template_tags_last[$matches[0][$i]] = array('type' => $type, 'name' => $name, 'attribs' => $attribs);
679				}
680			}
681			// Reverse the last array so the jdocs are in forward order.
682			$template_tags_last = array_reverse($template_tags_last);
683
684			$this->_template_tags = $template_tags_first + $template_tags_last;
685		}
686
687		return $this;
688	}
689
690	/**
691	 * Render pre-parsed template
692	 *
693	 * @return string rendered template
694	 *
695	 * @since   11.1
696	 */
697	protected function _renderTemplate()
698	{
699		$replace = array();
700		$with = array();
701
702		foreach ($this->_template_tags as $jdoc => $args)
703		{
704			$replace[] = $jdoc;
705			$with[] = $this->getBuffer($args['type'], $args['name'], $args['attribs']);
706		}
707
708		return str_replace($replace, $with, $this->_template);
709	}
710}