PageRenderTime 89ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/libraries/kunena/controller.php

https://github.com/xillibit/Kunena-forum
PHP | 507 lines | 249 code | 66 blank | 192 comment | 36 complexity | d855b550ed0738ba64d2fc5a659f0114 MD5 | raw file
  1. <?php
  2. /**
  3. * Kunena Component
  4. *
  5. * @package Kunena.Framework
  6. *
  7. * @copyright Copyright (C) 2008 - 2020 Kunena Team. All rights reserved.
  8. * @license https://www.gnu.org/copyleft/gpl.html GNU/GPL
  9. * @link https://www.kunena.org
  10. **/
  11. defined('_JEXEC') or die();
  12. use Joomla\CMS\Factory;
  13. use Joomla\CMS\Language\Text;
  14. use Joomla\CMS\Uri\Uri;
  15. use Joomla\CMS\Log\Log;
  16. use Joomla\CMS\Router\Route;
  17. use Joomla\CMS\Application\CMSApplicationInterface;
  18. /**
  19. * Class KunenaController
  20. *
  21. * @since Kunena
  22. */
  23. class KunenaController extends Joomla\CMS\MVC\Controller\BaseController
  24. {
  25. /**
  26. * @var CMSApplicationInterface
  27. * @since Kunena
  28. */
  29. protected $app;
  30. /**
  31. * @var KunenaUser|null
  32. * @since Kunena
  33. */
  34. public $me = null;
  35. /**
  36. * @var KunenaConfig|null
  37. * @since Kunena
  38. */
  39. public $config = null;
  40. /**
  41. * @param array $config config
  42. *
  43. * @since Kunena
  44. * @throws Exception
  45. */
  46. public function __construct($config = array())
  47. {
  48. parent::__construct($config);
  49. $this->profiler = KunenaProfiler::instance('Kunena');
  50. $this->config = KunenaFactory::getConfig();
  51. $this->me = KunenaUserHelper::getMyself();
  52. // Save user profile if it didn't exist.
  53. if ($this->me->userid && !$this->me->exists())
  54. {
  55. $this->me->save();
  56. }
  57. if (empty($this->input))
  58. {
  59. $this->input = $this->app->input;
  60. }
  61. }
  62. /**
  63. * Method to get the appropriate controller.
  64. *
  65. * @param string $prefix prefix
  66. * @param mixed $config config
  67. *
  68. * @return KunenaController
  69. * @since Kunena
  70. * @throws Exception
  71. */
  72. public static function getInstance($prefix = 'Kunena', $config = array())
  73. {
  74. static $instance = null;
  75. if (!$prefix)
  76. {
  77. $prefix = 'Kunena';
  78. }
  79. if (!empty($instance) && !isset($instance->home))
  80. {
  81. return $instance;
  82. }
  83. $input = Factory::getApplication()->input;
  84. $app = Factory::getApplication();
  85. $command = $input->get('task', 'display');
  86. // Check for a controller.task command.
  87. if (strpos($command, '.') !== false)
  88. {
  89. // Explode the controller.task command.
  90. list($view, $task) = explode('.', $command);
  91. // Reset the task without the controller context.
  92. $input->set('task', $task);
  93. }
  94. else
  95. {
  96. // Base controller.
  97. $view = strtolower(Factory::getApplication()->input->getWord('view', $app->isClient('administrator') ? 'cpanel' : 'home'));
  98. }
  99. $path = JPATH_COMPONENT . "/controllers/{$view}.php";
  100. // If the controller file path exists, include it ... else die with a 500 error.
  101. if (is_file($path))
  102. {
  103. require_once $path;
  104. }
  105. else
  106. {
  107. throw new Exception(Text::sprintf('COM_KUNENA_INVALID_CONTROLLER', ucfirst($view)), 404);
  108. }
  109. // Set the name for the controller and instantiate it.
  110. if ($app->isClient('administrator'))
  111. {
  112. $class = $prefix . 'AdminController' . ucfirst($view);
  113. KunenaFactory::loadLanguage('com_kunena.controllers', 'admin');
  114. KunenaFactory::loadLanguage('com_kunena.models', 'admin');
  115. KunenaFactory::loadLanguage('com_kunena.sys', 'admin');
  116. KunenaFactory::loadLanguage('com_kunena', 'site');
  117. }
  118. else
  119. {
  120. $class = $prefix . 'Controller' . ucfirst($view);
  121. KunenaFactory::loadLanguage('com_kunena.controllers');
  122. KunenaFactory::loadLanguage('com_kunena.models');
  123. KunenaFactory::loadLanguage('com_kunena.sys', 'admin');
  124. }
  125. if (class_exists($class))
  126. {
  127. $instance = new $class;
  128. }
  129. else
  130. {
  131. throw new Exception(Text::sprintf('COM_KUNENA_INVALID_CONTROLLER_CLASS', $class), 404);
  132. }
  133. return $instance;
  134. }
  135. /**
  136. * Calls a task and creates HTML or JSON response from it.
  137. *
  138. * If response is in HTML, we just redirect and enqueue message if there's an exception.
  139. * NOTE: legacy display task is a special case and reverts to original Joomla behavior.
  140. *
  141. * If response is in JSON, we return JSON response, which follows Joomla\CMS\Response\JsonResponse with some extra
  142. * data:
  143. *
  144. * Default: {code, location=null, success, message, messages, data={step, location, html}}
  145. * Redirect: {code, location=[string], success, message, messages=null, data}
  146. * Exception: {code, location=[null|string], success=false, message, messages, data={exceptions=[{code,
  147. * message}...]}}
  148. *
  149. * code = [int]: Usually HTTP status code, but can also error code from the exception (informal only).
  150. * location = [null|string]: If set, JavaScript should always redirect to another page.
  151. * success = [bool]: Determines whether the request (or action) was successful. Can be false without being an
  152. * error.
  153. * message = [string|null]: The main response message.
  154. * messages = [array|null]: Array of enqueue'd messages.
  155. * data = [mixed]: The response data.
  156. *
  157. * @param string $task Task to be run.
  158. *
  159. * @return void
  160. * @since Kunena
  161. * @throws Exception
  162. * @throws null
  163. */
  164. public function execute($task)
  165. {
  166. if (!$task)
  167. {
  168. $task = 'display';
  169. }
  170. $app = Factory::getApplication();
  171. $this->format = $this->input->getWord('format', 'html');
  172. try
  173. {
  174. // TODO: This would be great, but we would need to store POST before doing it in here...
  175. /*
  176. if ($task != 'display')
  177. {
  178. // Make sure that Kunena is online before running any tasks (doesn't affect admins).
  179. if (!KunenaForum::enabled(true))
  180. {
  181. throw new KunenaExceptionAuthorise(Text::_('COM_KUNENA_FORUM_IS_OFFLINE'), 503);
  182. }
  183. // If forum is for registered users only, prevent guests from accessing tasks.
  184. if ($this->config->regonly && !$this->me->exists())
  185. {
  186. throw new KunenaExceptionAuthorise(Text::_('COM_KUNENA_LOGIN_NOTIFICATION'), 403);
  187. }
  188. }
  189. */
  190. // Execute the task.
  191. $content = static::executeTask($task);
  192. }
  193. catch (Exception $e)
  194. {
  195. $content = $e;
  196. }
  197. // Legacy view support.
  198. if ($task == 'display')
  199. {
  200. if ($content instanceof Exception)
  201. {
  202. throw $content;
  203. }
  204. return;
  205. }
  206. // Create HTML redirect.
  207. if ($this->format == 'html')
  208. {
  209. if ($content instanceof Exception)
  210. {
  211. $app->enqueueMessage($content->getMessage(), 'error');
  212. if (!$this->redirect)
  213. {
  214. // On exceptions always return back to the referrer page.
  215. $this->setRedirect(KunenaRoute::getReferrer());
  216. }
  217. }
  218. // The following code gets only called for successful tasks.
  219. if (!$this->redirect)
  220. {
  221. // If controller didn't set a new redirect, try if request has return url in it.
  222. $return = base64_decode($app->input->getBase64('return'));
  223. // Only allow internal urls to be used.
  224. if ($return && Uri::isInternal($return))
  225. {
  226. $redirect = Route::_($return, false);
  227. }
  228. // Otherwise return back to the referrer.
  229. else
  230. {
  231. $redirect = KunenaRoute::getReferrer();
  232. }
  233. $this->setRedirect($redirect);
  234. }
  235. return;
  236. }
  237. // Otherwise tell the browser that our response is in JSON.
  238. header('Content-type: application/json', true);
  239. // Create JSON response and set the redirect.
  240. $response = new KunenaResponseJson($content, null, false, !empty($this->redirect));
  241. $response->location = $this->redirect;
  242. // In case of an error we want to set HTTP error code.
  243. if ($content instanceof Exception)
  244. {
  245. // We want to wrap the exception to be able to display correct HTTP status code.
  246. $exception = new KunenaExceptionAuthorise($content->getMessage(), $content->getCode(), $content);
  247. header('HTTP/1.1 ' . $exception->getResponseStatus(), true);
  248. }
  249. echo json_encode($response);
  250. // It's much faster and safer to exit now than let Joomla to send the response.
  251. Factory::getApplication()->close();
  252. }
  253. /**
  254. * Execute task (slightly modified from Joomla).
  255. *
  256. * @param string $task task
  257. *
  258. * @return mixed
  259. * @since Kunena
  260. * @throws Exception
  261. */
  262. protected function executeTask($task)
  263. {
  264. $dot = strpos($task, '.');
  265. $this->task = $dot ? substr($task, $dot + 1) : $task;
  266. $task = strtolower($this->task);
  267. if (isset($this->taskMap[$this->task]))
  268. {
  269. $doTask = $this->taskMap[$this->task];
  270. }
  271. elseif (isset($this->taskMap['__default']))
  272. {
  273. $doTask = $this->taskMap['__default'];
  274. }
  275. else
  276. {
  277. throw new Exception(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404);
  278. }
  279. // Record the actual task being fired
  280. $this->doTask = $doTask;
  281. return $this->$doTask();
  282. }
  283. /**
  284. * Method to display a view.
  285. *
  286. * @param boolean $cachable If true, the view output will be cached
  287. * @param array|bool $urlparams An array of safe url parameters and their variable types, for valid values see
  288. * {@link Joomla\CMS\Filter\InputFilter::clean()}.
  289. *
  290. * @return Joomla\CMS\MVC\Controller\BaseController A Joomla\CMS\MVC\Controller\BaseController object to
  291. * support chaining.
  292. * @since Kunena
  293. * @throws Exception
  294. * @throws null
  295. */
  296. public function display($cachable = false, $urlparams = false)
  297. {
  298. KUNENA_PROFILER ? $this->profiler->mark('beforeDisplay') : null;
  299. KUNENA_PROFILER ? KunenaProfiler::instance()->start('function ' . __CLASS__ . '::' . __FUNCTION__ . '()') : null;
  300. // Get the document object.
  301. $document = Factory::getApplication()->getDocument();
  302. // Set the default view name and format from the Request.
  303. $vName = Factory::getApplication()->input->getWord('view', $this->app->isClient('administrator') ? 'cpanel' : 'home');
  304. $lName = Factory::getApplication()->input->getWord('layout', 'default');
  305. $vFormat = $document->getType();
  306. if ($this->app->isClient('administrator'))
  307. {
  308. // Load admin language files
  309. KunenaFactory::loadLanguage('com_kunena.install', 'admin');
  310. KunenaFactory::loadLanguage('com_kunena.views', 'admin');
  311. // Load last to get deprecated language files to work
  312. KunenaFactory::loadLanguage('com_kunena', 'admin');
  313. // Version warning, disable J4 for now.
  314. require_once KPATH_ADMIN . '/install/version.php';
  315. $version = new KunenaVersion;
  316. $version_warning = $version->getVersionWarning();
  317. }
  318. else
  319. {
  320. // Load site language files
  321. KunenaFactory::loadLanguage('com_kunena.views');
  322. KunenaFactory::loadLanguage('com_kunena.templates');
  323. // Load last to get deprecated language files to work
  324. KunenaFactory::loadLanguage('com_kunena');
  325. $menu = $this->app->getMenu();
  326. $active = $menu->getActive();
  327. // Check if menu item was correctly routed
  328. $routed = $menu->getItem(KunenaRoute::getItemID());
  329. if ($vFormat == 'html' && !empty($routed->id) && (empty($active->id) || $active->id != $routed->id))
  330. {
  331. // Routing has been changed, redirect
  332. // FIXME: check possible redirect loops!
  333. $route = KunenaRoute::_(null, false);
  334. $activeId = !empty($active->id) ? $active->id : 0;
  335. Log::add("Redirect from " . Uri::getInstance()->toString(array('path', 'query')) . " ({$activeId}) to {$route} ($routed->id)", Log::DEBUG, 'kunena');
  336. $this->app->redirect($route);
  337. }
  338. // Joomla 2.5+ multi-language support
  339. /*
  340. // FIXME:
  341. if (isset($active->language) && $active->language != '*') {
  342. $language = Factory::getApplication()->getDocument()->getLanguage();
  343. if (strtolower($active->language) != strtolower($language)) {
  344. $route = KunenaRoute::_(null, false);
  345. Log::add("Language redirect from ".Uri::getInstance()->toString(array('path', 'query'))." to {$route}", Log::DEBUG, 'kunena');
  346. $this->redirect ($route);
  347. }
  348. }
  349. */
  350. }
  351. $view = $this->getView($vName, $vFormat);
  352. if ($view)
  353. {
  354. if ($this->app->isClient('site') && $vFormat == 'html')
  355. {
  356. $common = $this->getView('common', $vFormat);
  357. $model = $this->getModel('common');
  358. $common->setModel($model, true);
  359. $view->ktemplate = $common->ktemplate = KunenaFactory::getTemplate();
  360. $view->common = $common;
  361. }
  362. // Set the view layout.
  363. $view->setLayout($lName);
  364. // Get the appropriate model for the view.
  365. $model = $this->getModel($vName);
  366. // Push the model into the view (as default).
  367. $view->setModel($model, true);
  368. // Push document object into the view.
  369. $view->document = $document;
  370. // Render the view.
  371. if ($vFormat == 'html')
  372. {
  373. Joomla\CMS\Plugin\PluginHelper::importPlugin('kunena');
  374. Factory::getApplication()->triggerEvent('onKunenaDisplay', array('start', $view));
  375. $view->displayAll();
  376. Factory::getApplication()->triggerEvent('onKunenaDisplay', array('end', $view));
  377. }
  378. else
  379. {
  380. $view->displayLayout();
  381. }
  382. }
  383. KUNENA_PROFILER ? KunenaProfiler::instance()->stop('function ' . __CLASS__ . '::' . __FUNCTION__ . '()') : null;
  384. return $this;
  385. }
  386. /**
  387. * Escapes a value for output in a view script.
  388. *
  389. * @param string $var The output to escape.
  390. *
  391. * @return string The escaped value.
  392. * @since Kunena
  393. */
  394. public function escape($var)
  395. {
  396. return htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
  397. }
  398. /**
  399. * @return string
  400. * @since Kunena
  401. */
  402. public function getRedirect()
  403. {
  404. return $this->redirect;
  405. }
  406. /**
  407. * @return string
  408. * @since Kunena
  409. */
  410. public function getMessage()
  411. {
  412. return $this->message;
  413. }
  414. /**
  415. * @return string
  416. * @since Kunena
  417. */
  418. public function getMessageType()
  419. {
  420. return $this->messageType;
  421. }
  422. /**
  423. * Redirect back to the referrer page.
  424. *
  425. * If there's no referrer or it's external, Kunena will return to the default page.
  426. * Also redirects back to tasks are prevented.
  427. *
  428. * @param string $default default
  429. * @param string $anchor anchor
  430. *
  431. * @return void
  432. * @since Kunena
  433. * @throws null
  434. * @throws Exception
  435. */
  436. protected function setRedirectBack($default = null, $anchor = null)
  437. {
  438. $this->setRedirect(KunenaRoute::getReferrer($default, $anchor));
  439. }
  440. }