modrequest.class.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  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. /**
  11. * Encapsulates the interaction of MODX with an HTTP request.
  12. *
  13. * This class represents the functional portion of the MODX {@link
  14. * http://www.martinfowler.com/eaaCatalog/frontController.html front-
  15. * controller}, and is responsible for sanitizing, interpretting, and
  16. * dispatching a web request to the appropriate MODX {@link modResource
  17. * Web Resource}.
  18. *
  19. * @package modx
  20. */
  21. class modRequest {
  22. /**
  23. * A reference to the modX object
  24. * @var modX $modx
  25. */
  26. public $modx = null;
  27. /**
  28. * The current request method
  29. * @var string $method
  30. */
  31. public $method = null;
  32. /**
  33. * The parameters sent in the request
  34. * @var array $parameters
  35. */
  36. public $parameters = null;
  37. /**
  38. * The HTTP headers sent in the request
  39. * @var array $headers
  40. */
  41. public $headers = null;
  42. /**
  43. * @param modX $modx A reference to the modX object
  44. */
  45. function __construct(modX &$modx) {
  46. $this->modx = & $modx;
  47. $this->parameters['GET'] =& $_GET;
  48. $this->parameters['POST'] =& $_POST;
  49. $this->parameters['COOKIE'] =& $_COOKIE;
  50. $this->parameters['REQUEST'] =& $_REQUEST;
  51. }
  52. /**
  53. * The primary MODX request handler (a.k.a. controller).
  54. *
  55. * @return boolean True if a request is handled without interruption.
  56. */
  57. public function handleRequest() {
  58. $this->loadErrorHandler();
  59. // If enabled, send the X-Powered-By header to identify this site as running MODX, per discussion in #12882
  60. if ($this->modx->getOption('send_poweredby_header', null, true)) {
  61. $version = $this->modx->getVersionData();
  62. header("X-Powered-By: MODX {$version['code_name']}");
  63. }
  64. $this->sanitizeRequest();
  65. $this->modx->invokeEvent('OnHandleRequest');
  66. if (!$this->modx->checkSiteStatus()) {
  67. header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
  68. if (!$this->modx->getOption('site_unavailable_page',null,1)) {
  69. $this->modx->resource = $this->modx->newObject('modDocument');
  70. $this->modx->resource->template = 0;
  71. $this->modx->resource->content = $this->modx->getOption('site_unavailable_message');
  72. } else {
  73. $this->modx->resourceMethod = "id";
  74. $this->modx->resourceIdentifier = $this->modx->getOption('site_unavailable_page',null,1);
  75. }
  76. } else {
  77. $this->checkPublishStatus();
  78. $this->modx->resourceMethod = $this->getResourceMethod();
  79. $this->modx->resourceIdentifier = $this->getResourceIdentifier($this->modx->resourceMethod);
  80. if ($this->modx->resourceMethod == 'id' && $this->modx->getOption('friendly_urls', null, false) && $this->modx->getOption('request_method_strict', null, false)) {
  81. $uri = $this->modx->context->getResourceURI($this->modx->resourceIdentifier);
  82. if (!empty($uri)) {
  83. if ((integer) $this->modx->resourceIdentifier === (integer) $this->modx->getOption('site_start', null, 1)) {
  84. $url = $this->modx->getOption('site_url', null, MODX_SITE_URL);
  85. } else {
  86. $url = $this->modx->getOption('site_url', null, MODX_SITE_URL) . $uri;
  87. }
  88. $this->modx->sendRedirect($url, array('responseCode' => $_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently'));
  89. }
  90. }
  91. }
  92. if (empty ($this->modx->resourceMethod)) {
  93. $this->modx->resourceMethod = "id";
  94. }
  95. if ($this->modx->resourceMethod == "alias") {
  96. $this->modx->resourceIdentifier = $this->_cleanResourceIdentifier($this->modx->resourceIdentifier);
  97. }
  98. if ($this->modx->resourceMethod == "alias") {
  99. $found = $this->modx->findResource($this->modx->resourceIdentifier);
  100. if ($found) {
  101. $this->modx->resourceIdentifier = $found;
  102. $this->modx->resourceMethod = 'id';
  103. } else {
  104. $this->modx->sendErrorPage();
  105. }
  106. }
  107. $this->modx->beforeRequest();
  108. $this->modx->invokeEvent("OnWebPageInit");
  109. if (!is_object($this->modx->resource)) {
  110. if (!$this->modx->resource = $this->getResource($this->modx->resourceMethod, $this->modx->resourceIdentifier)) {
  111. $this->modx->sendErrorPage();
  112. return true;
  113. }
  114. }
  115. return $this->prepareResponse();
  116. }
  117. /**
  118. * Prepares the MODX response to a web request that is being handled.
  119. *
  120. * @param array $options An array of options
  121. * @return boolean True if the response is properly prepared.
  122. */
  123. public function prepareResponse(array $options = array()) {
  124. $this->modx->beforeProcessing();
  125. $this->modx->invokeEvent("OnLoadWebDocument");
  126. if (!$this->modx->getResponse()) {
  127. $this->modx->log(modX::LOG_LEVEL_FATAL, 'Could not load response class.');
  128. }
  129. $this->modx->response->outputContent($options);
  130. }
  131. /**
  132. * Gets the method used to request a resource.
  133. *
  134. * @return string 'alias', 'id', or an empty string.
  135. */
  136. public function getResourceMethod() {
  137. $method = '';
  138. $hasId = isset($_REQUEST[$this->modx->getOption('request_param_id',null,'id')]);
  139. $hasAlias = isset($_REQUEST[$this->modx->getOption('request_param_alias',null,'q')]);
  140. if ($hasId || $hasAlias) {
  141. if ($this->modx->getOption('request_method_strict', null, false)) {
  142. $method = $this->modx->getOption('friendly_urls', null, false) ? 'alias' : 'id';
  143. } elseif ($hasAlias) {
  144. $method = "alias";
  145. } elseif ($hasId) {
  146. $method = "id";
  147. }
  148. }
  149. return $method;
  150. }
  151. /**
  152. * Gets a requested resource and all required data.
  153. *
  154. * @param string $method The method, 'id', or 'alias', by which to perform
  155. * the resource lookup.
  156. * @param string|integer $identifier The identifier with which to search.
  157. * @param array $options An array of options for the resource fetching
  158. * @return modResource The requested modResource instance or request
  159. * is forwarded to the error page, or unauthorized page.
  160. */
  161. public function getResource($method, $identifier, array $options = array()) {
  162. $resource = null;
  163. if ($method == 'alias') {
  164. $resourceId = $this->modx->findResource($identifier);
  165. } else {
  166. $resourceId = $identifier;
  167. }
  168. if (!is_numeric($resourceId)) {
  169. $this->modx->sendErrorPage();
  170. }
  171. $isForward = array_key_exists('forward', $options) && !empty($options['forward']);
  172. $fromCache = false;
  173. $cacheKey = $this->modx->context->get('key') . "/resources/{$resourceId}";
  174. $cachedResource = $this->modx->cacheManager->get($cacheKey, array(
  175. xPDO::OPT_CACHE_KEY => $this->modx->getOption('cache_resource_key', null, 'resource'),
  176. xPDO::OPT_CACHE_HANDLER => $this->modx->getOption('cache_resource_handler', null, $this->modx->getOption(xPDO::OPT_CACHE_HANDLER)),
  177. xPDO::OPT_CACHE_FORMAT => (integer) $this->modx->getOption('cache_resource_format', null, $this->modx->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP)),
  178. ));
  179. if (is_array($cachedResource) && array_key_exists('resource', $cachedResource) && is_array($cachedResource['resource'])) {
  180. /** @var modResource $resource */
  181. $resource = $this->modx->newObject($cachedResource['resourceClass']);
  182. if ($resource) {
  183. $resource->fromArray($cachedResource['resource'], '', true, true, true);
  184. $resource->_content = $cachedResource['resource']['_content'];
  185. $resource->_isForward = $isForward;
  186. if (isset($cachedResource['contentType'])) {
  187. $contentType = $this->modx->newObject('modContentType');
  188. $contentType->fromArray($cachedResource['contentType'], '', true, true, true);
  189. $resource->addOne($contentType, 'ContentType');
  190. }
  191. if (isset($cachedResource['resourceGroups'])) {
  192. $rGroups = array();
  193. foreach ($cachedResource['resourceGroups'] as $rGroupKey => $rGroup) {
  194. $rGroups[$rGroupKey]= $this->modx->newObject('modResourceGroupResource', $rGroup);
  195. }
  196. $resource->addMany($rGroups);
  197. }
  198. if (isset($cachedResource['policyCache'])) $resource->setPolicies(array($this->modx->context->get('key') => $cachedResource['policyCache']));
  199. if (isset($cachedResource['elementCache'])) $this->modx->elementCache = $cachedResource['elementCache'];
  200. if (isset($cachedResource['sourceCache'])) $this->modx->sourceCache = $cachedResource['sourceCache'];
  201. if ($resource->get('_jscripts')) $this->modx->jscripts = $this->modx->jscripts + $resource->get('_jscripts');
  202. if ($resource->get('_sjscripts')) $this->modx->sjscripts = $this->modx->sjscripts + $resource->get('_sjscripts');
  203. if ($resource->get('_loadedjscripts')) $this->modx->loadedjscripts = array_merge($this->modx->loadedjscripts, $resource->get('_loadedjscripts'));
  204. $isForward= $resource->_isForward;
  205. $resource->setProcessed(true);
  206. $fromCache = true;
  207. }
  208. }
  209. if (!$fromCache || !is_object($resource)) {
  210. $criteria = $this->modx->newQuery('modResource');
  211. $criteria->select(array($this->modx->escape('modResource').'.*'));
  212. $criteria->where(array('id' => $resourceId, 'deleted' => '0'));
  213. if (!$this->modx->hasPermission('view_unpublished') || $this->modx->getSessionState() !== modX::SESSION_STATE_INITIALIZED) {
  214. $criteria->where(array('published' => 1));
  215. }
  216. if ($resource = $this->modx->getObject('modResource', $criteria)) {
  217. if ($resource instanceof modResource) {
  218. if ($resource->get('context_key') !== $this->modx->context->get('key')) {
  219. if (!$isForward || ($isForward && !$this->modx->getOption('allow_forward_across_contexts', $options, false))) {
  220. if (!$this->modx->getCount('modContextResource', array($this->modx->context->get('key'), $resourceId))) {
  221. return null;
  222. }
  223. }
  224. }
  225. $resource->_isForward= $isForward;
  226. if (!$resource->checkPolicy('view')) {
  227. $this->modx->sendUnauthorizedPage();
  228. }
  229. if ($tvs = $resource->getMany('TemplateVars', 'all')) {
  230. /** @var modTemplateVar $tv */
  231. foreach ($tvs as $tv) {
  232. $resource->set($tv->get('name'), array(
  233. $tv->get('name'),
  234. $tv->getValue($resource->get('id')),
  235. $tv->get('display'),
  236. $tv->get('display_params'),
  237. $tv->get('type'),
  238. ));
  239. }
  240. }
  241. $this->modx->resourceGenerated = true;
  242. }
  243. }
  244. } elseif ($fromCache && $resource instanceof modResource && !$resource->get('deleted')) {
  245. if ($resource->checkPolicy('load') && ($resource->get('published') || ($this->modx->getSessionState() === modX::SESSION_STATE_INITIALIZED && $this->modx->hasPermission('view_unpublished')))) {
  246. if ($resource->get('context_key') !== $this->modx->context->get('key')) {
  247. if (!$isForward || ($isForward && !$this->modx->getOption('allow_forward_across_contexts', $options, false))) {
  248. if (!$this->modx->getCount('modContextResource', array($this->modx->context->get('key'), $resourceId))) {
  249. return null;
  250. }
  251. }
  252. }
  253. if (!$resource->checkPolicy('view')) {
  254. $this->modx->sendUnauthorizedPage();
  255. }
  256. } else {
  257. return null;
  258. }
  259. $this->modx->invokeEvent('OnLoadWebPageCache', array(
  260. 'resource' => &$resource,
  261. ));
  262. }
  263. return $resource;
  264. }
  265. /**
  266. * Gets the idetifier used to request a resource.
  267. *
  268. * @param string $method 'alias' or 'id'.
  269. * @return string The identifier for the requested resource.
  270. */
  271. public function getResourceIdentifier($method) {
  272. $identifier = '';
  273. switch ($method) {
  274. case 'alias' :
  275. $rAlias = $this->modx->getOption('request_param_alias', null, 'q');
  276. $identifier = isset ($_REQUEST[$rAlias]) ? $_REQUEST[$rAlias] : $identifier;
  277. break;
  278. case 'id' :
  279. $rId = $this->modx->getOption('request_param_id', null, 'id');
  280. $identifier = isset ($_REQUEST[$rId]) ? $_REQUEST[$rId] : $identifier;
  281. break;
  282. default :
  283. $identifier = $this->modx->getOption('site_start', null, 1);
  284. }
  285. return $identifier;
  286. }
  287. /**
  288. * Cleans the resource identifier from the request params.
  289. *
  290. * @param string $identifier The raw identifier.
  291. * @return string|integer The cleansed identifier.
  292. */
  293. public function _cleanResourceIdentifier($identifier) {
  294. if (empty ($identifier)) {
  295. if ($this->modx->getOption('base_url', null, MODX_BASE_URL) !== strtok($_SERVER["REQUEST_URI"],'?')) {
  296. $this->modx->sendRedirect($this->modx->getOption('site_url', null, MODX_SITE_URL), array('responseCode' => $_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently'));
  297. }
  298. $identifier = $this->modx->getOption('site_start', null, 1);
  299. $this->modx->resourceMethod = 'id';
  300. }
  301. elseif ($this->modx->getOption('friendly_urls', null, false) && $this->modx->resourceMethod == 'alias') {
  302. $containerSuffix = trim($this->modx->getOption('container_suffix', null, ''));
  303. $found = $this->modx->findResource($identifier);
  304. if ($found === false && !empty ($containerSuffix)) {
  305. $suffixLen = strlen($containerSuffix);
  306. $identifierLen = strlen($identifier);
  307. if (substr($identifier, $identifierLen - $suffixLen) === $containerSuffix) {
  308. $identifier = substr($identifier, 0, $identifierLen - $suffixLen);
  309. $found = $this->modx->findResource($identifier);
  310. } else {
  311. $identifier = "{$identifier}{$containerSuffix}";
  312. $found = $this->modx->findResource($identifier);
  313. }
  314. if ($found) {
  315. $parameters = $this->getParameters();
  316. unset($parameters[$this->modx->getOption('request_param_alias')]);
  317. $url = $this->modx->makeUrl($found, $this->modx->context->get('key'), $parameters, 'full');
  318. $this->modx->sendRedirect($url, array('responseCode' => $_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently'));
  319. }
  320. $this->modx->resourceMethod = 'alias';
  321. } elseif ((integer) $this->modx->getOption('site_start', null, 1) === $found) {
  322. $parameters = $this->getParameters();
  323. unset($parameters[$this->modx->getOption('request_param_alias')]);
  324. $url = $this->modx->makeUrl($this->modx->getOption('site_start', null, 1), $this->modx->context->get('key'), $parameters, 'full');
  325. $this->modx->sendRedirect($url, array('responseCode' => $_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently'));
  326. } else {
  327. if ($this->modx->getOption('friendly_urls_strict', null, false)) {
  328. $requestUri = $_SERVER['REQUEST_URI'];
  329. $qsPos = strpos($requestUri, '?');
  330. if ($qsPos !== false) $requestUri = substr($requestUri, 0, $qsPos);
  331. $fullId = $this->modx->getOption('base_url', null, MODX_BASE_URL) . $identifier;
  332. $requestUri = urldecode($requestUri);
  333. if ($fullId !== $requestUri && strpos($requestUri, $fullId) !== 0) {
  334. $parameters = $this->getParameters();
  335. unset($parameters[$this->modx->getOption('request_param_alias')]);
  336. $url = $this->modx->makeUrl($found, $this->modx->context->get('key'), $parameters, 'full');
  337. $this->modx->sendRedirect($url, array('responseCode' => $_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently'));
  338. }
  339. }
  340. $this->modx->resourceMethod = 'alias';
  341. }
  342. } else {
  343. $this->modx->resourceMethod = 'id';
  344. }
  345. return $identifier;
  346. }
  347. /**
  348. * Harden GPC variables by removing any MODX tags, Javascript, or entities.
  349. */
  350. public function sanitizeRequest() {
  351. $modxtags = array_values($this->modx->sanitizePatterns);
  352. $depth = (int)ini_get('max_input_nesting_level');
  353. $depth = ($depth <= 0) ? 99 : $depth + 1;
  354. modX :: sanitize($_GET, $modxtags, $depth);
  355. if ($this->modx->getOption('allow_tags_in_post',null,true)) {
  356. modX :: sanitize($_POST);
  357. } else {
  358. modX :: sanitize($_POST, $modxtags, $depth);
  359. }
  360. modX :: sanitize($_COOKIE, $modxtags, $depth);
  361. modX :: sanitize($_REQUEST, $modxtags, $depth);
  362. $rAlias = $this->modx->getOption('request_param_alias', null, 'q');
  363. if (isset ($_GET[$rAlias])) {
  364. $_GET[$rAlias] = preg_replace("/[^A-Za-z0-9_\-\.\/]/", "", $_GET[$rAlias]);
  365. }
  366. }
  367. /**
  368. * Loads the error handling class for the request.
  369. *
  370. * @param string $class The class to use as the error handler.
  371. */
  372. public function loadErrorHandler($class = 'modError') {
  373. if ($className = $this->modx->loadClass('error.'.$class,'',false,true)) {
  374. $this->modx->error = new $className($this->modx);
  375. } else {
  376. $this->modx->log(modX::LOG_LEVEL_FATAL,'Error handling class could not be loaded: '.$class);
  377. }
  378. }
  379. /**
  380. * Provides an easy way to initiate register logging.
  381. *
  382. * Through an array of options, you can have all calls to modX::log()
  383. * recorded in a topic of a modRegister instance. The options include:
  384. *
  385. * <ul>
  386. * <li>register: the name of the register (required)</li>
  387. * <li>topic: the topic to record to (required)</li>
  388. * <li>register_class: the modRegister class (defaults to modFileRegister)</li>
  389. * <li>log_level: the logging level (defaults to MODX_LOG_LEVEL_INFO)</li>
  390. * <li>clear: set flag to clear register before logging new messages into it (optional)</li>
  391. * </ul>
  392. *
  393. * @param array $options An array containing all the options required to
  394. * initiate and configure logging to a modRegister instance.
  395. */
  396. public function registerLogging(array $options = array()) {
  397. if (isset($options['register']) && isset($options['topic'])) {
  398. if ($this->modx->getService('registry','registry.modRegistry')) {
  399. $register_class = isset($options['register_class']) ? $options['register_class'] : 'registry.modFileRegister';
  400. $register = $this->modx->registry->getRegister($options['register'], $register_class);
  401. if ($register) {
  402. $level = isset($options['log_level']) ? $options['log_level'] : modX::LOG_LEVEL_INFO;
  403. $clear = (!empty($options['clear']) && $options['clear'] !== 'false');
  404. $this->modx->registry->setLogging($register, $options['topic'], $level, $clear);
  405. }
  406. }
  407. }
  408. }
  409. /**
  410. * Preserves the $_REQUEST superglobal to the $_SESSION.
  411. *
  412. * @param string $key A key to save the $_REQUEST as; default is 'referrer'.
  413. */
  414. public function preserveRequest($key = 'referrer') {
  415. if (isset ($_SESSION)) {
  416. $_SESSION['modx.request.' . $key] = $_REQUEST;
  417. }
  418. }
  419. /**
  420. * Retrieve a preserved $_REQUEST from $_SESSION.
  421. *
  422. * @param string $key A key to identify a specific $_REQUEST; default is 'referrer'.
  423. * @return string
  424. */
  425. public function retrieveRequest($key = 'referrer') {
  426. $request = null;
  427. if (isset ($_SESSION['modx.request.' . $key])) {
  428. $request = $_SESSION['modx.request.' . $key];
  429. }
  430. return $request;
  431. }
  432. /**
  433. * Return the HTTP headers sent through the request
  434. * @param boolean $ucKeys if true, upper-case all keys for the headers
  435. * @return array
  436. */
  437. public function getHeaders($ucKeys = false) {
  438. if (!isset($this->headers)) {
  439. $headers = array ();
  440. foreach ($_SERVER as $name => $value) {
  441. if (substr(strtoupper($name), 0, 5) == 'HTTP_') {
  442. $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
  443. }
  444. }
  445. if (!empty($headers) && $ucKeys) {
  446. $headers = array_change_key_case($headers, CASE_UPPER);
  447. }
  448. $this->headers = $headers;
  449. }
  450. return $this->headers;
  451. }
  452. /**
  453. * Checks the current status of timed publishing events and automatically (un)publishes resources if needed.
  454. */
  455. public function checkPublishStatus() {
  456. $partKey = $this->modx->getOption('cache_auto_publish_key', null, 'auto_publish');
  457. $partHandler = $this->modx->getOption('cache_auto_publish_handler', null, $this->modx->getOption(xPDO::OPT_CACHE_HANDLER));
  458. $partOptions = array(xPDO::OPT_CACHE_KEY => $partKey, xPDO::OPT_CACHE_HANDLER => $partHandler);
  459. $cacheRefreshTime = (integer) $this->modx->cacheManager->get('auto_publish', array(
  460. xPDO::OPT_CACHE_KEY => $partKey,
  461. xPDO::OPT_CACHE_HANDLER => $partHandler
  462. ));
  463. if ($cacheRefreshTime > 0) {
  464. $timeNow = time();
  465. if ($cacheRefreshTime <= $timeNow) {
  466. $results = $this->modx->cacheManager->autoPublish($partOptions);
  467. // Get the affected contexts
  468. $contexts = array();
  469. if (array_key_exists('published_resources', $results) && is_array($results['published_resources'])) {
  470. foreach ($results['published_resources'] as $published) {
  471. $contexts[] = $published['context_key'];
  472. }
  473. }
  474. if (array_key_exists('unpublished_resources', $results) && is_array($results['unpublished_resources'])) {
  475. foreach ($results['unpublished_resources'] as $unpublished) {
  476. $contexts[] = $unpublished['context_key'];
  477. }
  478. }
  479. $contexts = array_unique($contexts);
  480. // If at least one context was affected, refresh the context_settings (which contains the alias map etc)
  481. // and the resource cache for those contexts.
  482. if (count($contexts) > 0) {
  483. $this->modx->cacheManager->refresh(array(
  484. 'db' => array(),
  485. 'context_settings' => array('contexts' => $contexts),
  486. 'resource' => array('contexts' => $contexts),
  487. ));
  488. }
  489. }
  490. }
  491. }
  492. /**
  493. * Get a list of all modAction IDs
  494. *
  495. * @deprecated Has no meaning in 2.3; will be removed in 2.4/3.0
  496. *
  497. * @param string $namespace
  498. * @return array
  499. */
  500. public function getAllActionIDs($namespace = '') {
  501. $c = array();
  502. if (!empty($namespace)) $c['namespace'] = $namespace;
  503. $actions = $this->modx->getCollection('modAction',$c);
  504. $actionList = array();
  505. /** @var modAction $action */
  506. foreach ($actions as $action) {
  507. $key = ($action->get('namespace') == 'core' ? '' : $action->get('namespace').':').$action->get('controller');
  508. $actionList[$key] = $action->get('id');
  509. }
  510. // Also add old core actions for backwards compatibility
  511. $oldActions = array('browser', 'context',
  512. 'context/create', 'context/update', 'context/view',
  513. 'element', 'element/chunk', 'element/chunk/create', 'element/chunk/update',
  514. 'element/plugin', 'element/plugin/create', 'element/plugin/update:',
  515. 'element/propertyset/index', 'element/snippet', 'element/snippet/create',
  516. 'element/snippet/update', 'element/template', 'element/template/create',
  517. 'element/template/tvsort', 'element/template/update', 'element/tv',
  518. 'element/tv/create', 'element/tv/update', 'element/view', 'help',
  519. 'resource', 'resource/create', 'resource/data', 'resource/empty_recycle_bin',
  520. 'resource/site_schedule', 'resource/tvs', 'resource/update', 'search', 'security',
  521. 'security/access/policy/template/update', 'security/access/policy/update',
  522. 'security/forms', 'security/forms/profile/update', 'security/forms/set/update',
  523. 'security/login', 'security/message', 'security/permission', 'security/profile',
  524. 'security/resourcegroup/index', 'security/role', 'security/user', 'security/user/create',
  525. 'security/user/update', 'security/usergroup/create', 'security/usergroup/update',
  526. 'source/create', 'source/index', 'source/update', 'system', 'system/action',
  527. 'system/contenttype', 'system/dashboards', 'system/dashboards/create',
  528. 'system/dashboards/update', 'system/dashboards/widget/create',
  529. 'system/dashboards/widget/update', 'system/event', 'system/file', 'system/file/create',
  530. 'system/file/edit', 'system/import', 'system/import/html', 'system/info',
  531. 'system/logs/index', 'system/phpinfo', 'system/refresh_site', 'system/settings',
  532. 'welcome', 'workspaces', 'workspaces/lexicon',
  533. 'workspaces/namespace', 'workspaces/package/view');
  534. if (empty($namespace) || $namespace == 'core') {
  535. foreach ($oldActions as $a) {
  536. $actionList[$a] = $a;
  537. }
  538. }
  539. return $actionList;
  540. }
  541. /**
  542. * Get the IDs for a collection of string action keys
  543. * @param array $actions
  544. * @param string $namespace
  545. * @return array
  546. */
  547. public function getActionIDs(array $actions = array(), $namespace = 'core') {
  548. $as = array();
  549. foreach ($actions as $action) {
  550. /** @var modAction $actionObject */
  551. $actionObject = $this->modx->getObject('modAction',array(
  552. 'namespace' => $namespace,
  553. 'controller' => $action,
  554. ));
  555. if (empty($actionObject)) {
  556. $as[$action] = 0;
  557. } else {
  558. $as[$action] = $actionObject->get('id');
  559. }
  560. }
  561. return $as;
  562. }
  563. /**
  564. * Get a GPC/REQUEST variable value or an array of values from the request.
  565. *
  566. * @param string|array $keys A key or array of keys to retrieve from the GPC variable. An empty
  567. * array means get all keys of the variable.
  568. * @param string $type The type of GPC variable, GET by default (GET, POST, COOKIE or REQUEST).
  569. * @return mixed
  570. */
  571. public function getParameters($keys = array(), $type = 'GET') {
  572. $value = null;
  573. if (!is_string($type) || !in_array($type, array('GET', 'POST', 'COOKIE', 'REQUEST'))) {
  574. $type = 'GET';
  575. }
  576. if (is_array($keys)) {
  577. $value = array();
  578. if (empty($keys) && isset($this->parameters[$type])) {
  579. $keys = array_keys($this->parameters[$type]);
  580. }
  581. }
  582. if (isset($this->parameters[$type])) {
  583. $method = '';
  584. $methodIdentifier = '';
  585. if ($type == 'GET') {
  586. $method = $this->getResourceMethod();
  587. $methodIdentifier = $this->modx->getOption('request_param_' . $method, null,$method);
  588. }
  589. if (is_array($keys)) {
  590. foreach ($keys as $key) {
  591. if ($type != 'GET' || $key != $methodIdentifier) {
  592. $keyValue = $this->getParameters($key, $type);
  593. if ($keyValue !== null) $value[$key] = $keyValue;
  594. }
  595. }
  596. } else {
  597. if ($type != 'GET' || $keys != $methodIdentifier) {
  598. if (array_key_exists($keys, $this->parameters[$type])) {
  599. $value = $this->parameters[$type][$keys];
  600. }
  601. }
  602. }
  603. }
  604. return $value;
  605. }
  606. /**
  607. * Get the true client IP. Returns an array of values:
  608. *
  609. * * ip - The real, true client IP
  610. * * suspected - The suspected IP, if not alike to REMOTE_ADDR
  611. * * network - The client's network IP
  612. *
  613. * @access public
  614. * @return array
  615. */
  616. public function getClientIp() {
  617. $ip = '';
  618. $ipAll = array(); // networks IP
  619. $ipSus = array(); // suspected IP
  620. $serverVariables = array(
  621. 'HTTP_X_FORWARDED_FOR',
  622. 'HTTP_X_FORWARDED',
  623. 'HTTP_X_CLUSTER_CLIENT_IP',
  624. 'HTTP_X_COMING_FROM',
  625. 'HTTP_FORWARDED_FOR',
  626. 'HTTP_FORWARDED',
  627. 'HTTP_COMING_FROM',
  628. 'HTTP_CLIENT_IP',
  629. 'HTTP_FROM',
  630. 'HTTP_VIA',
  631. 'REMOTE_ADDR',
  632. );
  633. foreach ($serverVariables as $serverVariable) {
  634. $value = '';
  635. if (isset($_SERVER[$serverVariable])) {
  636. $value = $_SERVER[$serverVariable];
  637. } elseif (getenv($serverVariable)) {
  638. $value = getenv($serverVariable);
  639. }
  640. if (!empty($value)) {
  641. $tmp = explode(',', $value);
  642. $ipSus[] = $tmp[0];
  643. $ipAll = array_merge($ipAll,$tmp);
  644. }
  645. }
  646. $ipSus = array_unique($ipSus);
  647. $ipAll = array_unique($ipAll);
  648. $ip = (sizeof($ipSus) > 0) ? $ipSus[0] : $ip;
  649. return array(
  650. 'ip' => $ip,
  651. 'suspected' => $ipSus,
  652. 'network' => $ipAll,
  653. );
  654. }
  655. }