1 <?php
2 /**
3 * @package Joomla.Libraries
4 * @subpackage Application
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 use Joomla\Registry\Registry;
13
14 /**
15 * Joomla! Site Application class
16 *
17 * @since 3.2
18 */
19 final class JApplicationSite extends JApplicationCms
20 {
21 /**
22 * Option to filter by language
23 *
24 * @var boolean
25 * @since 3.2
26 * @deprecated 4.0 Will be renamed $language_filter
27 */
28 protected $_language_filter = false;
29
30 /**
31 * Option to detect language by the browser
32 *
33 * @var boolean
34 * @since 3.2
35 * @deprecated 4.0 Will be renamed $detect_browser
36 */
37 protected $_detect_browser = false;
38
39 /**
40 * Class constructor.
41 *
42 * @param JInput $input An optional argument to provide dependency injection for the application's
43 * input object. If the argument is a JInput object that object will become
44 * the application's input object, otherwise a default input object is created.
45 * @param Registry $config An optional argument to provide dependency injection for the application's
46 * config object. If the argument is a Registry object that object will become
47 * the application's config object, otherwise a default config object is created.
48 * @param JApplicationWebClient $client An optional argument to provide dependency injection for the application's
49 * client object. If the argument is a JApplicationWebClient object that object will become
50 * the application's client object, otherwise a default client object is created.
51 *
52 * @since 3.2
53 */
54 public function __construct(JInput $input = null, Registry $config = null, JApplicationWebClient $client = null)
55 {
56 // Register the application name
57 $this->_name = 'site';
58
59 // Register the client ID
60 $this->_clientId = 0;
61
62 // Execute the parent constructor
63 parent::__construct($input, $config, $client);
64 }
65
66 /**
67 * Check if the user can access the application
68 *
69 * @param integer $itemid The item ID to check authorisation for
70 *
71 * @return void
72 *
73 * @since 3.2
74 *
75 * @throws Exception When you are not authorised to view the home page menu item
76 */
77 protected function authorise($itemid)
78 {
79 $menus = $this->getMenu();
80 $user = JFactory::getUser();
81
82 if (!$menus->authorise($itemid))
83 {
84 if ($user->get('id') == 0)
85 {
86 // Set the data
87 $this->setUserState('users.login.form.data', array('return' => JUri::getInstance()->toString()));
88
89 $url = JRoute::_('index.php?option=com_users&view=login', false);
90
91 $this->enqueueMessage(JText::_('JGLOBAL_YOU_MUST_LOGIN_FIRST'));
92 $this->redirect($url);
93 }
94 else
95 {
96 // Get the home page menu item
97 $home_item = $menus->getDefault($this->getLanguage()->getTag());
98
99 // If we are already in the homepage raise an exception
100 if ($menus->getActive()->id == $home_item->id)
101 {
102 throw new Exception(JText::_('JERROR_ALERTNOAUTHOR'), 403);
103 }
104
105 // Otherwise redirect to the homepage and show an error
106 $this->enqueueMessage(JText::_('JERROR_ALERTNOAUTHOR'), 'error');
107 $this->redirect(JRoute::_('index.php?Itemid=' . $home_item->id, false));
108 }
109 }
110 }
111
112 /**
113 * Dispatch the application
114 *
115 * @param string $component The component which is being rendered.
116 *
117 * @return void
118 *
119 * @since 3.2
120 */
121 public function dispatch($component = null)
122 {
123 // Get the component if not set.
124 if (!$component)
125 {
126 $component = $this->input->getCmd('option', null);
127 }
128
129 // Load the document to the API
130 $this->loadDocument();
131
132 // Set up the params
133 $document = $this->getDocument();
134 $router = static::getRouter();
135 $params = $this->getParams();
136
137 // Register the document object with JFactory
138 JFactory::$document = $document;
139
140 switch ($document->getType())
141 {
142 case 'html':
143 // Get language
144 $lang_code = $this->getLanguage()->getTag();
145 $languages = JLanguageHelper::getLanguages('lang_code');
146
147 // Set metadata
148 if (isset($languages[$lang_code]) && $languages[$lang_code]->metakey)
149 {
150 $document->setMetaData('keywords', $languages[$lang_code]->metakey);
151 }
152 else
153 {
154 $document->setMetaData('keywords', $this->get('MetaKeys'));
155 }
156
157 $document->setMetaData('rights', $this->get('MetaRights'));
158
159 if ($router->getMode() == JROUTER_MODE_SEF)
160 {
161 $document->setBase(htmlspecialchars(JUri::current()));
162 }
163
164 // Get the template
165 $template = $this->getTemplate(true);
166
167 // Store the template and its params to the config
168 $this->set('theme', $template->template);
169 $this->set('themeParams', $template->params);
170
171 break;
172
173 case 'feed':
174 $document->setBase(htmlspecialchars(JUri::current()));
175 break;
176 }
177
178 $document->setTitle($params->get('page_title'));
179 $document->setDescription($params->get('page_description'));
180
181 // Add version number or not based on global configuration
182 if ($this->get('MetaVersion', 0))
183 {
184 $document->setGenerator('Joomla! - Open Source Content Management - Version ' . JVERSION);
185 }
186 else
187 {
188 $document->setGenerator('Joomla! - Open Source Content Management');
189 }
190
191 $contents = JComponentHelper::renderComponent($component);
192 $document->setBuffer($contents, 'component');
193
194 // Trigger the onAfterDispatch event.
195 JPluginHelper::importPlugin('system');
196 $this->triggerEvent('onAfterDispatch');
197 }
198
199 /**
200 * Method to run the Web application routines.
201 *
202 * @return void
203 *
204 * @since 3.2
205 */
206 protected function doExecute()
207 {
208 // Initialise the application
209 $this->initialiseApp();
210
211 // Mark afterInitialise in the profiler.
212 JDEBUG ? $this->profiler->mark('afterInitialise') : null;
213
214 // Route the application
215 $this->route();
216
217 // Mark afterRoute in the profiler.
218 JDEBUG ? $this->profiler->mark('afterRoute') : null;
219
220 /*
221 * Check if the user is required to reset their password
222 *
223 * Before $this->route(); "option" and "view" can't be safely read using:
224 * $this->input->getCmd('option'); or $this->input->getCmd('view');
225 * ex: due of the sef urls
226 */
227 $this->checkUserRequireReset('com_users', 'profile', 'edit', 'com_users/profile.save,com_users/profile.apply,com_users/user.logout');
228
229 // Dispatch the application
230 $this->dispatch();
231
232 // Mark afterDispatch in the profiler.
233 JDEBUG ? $this->profiler->mark('afterDispatch') : null;
234 }
235
236 /**
237 * Return the current state of the detect browser option.
238 *
239 * @return boolean
240 *
241 * @since 3.2
242 */
243 public function getDetectBrowser()
244 {
245 return $this->_detect_browser;
246 }
247
248 /**
249 * Return the current state of the language filter.
250 *
251 * @return boolean
252 *
253 * @since 3.2
254 */
255 public function getLanguageFilter()
256 {
257 return $this->_language_filter;
258 }
259
260 /**
261 * Return a reference to the JMenu object.
262 *
263 * @param string $name The name of the application/client.
264 * @param array $options An optional associative array of configuration settings.
265 *
266 * @return JMenu JMenu object.
267 *
268 * @since 3.2
269 */
270 public function getMenu($name = 'site', $options = array())
271 {
272 return parent::getMenu($name, $options);
273 }
274
275 /**
276 * Get the application parameters
277 *
278 * @param string $option The component option
279 *
280 * @return Registry The parameters object
281 *
282 * @since 3.2
283 * @deprecated 4.0 Use getParams() instead
284 */
285 public function getPageParameters($option = null)
286 {
287 return $this->getParams($option);
288 }
289
290 /**
291 * Get the application parameters
292 *
293 * @param string $option The component option
294 *
295 * @return Registry The parameters object
296 *
297 * @since 3.2
298 */
299 public function getParams($option = null)
300 {
301 static $params = array();
302
303 $hash = '__default';
304
305 if (!empty($option))
306 {
307 $hash = $option;
308 }
309
310 if (!isset($params[$hash]))
311 {
312 // Get component parameters
313 if (!$option)
314 {
315 $option = $this->input->getCmd('option', null);
316 }
317
318 // Get new instance of component global parameters
319 $params[$hash] = clone JComponentHelper::getParams($option);
320
321 // Get menu parameters
322 $menus = $this->getMenu();
323 $menu = $menus->getActive();
324
325 // Get language
326 $lang_code = $this->getLanguage()->getTag();
327 $languages = JLanguageHelper::getLanguages('lang_code');
328
329 $title = $this->get('sitename');
330
331 if (isset($languages[$lang_code]) && $languages[$lang_code]->metadesc)
332 {
333 $description = $languages[$lang_code]->metadesc;
334 }
335 else
336 {
337 $description = $this->get('MetaDesc');
338 }
339
340 $rights = $this->get('MetaRights');
341 $robots = $this->get('robots');
342
343 // Retrieve com_menu global settings
344 $temp = clone JComponentHelper::getParams('com_menus');
345
346 // Lets cascade the parameters if we have menu item parameters
347 if (is_object($menu))
348 {
349 // Get show_page_heading from com_menu global settings
350 $params[$hash]->def('show_page_heading', $temp->get('show_page_heading'));
351
352 $params[$hash]->merge($menu->params);
353 $title = $menu->title;
354 }
355 else
356 {
357 // Merge com_menu global settings
358 $params[$hash]->merge($temp);
359
360 // If supplied, use page title
361 $title = $temp->get('page_title', $title);
362 }
363
364 $params[$hash]->def('page_title', $title);
365 $params[$hash]->def('page_description', $description);
366 $params[$hash]->def('page_rights', $rights);
367 $params[$hash]->def('robots', $robots);
368 }
369
370 return $params[$hash];
371 }
372
373 /**
374 * Return a reference to the JPathway object.
375 *
376 * @param string $name The name of the application.
377 * @param array $options An optional associative array of configuration settings.
378 *
379 * @return JPathway A JPathway object
380 *
381 * @since 3.2
382 */
383 public function getPathway($name = 'site', $options = array())
384 {
385 return parent::getPathway($name, $options);
386 }
387
388 /**
389 * Return a reference to the JRouter object.
390 *
391 * @param string $name The name of the application.
392 * @param array $options An optional associative array of configuration settings.
393 *
394 * @return JRouter
395 *
396 * @since 3.2
397 */
398 public static function getRouter($name = 'site', array $options = array())
399 {
400 $options['mode'] = JFactory::getConfig()->get('sef');
401
402 return parent::getRouter($name, $options);
403 }
404
405 /**
406 * Gets the name of the current template.
407 *
408 * @param boolean $params True to return the template parameters
409 *
410 * @return string The name of the template.
411 *
412 * @since 3.2
413 * @throws InvalidArgumentException
414 */
415 public function getTemplate($params = false)
416 {
417 if (is_object($this->template))
418 {
419 if (!file_exists(JPATH_THEMES . '/' . $this->template->template . '/index.php'))
420 {
421 throw new InvalidArgumentException(JText::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $this->template->template));
422 }
423
424 if ($params)
425 {
426 return $this->template;
427 }
428
429 return $this->template->template;
430 }
431
432 // Get the id of the active menu item
433 $menu = $this->getMenu();
434 $item = $menu->getActive();
435
436 if (!$item)
437 {
438 $item = $menu->getItem($this->input->getInt('Itemid', null));
439 }
440
441 $id = 0;
442
443 if (is_object($item))
444 {
445 // Valid item retrieved
446 $id = $item->template_style_id;
447 }
448
449 $tid = $this->input->getUint('templateStyle', 0);
450
451 if (is_numeric($tid) && (int) $tid > 0)
452 {
453 $id = (int) $tid;
454 }
455
456 $cache = JFactory::getCache('com_templates', '');
457
458 if ($this->_language_filter)
459 {
460 $tag = $this->getLanguage()->getTag();
461 }
462 else
463 {
464 $tag = '';
465 }
466
467 $cacheId = 'templates0' . $tag;
468
469 if ($cache->contains($cacheId))
470 {
471 $templates = $cache->get($cacheId);
472 }
473 else
474 {
475 // Load styles
476 $db = JFactory::getDbo();
477 $query = $db->getQuery(true)
478 ->select('id, home, template, s.params')
479 ->from('#__template_styles as s')
480 ->where('s.client_id = 0')
481 ->where('e.enabled = 1')
482 ->join('LEFT', '#__extensions as e ON e.element=s.template AND e.type=' . $db->quote('template') . ' AND e.client_id=s.client_id');
483
484 $db->setQuery($query);
485 $templates = $db->loadObjectList('id');
486
487 foreach ($templates as &$template)
488 {
489 // Create home element
490 if ($template->home == 1 && !isset($template_home) || $this->_language_filter && $template->home == $tag)
491 {
492 $template_home = clone $template;
493 }
494
495 $template->params = new Registry($template->params);
496 }
497
498 // Unset the $template reference to the last $templates[n] item cycled in the foreach above to avoid editing it later
499 unset($template);
500
501 // Add home element, after loop to avoid double execution
502 if (isset($template_home))
503 {
504 $template_home->params = new Registry($template_home->params);
505 $templates[0] = $template_home;
506 }
507
508 $cache->store($templates, $cacheId);
509 }
510
511 if (isset($templates[$id]))
512 {
513 $template = $templates[$id];
514 }
515 else
516 {
517 $template = $templates[0];
518 }
519
520 // Allows for overriding the active template from the request
521 $template_override = $this->input->getCmd('template', '');
522
523 // Only set template override if it is a valid template (= it exists and is enabled)
524 if (!empty($template_override))
525 {
526 if (file_exists(JPATH_THEMES . '/' . $template_override . '/index.php'))
527 {
528 foreach ($templates as $tmpl)
529 {
530 if ($tmpl->template === $template_override)
531 {
532 $template = $tmpl;
533 break;
534 }
535 }
536 }
537 }
538
539 // Need to filter the default value as well
540 $template->template = JFilterInput::getInstance()->clean($template->template, 'cmd');
541
542 // Fallback template
543 if (!file_exists(JPATH_THEMES . '/' . $template->template . '/index.php'))
544 {
545 $this->enqueueMessage(JText::_('JERROR_ALERTNOTEMPLATE'), 'error');
546
547 // Try to find data for 'beez3' template
548 $original_tmpl = $template->template;
549
550 foreach ($templates as $tmpl)
551 {
552 if ($tmpl->template === 'beez3')
553 {
554 $template = $tmpl;
555 break;
556 }
557 }
558
559 // Check, the data were found and if template really exists
560 if (!file_exists(JPATH_THEMES . '/' . $template->template . '/index.php'))
561 {
562 throw new InvalidArgumentException(JText::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $original_tmpl));
563 }
564 }
565
566 // Cache the result
567 $this->template = $template;
568
569 if ($params)
570 {
571 return $template;
572 }
573
574 return $template->template;
575 }
576
577 /**
578 * Initialise the application.
579 *
580 * @param array $options An optional associative array of configuration settings.
581 *
582 * @return void
583 *
584 * @since 3.2
585 */
586 protected function initialiseApp($options = array())
587 {
588 $user = JFactory::getUser();
589
590 // If the user is a guest we populate it with the guest user group.
591 if ($user->guest)
592 {
593 $guestUsergroup = JComponentHelper::getParams('com_users')->get('guest_usergroup', 1);
594 $user->groups = array($guestUsergroup);
595 }
596
597 /*
598 * If a language was specified it has priority, otherwise use user or default language settings
599 * Check this only if the languagefilter plugin is enabled
600 *
601 * @TODO - Remove the hardcoded dependency to the languagefilter plugin
602 */
603 if (JPluginHelper::isEnabled('system', 'languagefilter'))
604 {
605 $plugin = JPluginHelper::getPlugin('system', 'languagefilter');
606
607 $pluginParams = new Registry($plugin->params);
608
609 $this->setLanguageFilter(true);
610 $this->setDetectBrowser($pluginParams->get('detect_browser', '1') == '1');
611 }
612
613 if (empty($options['language']))
614 {
615 // Detect the specified language
616 $lang = $this->input->getString('language', null);
617
618 // Make sure that the user's language exists
619 if ($lang && JLanguageHelper::exists($lang))
620 {
621 $options['language'] = $lang;
622 }
623 }
624
625 if (empty($options['language']) && $this->getLanguageFilter())
626 {
627 // Detect cookie language
628 $lang = $this->input->cookie->get(md5($this->get('secret') . 'language'), null, 'string');
629
630 // Make sure that the user's language exists
631 if ($lang && JLanguageHelper::exists($lang))
632 {
633 $options['language'] = $lang;
634 }
635 }
636
637 if (empty($options['language']))
638 {
639 // Detect user language
640 $lang = $user->getParam('language');
641
642 // Make sure that the user's language exists
643 if ($lang && JLanguageHelper::exists($lang))
644 {
645 $options['language'] = $lang;
646 }
647 }
648
649 if (empty($options['language']) && $this->getDetectBrowser())
650 {
651 // Detect browser language
652 $lang = JLanguageHelper::detectLanguage();
653
654 // Make sure that the user's language exists
655 if ($lang && JLanguageHelper::exists($lang))
656 {
657 $options['language'] = $lang;
658 }
659 }
660
661 if (empty($options['language']))
662 {
663 // Detect default language
664 $params = JComponentHelper::getParams('com_languages');
665 $options['language'] = $params->get('site', $this->get('language', 'en-GB'));
666 }
667
668 // One last check to make sure we have something
669 if (!JLanguageHelper::exists($options['language']))
670 {
671 $lang = $this->config->get('language', 'en-GB');
672
673 if (JLanguageHelper::exists($lang))
674 {
675 $options['language'] = $lang;
676 }
677 else
678 {
679 // As a last ditch fail to english
680 $options['language'] = 'en-GB';
681 }
682 }
683
684 // Finish initialisation
685 parent::initialiseApp($options);
686 }
687
688 /**
689 * Load the library language files for the application
690 *
691 * @return void
692 *
693 * @since 3.6.3
694 */
695 protected function loadLibraryLanguage()
696 {
697 /*
698 * Try the lib_joomla file in the current language (without allowing the loading of the file in the default language)
699 * Fallback to the default language if necessary
700 */
701 $this->getLanguage()->load('lib_joomla', JPATH_SITE, null, false, true)
702 || $this->getLanguage()->load('lib_joomla', JPATH_ADMINISTRATOR, null, false, true);
703 }
704
705 /**
706 * Login authentication function
707 *
708 * @param array $credentials Array('username' => string, 'password' => string)
709 * @param array $options Array('remember' => boolean)
710 *
711 * @return boolean True on success.
712 *
713 * @since 3.2
714 */
715 public function login($credentials, $options = array())
716 {
717 // Set the application login entry point
718 if (!array_key_exists('entry_url', $options))
719 {
720 $options['entry_url'] = JUri::base() . 'index.php?option=com_users&task=user.login';
721 }
722
723 // Set the access control action to check.
724 $options['action'] = 'core.login.site';
725
726 return parent::login($credentials, $options);
727 }
728
729 /**
730 * Rendering is the process of pushing the document buffers into the template
731 * placeholders, retrieving data from the document and pushing it into
732 * the application response buffer.
733 *
734 * @return void
735 *
736 * @since 3.2
737 */
738 protected function render()
739 {
740 switch ($this->document->getType())
741 {
742 case 'feed':
743 // No special processing for feeds
744 break;
745
746 case 'html':
747 default:
748 $template = $this->getTemplate(true);
749 $file = $this->input->get('tmpl', 'index');
750
751 if ($file === 'offline' && !$this->get('offline'))
752 {
753 $this->set('themeFile', 'index.php');
754 }
755
756 if ($this->get('offline') && !JFactory::getUser()->authorise('core.login.offline'))
757 {
758 $this->setUserState('users.login.form.data', array('return' => JUri::getInstance()->toString()));
759 $this->set('themeFile', 'offline.php');
760 $this->setHeader('Status', '503 Service Temporarily Unavailable', 'true');
761 }
762
763 if (!is_dir(JPATH_THEMES . '/' . $template->template) && !$this->get('offline'))
764 {
765 $this->set('themeFile', 'component.php');
766 }
767
768 // Ensure themeFile is set by now
769 if ($this->get('themeFile') == '')
770 {
771 $this->set('themeFile', $file . '.php');
772 }
773
774 break;
775 }
776
777 parent::render();
778 }
779
780 /**
781 * Route the application.
782 *
783 * Routing is the process of examining the request environment to determine which
784 * component should receive the request. The component optional parameters
785 * are then set in the request object to be processed when the application is being
786 * dispatched.
787 *
788 * @return void
789 *
790 * @since 3.2
791 */
792 protected function route()
793 {
794 // Execute the parent method
795 parent::route();
796
797 $Itemid = $this->input->getInt('Itemid', null);
798 $this->authorise($Itemid);
799 }
800
801 /**
802 * Set the current state of the detect browser option.
803 *
804 * @param boolean $state The new state of the detect browser option
805 *
806 * @return boolean The previous state
807 *
808 * @since 3.2
809 */
810 public function setDetectBrowser($state = false)
811 {
812 $old = $this->_detect_browser;
813 $this->_detect_browser = $state;
814
815 return $old;
816 }
817
818 /**
819 * Set the current state of the language filter.
820 *
821 * @param boolean $state The new state of the language filter
822 *
823 * @return boolean The previous state
824 *
825 * @since 3.2
826 */
827 public function setLanguageFilter($state = false)
828 {
829 $old = $this->_language_filter;
830 $this->_language_filter = $state;
831
832 return $old;
833 }
834
835 /**
836 * Overrides the default template that would be used
837 *
838 * @param string $template The template name
839 * @param mixed $styleParams The template style parameters
840 *
841 * @return void
842 *
843 * @since 3.2
844 */
845 public function setTemplate($template, $styleParams = null)
846 {
847 if (is_dir(JPATH_THEMES . '/' . $template))
848 {
849 $this->template = new stdClass;
850 $this->template->template = $template;
851
852 if ($styleParams instanceof Registry)
853 {
854 $this->template->params = $styleParams;
855 }
856 else
857 {
858 $this->template->params = new Registry($styleParams);
859 }
860
861 // Store the template and its params to the config
862 $this->set('theme', $this->template->template);
863 $this->set('themeParams', $this->template->params);
864 }
865 }
866 }
867