1 <?php
2 /**
3 * @package Joomla.Legacy
4 * @subpackage View
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Base class for a Joomla View
14 *
15 * Class holding methods for displaying presentation data.
16 *
17 * @since 2.5.5
18 */
19 class JViewLegacy extends JObject
20 {
21 /**
22 * The active document object
23 *
24 * @var JDocument
25 * @since 3.0
26 */
27 public $document;
28
29 /**
30 * The name of the view
31 *
32 * @var array
33 * @since 3.0
34 */
35 protected $_name = null;
36
37 /**
38 * Registered models
39 *
40 * @var array
41 * @since 3.0
42 */
43 protected $_models = array();
44
45 /**
46 * The base path of the view
47 *
48 * @var string
49 * @since 3.0
50 */
51 protected $_basePath = null;
52
53 /**
54 * The default model
55 *
56 * @var string
57 * @since 3.0
58 */
59 protected $_defaultModel = null;
60
61 /**
62 * Layout name
63 *
64 * @var string
65 * @since 3.0
66 */
67 protected $_layout = 'default';
68
69 /**
70 * Layout extension
71 *
72 * @var string
73 * @since 3.0
74 */
75 protected $_layoutExt = 'php';
76
77 /**
78 * Layout template
79 *
80 * @var string
81 * @since 3.0
82 */
83 protected $_layoutTemplate = '_';
84
85 /**
86 * The set of search directories for resources (templates)
87 *
88 * @var array
89 * @since 3.0
90 */
91 protected $_path = array('template' => array(), 'helper' => array());
92
93 /**
94 * The name of the default template source file.
95 *
96 * @var string
97 * @since 3.0
98 */
99 protected $_template = null;
100
101 /**
102 * The output of the template script.
103 *
104 * @var string
105 * @since 3.0
106 */
107 protected $_output = null;
108
109 /**
110 * Callback for escaping.
111 *
112 * @var string
113 * @since 3.0
114 * @deprecated 3.0
115 */
116 protected $_escape = 'htmlspecialchars';
117
118 /**
119 * Charset to use in escaping mechanisms; defaults to urf8 (UTF-8)
120 *
121 * @var string
122 * @since 3.0
123 */
124 protected $_charset = 'UTF-8';
125
126 /**
127 * Constructor
128 *
129 * @param array $config A named configuration array for object construction.
130 * name: the name (optional) of the view (defaults to the view class name suffix).
131 * charset: the character set to use for display
132 * escape: the name (optional) of the function to use for escaping strings
133 * base_path: the parent path (optional) of the views directory (defaults to the component folder)
134 * template_plath: the path (optional) of the layout directory (defaults to base_path + /views/ + view name
135 * helper_path: the path (optional) of the helper files (defaults to base_path + /helpers/)
136 * layout: the layout (optional) to use to display the view
137 *
138 * @since 3.0
139 */
140 public function __construct($config = array())
141 {
142 // Set the view name
143 if (empty($this->_name))
144 {
145 if (array_key_exists('name', $config))
146 {
147 $this->_name = $config['name'];
148 }
149 else
150 {
151 $this->_name = $this->getName();
152 }
153 }
154
155 // Set the charset (used by the variable escaping functions)
156 if (array_key_exists('charset', $config))
157 {
158 JLog::add('Setting a custom charset for escaping is deprecated. Override JViewLegacy::escape() instead.', JLog::WARNING, 'deprecated');
159 $this->_charset = $config['charset'];
160 }
161
162 // User-defined escaping callback
163 if (array_key_exists('escape', $config))
164 {
165 $this->setEscape($config['escape']);
166 }
167
168 // Set a base path for use by the view
169 if (array_key_exists('base_path', $config))
170 {
171 $this->_basePath = $config['base_path'];
172 }
173 else
174 {
175 $this->_basePath = JPATH_COMPONENT;
176 }
177
178 // Set the default template search path
179 if (array_key_exists('template_path', $config))
180 {
181 // User-defined dirs
182 $this->_setPath('template', $config['template_path']);
183 }
184 elseif (is_dir($this->_basePath . '/view'))
185 {
186 $this->_setPath('template', $this->_basePath . '/view/' . $this->getName() . '/tmpl');
187 }
188 else
189 {
190 $this->_setPath('template', $this->_basePath . '/views/' . $this->getName() . '/tmpl');
191 }
192
193 // Set the default helper search path
194 if (array_key_exists('helper_path', $config))
195 {
196 // User-defined dirs
197 $this->_setPath('helper', $config['helper_path']);
198 }
199 else
200 {
201 $this->_setPath('helper', $this->_basePath . '/helpers');
202 }
203
204 // Set the layout
205 if (array_key_exists('layout', $config))
206 {
207 $this->setLayout($config['layout']);
208 }
209 else
210 {
211 $this->setLayout('default');
212 }
213
214 $this->baseurl = JUri::base(true);
215 }
216
217 /**
218 * Execute and display a template script.
219 *
220 * @param string $tpl The name of the template file to parse; automatically searches through the template paths.
221 *
222 * @return mixed A string if successful, otherwise an Error object.
223 *
224 * @see JViewLegacy::loadTemplate()
225 * @since 3.0
226 */
227 public function display($tpl = null)
228 {
229 $result = $this->loadTemplate($tpl);
230
231 if ($result instanceof Exception)
232 {
233 return $result;
234 }
235
236 echo $result;
237 }
238
239 /**
240 * Assigns variables to the view script via differing strategies.
241 *
242 * This method is overloaded; you can assign all the properties of
243 * an object, an associative array, or a single value by name.
244 *
245 * You are not allowed to set variables that begin with an underscore;
246 * these are either private properties for JView or private variables
247 * within the template script itself.
248 *
249 * <code>
250 * $view = new JView;
251 *
252 * // Assign directly
253 * $view->var1 = 'something';
254 * $view->var2 = 'else';
255 *
256 * // Assign by name and value
257 * $view->assign('var1', 'something');
258 * $view->assign('var2', 'else');
259 *
260 * // Assign by assoc-array
261 * $ary = array('var1' => 'something', 'var2' => 'else');
262 * $view->assign($obj);
263 *
264 * // Assign by object
265 * $obj = new stdClass;
266 * $obj->var1 = 'something';
267 * $obj->var2 = 'else';
268 * $view->assign($obj);
269 *
270 * </code>
271 *
272 * @return boolean True on success, false on failure.
273 *
274 * @since 3.0
275 * @deprecated 3.0 Use native PHP syntax.
276 */
277 public function assign()
278 {
279 JLog::add(__METHOD__ . ' is deprecated. Use native PHP syntax.', JLog::WARNING, 'deprecated');
280
281 // Get the arguments; there may be 1 or 2.
282 $arg0 = @func_get_arg(0);
283 $arg1 = @func_get_arg(1);
284
285 // Assign by object
286 if (is_object($arg0))
287 {
288 // Assign public properties
289 foreach (get_object_vars($arg0) as $key => $val)
290 {
291 if (substr($key, 0, 1) != '_')
292 {
293 $this->$key = $val;
294 }
295 }
296
297 return true;
298 }
299
300 // Assign by associative array
301 if (is_array($arg0))
302 {
303 foreach ($arg0 as $key => $val)
304 {
305 if (substr($key, 0, 1) != '_')
306 {
307 $this->$key = $val;
308 }
309 }
310
311 return true;
312 }
313
314 // Assign by string name and mixed value.
315
316 // We use array_key_exists() instead of isset() because isset()
317 // fails if the value is set to null.
318 if (is_string($arg0) && substr($arg0, 0, 1) != '_' && func_num_args() > 1)
319 {
320 $this->$arg0 = $arg1;
321
322 return true;
323 }
324
325 // $arg0 was not object, array, or string.
326 return false;
327 }
328
329 /**
330 * Assign variable for the view (by reference).
331 *
332 * You are not allowed to set variables that begin with an underscore;
333 * these are either private properties for JView or private variables
334 * within the template script itself.
335 *
336 * <code>
337 * $view = new JView;
338 *
339 * // Assign by name and value
340 * $view->assignRef('var1', $ref);
341 *
342 * // Assign directly
343 * $view->var1 = &$ref;
344 * </code>
345 *
346 * @param string $key The name for the reference in the view.
347 * @param mixed &$val The referenced variable.
348 *
349 * @return boolean True on success, false on failure.
350 *
351 * @since 3.0
352 * @deprecated 3.0 Use native PHP syntax.
353 */
354 public function assignRef($key, &$val)
355 {
356 JLog::add(__METHOD__ . ' is deprecated. Use native PHP syntax.', JLog::WARNING, 'deprecated');
357
358 if (is_string($key) && substr($key, 0, 1) != '_')
359 {
360 $this->$key = &$val;
361
362 return true;
363 }
364
365 return false;
366 }
367
368 /**
369 * Escapes a value for output in a view script.
370 *
371 * If escaping mechanism is either htmlspecialchars or htmlentities, uses
372 * {@link $_encoding} setting.
373 *
374 * @param mixed $var The output to escape.
375 *
376 * @return mixed The escaped value.
377 *
378 * @since 3.0
379 */
380 public function escape($var)
381 {
382 if (in_array($this->_escape, array('htmlspecialchars', 'htmlentities')))
383 {
384 return call_user_func($this->_escape, $var, ENT_COMPAT, $this->_charset);
385 }
386
387 return call_user_func($this->_escape, $var);
388 }
389
390 /**
391 * Method to get data from a registered model or a property of the view
392 *
393 * @param string $property The name of the method to call on the model or the property to get
394 * @param string $default The name of the model to reference or the default value [optional]
395 *
396 * @return mixed The return value of the method
397 *
398 * @since 3.0
399 */
400 public function get($property, $default = null)
401 {
402 // If $model is null we use the default model
403 if (is_null($default))
404 {
405 $model = $this->_defaultModel;
406 }
407 else
408 {
409 $model = strtolower($default);
410 }
411
412 // First check to make sure the model requested exists
413 if (isset($this->_models[$model]))
414 {
415 // Model exists, let's build the method name
416 $method = 'get' . ucfirst($property);
417
418 // Does the method exist?
419 if (method_exists($this->_models[$model], $method))
420 {
421 // The method exists, let's call it and return what we get
422 $result = $this->_models[$model]->$method();
423
424 return $result;
425 }
426 }
427
428 // Degrade to JObject::get
429 $result = parent::get($property, $default);
430
431 return $result;
432 }
433
434 /**
435 * Method to get the model object
436 *
437 * @param string $name The name of the model (optional)
438 *
439 * @return mixed JModelLegacy object
440 *
441 * @since 3.0
442 */
443 public function getModel($name = null)
444 {
445 if ($name === null)
446 {
447 $name = $this->_defaultModel;
448 }
449
450 return $this->_models[strtolower($name)];
451 }
452
453 /**
454 * Get the layout.
455 *
456 * @return string The layout name
457 *
458 * @since 3.0
459 */
460 public function getLayout()
461 {
462 return $this->_layout;
463 }
464
465 /**
466 * Get the layout template.
467 *
468 * @return string The layout template name
469 *
470 * @since 3.0
471 */
472 public function getLayoutTemplate()
473 {
474 return $this->_layoutTemplate;
475 }
476
477 /**
478 * Method to get the view name
479 *
480 * The model name by default parsed using the classname, or it can be set
481 * by passing a $config['name'] in the class constructor
482 *
483 * @return string The name of the model
484 *
485 * @since 3.0
486 * @throws Exception
487 */
488 public function getName()
489 {
490 if (empty($this->_name))
491 {
492 $classname = get_class($this);
493 $viewpos = strpos($classname, 'View');
494
495 if ($viewpos === false)
496 {
497 throw new Exception(JText::_('JLIB_APPLICATION_ERROR_VIEW_GET_NAME'), 500);
498 }
499
500 $this->_name = strtolower(substr($classname, $viewpos + 4));
501 }
502
503 return $this->_name;
504 }
505
506 /**
507 * Method to add a model to the view. We support a multiple model single
508 * view system by which models are referenced by classname. A caveat to the
509 * classname referencing is that any classname prepended by JModel will be
510 * referenced by the name without JModel, eg. JModelCategory is just
511 * Category.
512 *
513 * @param JModelLegacy $model The model to add to the view.
514 * @param boolean $default Is this the default model?
515 *
516 * @return JModelLegacy The added model.
517 *
518 * @since 3.0
519 */
520 public function setModel($model, $default = false)
521 {
522 $name = strtolower($model->getName());
523 $this->_models[$name] = $model;
524
525 if ($default)
526 {
527 $this->_defaultModel = $name;
528 }
529
530 return $model;
531 }
532
533 /**
534 * Sets the layout name to use
535 *
536 * @param string $layout The layout name or a string in format <template>:<layout file>
537 *
538 * @return string Previous value.
539 *
540 * @since 3.0
541 */
542 public function setLayout($layout)
543 {
544 $previous = $this->_layout;
545
546 if (strpos($layout, ':') === false)
547 {
548 $this->_layout = $layout;
549 }
550 else
551 {
552 // Convert parameter to array based on :
553 $temp = explode(':', $layout);
554 $this->_layout = $temp[1];
555
556 // Set layout template
557 $this->_layoutTemplate = $temp[0];
558 }
559
560 return $previous;
561 }
562
563 /**
564 * Allows a different extension for the layout files to be used
565 *
566 * @param string $value The extension.
567 *
568 * @return string Previous value
569 *
570 * @since 3.0
571 */
572 public function setLayoutExt($value)
573 {
574 $previous = $this->_layoutExt;
575
576 if ($value = preg_replace('#[^A-Za-z0-9]#', '', trim($value)))
577 {
578 $this->_layoutExt = $value;
579 }
580
581 return $previous;
582 }
583
584 /**
585 * Sets the _escape() callback.
586 *
587 * @param mixed $spec The callback for _escape() to use.
588 *
589 * @return void
590 *
591 * @since 3.0
592 * @deprecated 3.0 Override JViewLegacy::escape() instead.
593 */
594 public function setEscape($spec)
595 {
596 JLog::add(__METHOD__ . ' is deprecated. Override JViewLegacy::escape() instead.', JLog::WARNING, 'deprecated');
597
598 $this->_escape = $spec;
599 }
600
601 /**
602 * Adds to the stack of view script paths in LIFO order.
603 *
604 * @param mixed $path A directory path or an array of paths.
605 *
606 * @return void
607 *
608 * @since 3.0
609 */
610 public function addTemplatePath($path)
611 {
612 $this->_addPath('template', $path);
613 }
614
615 /**
616 * Adds to the stack of helper script paths in LIFO order.
617 *
618 * @param mixed $path A directory path or an array of paths.
619 *
620 * @return void
621 *
622 * @since 3.0
623 */
624 public function addHelperPath($path)
625 {
626 $this->_addPath('helper', $path);
627 }
628
629 /**
630 * Load a template file -- first look in the templates folder for an override
631 *
632 * @param string $tpl The name of the template source file; automatically searches the template paths and compiles as needed.
633 *
634 * @return string The output of the the template script.
635 *
636 * @since 3.0
637 * @throws Exception
638 */
639 public function loadTemplate($tpl = null)
640 {
641 // Clear prior output
642 $this->_output = null;
643
644 $template = JFactory::getApplication()->getTemplate();
645 $layout = $this->getLayout();
646 $layoutTemplate = $this->getLayoutTemplate();
647
648 // Create the template file name based on the layout
649 $file = isset($tpl) ? $layout . '_' . $tpl : $layout;
650
651 // Clean the file name
652 $file = preg_replace('/[^A-Z0-9_\.-]/i', '', $file);
653 $tpl = isset($tpl) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $tpl) : $tpl;
654
655 // Load the language file for the template
656 $lang = JFactory::getLanguage();
657 $lang->load('tpl_' . $template, JPATH_BASE, null, false, true)
658 || $lang->load('tpl_' . $template, JPATH_THEMES . "/$template", null, false, true);
659
660 // Change the template folder if alternative layout is in different template
661 if (isset($layoutTemplate) && $layoutTemplate != '_' && $layoutTemplate != $template)
662 {
663 $this->_path['template'] = str_replace($template, $layoutTemplate, $this->_path['template']);
664 }
665
666 // Load the template script
667 jimport('joomla.filesystem.path');
668 $filetofind = $this->_createFileName('template', array('name' => $file));
669 $this->_template = JPath::find($this->_path['template'], $filetofind);
670
671 // If alternate layout can't be found, fall back to default layout
672 if ($this->_template == false)
673 {
674 $filetofind = $this->_createFileName('', array('name' => 'default' . (isset($tpl) ? '_' . $tpl : $tpl)));
675 $this->_template = JPath::find($this->_path['template'], $filetofind);
676 }
677
678 if ($this->_template != false)
679 {
680 // Unset so as not to introduce into template scope
681 unset($tpl, $file);
682
683 // Never allow a 'this' property
684 if (isset($this->this))
685 {
686 unset($this->this);
687 }
688
689 // Start capturing output into a buffer
690 ob_start();
691
692 // Include the requested template filename in the local scope
693 // (this will execute the view logic).
694 include $this->_template;
695
696 // Done with the requested template; get the buffer and
697 // clear it.
698 $this->_output = ob_get_contents();
699 ob_end_clean();
700
701 return $this->_output;
702 }
703 else
704 {
705 throw new Exception(JText::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $file), 500);
706 }
707 }
708
709 /**
710 * Load a helper file
711 *
712 * @param string $hlp The name of the helper source file automatically searches the helper paths and compiles as needed.
713 *
714 * @return void
715 *
716 * @since 3.0
717 */
718 public function loadHelper($hlp = null)
719 {
720 // Clean the file name
721 $file = preg_replace('/[^A-Z0-9_\.-]/i', '', $hlp);
722
723 // Load the template script
724 jimport('joomla.filesystem.path');
725 $helper = JPath::find($this->_path['helper'], $this->_createFileName('helper', array('name' => $file)));
726
727 if ($helper != false)
728 {
729 // Include the requested template filename in the local scope
730 include_once $helper;
731 }
732 }
733
734 /**
735 * Sets an entire array of search paths for templates or resources.
736 *
737 * @param string $type The type of path to set, typically 'template'.
738 * @param mixed $path The new search path, or an array of search paths. If null or false, resets to the current directory only.
739 *
740 * @return void
741 *
742 * @since 3.0
743 */
744 protected function _setPath($type, $path)
745 {
746 $component = JApplicationHelper::getComponentName();
747 $app = JFactory::getApplication();
748
749 // Clear out the prior search dirs
750 $this->_path[$type] = array();
751
752 // Actually add the user-specified directories
753 $this->_addPath($type, $path);
754
755 // Always add the fallback directories as last resort
756 switch (strtolower($type))
757 {
758 case 'template':
759 // Set the alternative template search dir
760 if (isset($app))
761 {
762 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $component);
763 $fallback = JPATH_THEMES . '/' . $app->getTemplate() . '/html/' . $component . '/' . $this->getName();
764 $this->_addPath('template', $fallback);
765 }
766 break;
767 }
768 }
769
770 /**
771 * Adds to the search path for templates and resources.
772 *
773 * @param string $type The type of path to add.
774 * @param mixed $path The directory or stream, or an array of either, to search.
775 *
776 * @return void
777 *
778 * @since 3.0
779 */
780 protected function _addPath($type, $path)
781 {
782 jimport('joomla.filesystem.path');
783
784 // Loop through the path directories
785 foreach ((array) $path as $dir)
786 {
787 // Clean up the path
788 $dir = JPath::clean($dir);
789
790 // Add trailing separators as needed
791 if (substr($dir, -1) != DIRECTORY_SEPARATOR)
792 {
793 // Directory
794 $dir .= DIRECTORY_SEPARATOR;
795 }
796
797 // Add to the top of the search dirs
798 array_unshift($this->_path[$type], $dir);
799 }
800 }
801
802 /**
803 * Create the filename for a resource
804 *
805 * @param string $type The resource type to create the filename for
806 * @param array $parts An associative array of filename information
807 *
808 * @return string The filename
809 *
810 * @since 3.0
811 */
812 protected function _createFileName($type, $parts = array())
813 {
814 switch ($type)
815 {
816 case 'template':
817 $filename = strtolower($parts['name']) . '.' . $this->_layoutExt;
818 break;
819
820 default:
821 $filename = strtolower($parts['name']) . '.php';
822 break;
823 }
824
825 return $filename;
826 }
827
828 /**
829 * Returns the form object
830 *
831 * @return mixed A JForm object on success, false on failure
832 *
833 * @since 3.2
834 */
835 public function getForm()
836 {
837 if (!is_object($this->form))
838 {
839 $this->form = $this->get('Form');
840 }
841
842 return $this->form;
843 }
844
845 /**
846 * Sets the document title according to Global Configuration options
847 *
848 * @param string $title The page title
849 *
850 * @return void
851 *
852 * @since 3.6
853 */
854 public function setDocumentTitle($title)
855 {
856 $app = JFactory::getApplication();
857
858 // Check for empty title and add site name if param is set
859 if (empty($title))
860 {
861 $title = $app->get('sitename');
862 }
863 elseif ($app->get('sitename_pagetitles', 0) == 1)
864 {
865 $title = JText::sprintf('JPAGETITLE', $app->get('sitename'), $title);
866 }
867 elseif ($app->get('sitename_pagetitles', 0) == 2)
868 {
869 $title = JText::sprintf('JPAGETITLE', $title, $app->get('sitename'));
870 }
871
872 $this->document->setTitle($title);
873 }
874 }
875