modmanagerresponse.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. require_once MODX_CORE_PATH . 'model/modx/modresponse.class.php';
  11. /**
  12. * Encapsulates an HTTP response from the MODX manager.
  13. *
  14. * {@inheritdoc}
  15. *
  16. * @package modx
  17. */
  18. class modManagerResponse extends modResponse {
  19. /** @var array A cached array of the current modAction object */
  20. public $action = array();
  21. public $namespace = 'core';
  22. public $namespaces = array();
  23. protected function _loadNamespaces() {
  24. $loaded = false;
  25. $cache = $this->modx->call('modNamespace','loadCache',array(&$this->modx));
  26. if ($cache) {
  27. $this->namespaces = $cache;
  28. $loaded = true;
  29. }
  30. return $loaded;
  31. }
  32. /**
  33. * @param array $options
  34. * @return mixed|string
  35. */
  36. public function outputContent(array $options = array()) {
  37. $route = $this->modx->request->action;
  38. $this->namespace = $this->modx->request->namespace;
  39. if (empty($route)) {
  40. $route = $this->namespace == 'core' ? 'welcome' : 'index';
  41. }
  42. $this->modx->lexicon->load('dashboard','topmenu','file','action');
  43. $this->_loadNamespaces();
  44. if (!array_key_exists($this->namespace,$this->namespaces)) {
  45. $this->namespace = 'core';
  46. $this->action = array();
  47. } else {
  48. $namespace = $this->namespaces[$this->namespace];
  49. $this->action['namespace'] = $this->namespace;
  50. $this->action['namespace_name'] = $namespace['name'];
  51. $this->action['namespace_path'] = $namespace['path'];
  52. $this->action['namespace_assets_path'] = $namespace['assets_path'];
  53. $this->action['lang_topics'] = '';
  54. $this->action['controller'] = $route;
  55. }
  56. $isDeprecated = false;
  57. /* handle 2.2< controllers */
  58. if (intval($route) > 0) {
  59. $this->modx->request->loadActionMap();
  60. $this->action = !empty($this->modx->actionMap[$route]) ? $this->modx->actionMap[$route] : array();
  61. $this->namespace = !empty($this->action['namespace']) ? $this->action['namespace'] : 'core';
  62. $this->modx->deprecated('2.3.0', 'Support for modAction has been replaced with routing based on a namespace and action name. Please update the extra with the namespace ' . $this->namespace . ' to the routing based system.', 'modAction support');
  63. $isDeprecated = true;
  64. }
  65. $isLoggedIn = $this->validateAuthentication();
  66. if ($isLoggedIn && !$this->checkForMenuPermissions($route)) {
  67. $this->body = $this->modx->error->failure($this->modx->lexicon('access_denied'));
  68. } else {
  69. $this->modx->loadClass('modManagerController','',false,true);
  70. $className = $this->loadControllerClass(!$isDeprecated);
  71. $this->instantiateController($className,$isDeprecated ? 'getInstanceDeprecated' : 'getInstance');
  72. $this->body = $this->modx->controller->render();
  73. }
  74. if (empty($this->body)) {
  75. $this->body = $this->modx->error->failure($this->modx->lexicon('action_err_ns'));
  76. }
  77. return $this->send();
  78. }
  79. /**
  80. * Ensure the user has access to the manager
  81. * @return bool|string
  82. */
  83. public function validateAuthentication() {
  84. $isLoggedIn = $this->modx->user->isAuthenticated('mgr');
  85. if (!$isLoggedIn) {
  86. $alternateLogin = $this->modx->getOption('manager_login_url_alternate',null,'');
  87. if (!empty($alternateLogin)) {
  88. $this->modx->sendRedirect($alternateLogin);
  89. return '';
  90. }
  91. $this->namespace = 'core';
  92. $this->action['namespace'] = 'core';
  93. $this->action['namespace_name'] = 'core';
  94. $this->action['namespace_path'] = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  95. $this->action['namespace_assets_path'] = $this->modx->getOption('assets_path',null,MODX_ASSETS_PATH);
  96. $this->action['lang_topics'] = 'login';
  97. $this->action['controller'] = 'security/login';
  98. } else if (!$this->modx->hasPermission('frames')) {
  99. $this->namespace = 'core';
  100. $this->action['namespace'] = 'core';
  101. $this->action['namespace_name'] = 'core';
  102. $this->action['namespace_path'] = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  103. $this->action['namespace_assets_path'] = $this->modx->getOption('assets_path',null,MODX_ASSETS_PATH);
  104. $this->action['lang_topics'] = 'login';
  105. $this->action['controller'] = 'security/logout';
  106. }
  107. return $isLoggedIn;
  108. }
  109. /**
  110. * Send the response to the client
  111. */
  112. public function send() {
  113. if (is_array($this->body)) {
  114. $this->modx->smarty->assign('_e', $this->body);
  115. if (!file_exists($this->modx->smarty->template_dir.'error.tpl')) {
  116. $templatePath = $this->modx->getOption('manager_path') . 'templates/default/';
  117. $this->modx->smarty->setTemplatePath($templatePath);
  118. }
  119. echo $this->modx->smarty->fetch('error.tpl');
  120. } else {
  121. echo $this->body;
  122. }
  123. @session_write_close();
  124. exit();
  125. }
  126. /**
  127. * Include the correct controller class for the action
  128. *
  129. * @param bool $prefixNamespace Whether or not to prefix the Namespace name to the class. Default for 2.3+
  130. * controllers, set to false for 2.2< deprecated controllers.
  131. * @return string
  132. */
  133. public function loadControllerClass($prefixNamespace = true) {
  134. $theme = $this->modx->getOption('manager_theme',null,'default');
  135. $paths = $this->getNamespacePath($theme);
  136. $f = $this->action['controller'];
  137. $className = $this->getControllerClassName();
  138. if (!class_exists($className) && $this->namespace != 'core' && $prefixNamespace) {
  139. $className = ucfirst($this->namespace).$className;
  140. }
  141. if (!class_exists($className)) {
  142. $classFile = strtolower($f).'.class.php';
  143. $classPath = null;
  144. foreach ($paths as $controllersPath) {
  145. if (!file_exists($controllersPath.$classFile)) {
  146. if (file_exists($controllersPath.strtolower($f).'/index.class.php')) {
  147. $classPath = $controllersPath.strtolower($f).'/index.class.php';
  148. }
  149. } else {
  150. $classPath = $controllersPath.$classFile;
  151. break;
  152. }
  153. }
  154. /* handle Revo <2.2 controllers */
  155. if (empty($classPath)) {
  156. $className = 'modManagerControllerDeprecated';
  157. $classPath = MODX_CORE_PATH.'model/modx/modmanagercontrollerdeprecated.class.php';
  158. }
  159. if (!file_exists($classPath)) {
  160. if (file_exists(strtolower($f).'/index.class.php')) {
  161. $classPath = strtolower($f).'/index.class.php';
  162. } else { /* handle Revo <2.2 controllers */
  163. $className = 'modManagerControllerDeprecated';
  164. $classPath = MODX_CORE_PATH.'model/modx/modmanagercontrollerdeprecated.class.php';
  165. }
  166. }
  167. ob_start();
  168. require_once $classPath;
  169. ob_end_clean();
  170. }
  171. return $className;
  172. }
  173. public function instantiateController($className,$getInstanceMethod = 'getInstance') {
  174. try {
  175. $c = new $className($this->modx,$this->action);
  176. if (!($c instanceof modExtraManagerController) && $getInstanceMethod == 'getInstanceDeprecated') {
  177. $getInstanceMethod = 'getInstance';
  178. }
  179. /* this line allows controller derivatives to decide what instance they want to return (say, for derivative class_key types) */
  180. $this->modx->controller = call_user_func_array(array($c,$getInstanceMethod),array(&$this->modx,$className,$this->action));
  181. $this->modx->controller->setProperties($c instanceof SecurityLoginManagerController ? $_POST : array_merge($_GET,$_POST));
  182. $this->modx->controller->initialize();
  183. } catch (Exception $e) {
  184. die($e->getMessage());
  185. }
  186. return $this->modx->controller;
  187. }
  188. /**
  189. * If this action has a menu item, ensure user has access to menu
  190. * @param string $action
  191. * @return bool
  192. */
  193. public function checkForMenuPermissions($action) {
  194. $canAccess = true;
  195. /** @var modMenu $menu */
  196. $menu = $this->modx->getObject('modMenu', array(
  197. 'action' => $action,
  198. 'namespace' => $this->namespace,
  199. ));
  200. if ($menu) {
  201. $permissions = $menu->get('permissions');
  202. if (!empty($permissions)) {
  203. $permissions = explode(',', $permissions);
  204. foreach ($permissions as $permission) {
  205. if (!$this->modx->hasPermission($permission)) {
  206. return false;
  207. }
  208. }
  209. }
  210. }
  211. return $canAccess;
  212. }
  213. /**
  214. * Gets the controller class name from the active modAction object
  215. *
  216. * @return string
  217. */
  218. public function getControllerClassName() {
  219. $className = $this->action['controller'].(!empty($this->action['class_postfix']) ? $this->action['class_postfix'] : 'ManagerController');
  220. $className = explode('/',$className);
  221. $o = array();
  222. foreach ($className as $k) {
  223. $o[] = ucfirst(str_replace(array('.','_','-'),'',$k));
  224. }
  225. return implode('',$o);
  226. }
  227. /**
  228. * Get the appropriate path to the controllers directory for the active Namespace.
  229. *
  230. * @param string $theme
  231. * @return array An array of paths to the Namespace's controllers directory.
  232. */
  233. public function getNamespacePath($theme = 'default') {
  234. $namespace = array_key_exists($this->namespace,$this->namespaces) ? $this->namespaces[$this->namespace] : $this->namespaces['core'];
  235. /* find context path */
  236. if (isset($namespace['name']) && $namespace['name'] != 'core') {
  237. $paths[] = $namespace['path'].'controllers/'.trim($theme,'/').'/';
  238. if ($theme != 'default') {
  239. $paths[] = $namespace['path'].'controllers/default/';
  240. }
  241. $paths[] = $namespace['path'].'controllers/';
  242. /* deprecated old usage */
  243. $paths[] = $namespace['path'].trim($theme,'/');
  244. if ($theme != 'default') {
  245. $paths[] = $namespace['path'].'default/';
  246. }
  247. $paths[] = $namespace['path'];
  248. } else {
  249. $paths[] = $namespace['path'].'controllers/'.trim($theme,'/').'/';
  250. if ($theme != 'default') {
  251. $paths[] = $namespace['path'].'controllers/default/';
  252. }
  253. $paths[] = $namespace['path'].'controllers/';
  254. }
  255. return $paths;
  256. }
  257. /**
  258. * Adds a lexicon topic to this page's language topics to load. Will load
  259. * the topic as well.
  260. *
  261. * @param string $topic The topic to load, in standard namespace:topic format
  262. * @return boolean True if successful
  263. */
  264. public function addLangTopic($topic) {
  265. $this->modx->lexicon->load($topic);
  266. $topics = $this->getLangTopics();
  267. $topics[] = $topic;
  268. return $this->setLangTopics($topics);
  269. }
  270. /**
  271. * Adds a lexicon topic to this page's language topics to load
  272. *
  273. * @return boolean True if successful
  274. */
  275. public function getLangTopics() {
  276. $topics = $this->modx->smarty->get_template_vars('_lang_topics');
  277. return explode(',',$topics);
  278. }
  279. /**
  280. * Sets the language topics for this page
  281. *
  282. * @param array $topics The array of topics to set
  283. * @return boolean True if successful
  284. */
  285. public function setLangTopics(array $topics = array()) {
  286. if (!is_array($topics) || empty($topics)) return false;
  287. $topics = array_unique($topics);
  288. $topics = implode(',',$topics);
  289. return $this->modx->smarty->assign('_lang_topics',$topics);
  290. }
  291. }