CommonComponent.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. namespace Tools\Controller\Component;
  3. use Cake\Utility\Inflector;
  4. use Shim\Controller\Component\Component;
  5. use Cake\Core\Configure;
  6. use Cake\Event\Event;
  7. use Cake\Routing\Router;
  8. use Tools\Utility\Utility;
  9. /**
  10. * A component included in every app to take care of common stuff.
  11. *
  12. * @author Mark Scherer
  13. * @copyright 2012 Mark Scherer
  14. * @license MIT
  15. */
  16. class CommonComponent extends Component {
  17. /**
  18. * For this helper the controller has to be passed as reference
  19. * for manual startup with $disableStartup = true (requires this to be called prior to any other method)
  20. *
  21. * @return void
  22. */
  23. public function startup(Event $event) {
  24. // Data preparation
  25. if (!empty($this->Controller->request->data) && !Configure::read('DataPreparation.notrim')) {
  26. $this->Controller->request->data = Utility::trimDeep($this->Controller->request->data);
  27. }
  28. if (!empty($this->Controller->request->query) && !Configure::read('DataPreparation.notrim')) {
  29. $this->Controller->request->query = Utility::trimDeep($this->Controller->request->query);
  30. }
  31. if (!empty($this->Controller->request->params['pass']) && !Configure::read('DataPreparation.notrim')) {
  32. $this->Controller->request->params['pass'] = Utility::trimDeep($this->Controller->request->params['pass']);
  33. }
  34. }
  35. /**
  36. * List all direct actions of a controller
  37. *
  38. * @return array Actions
  39. */
  40. public function listActions() {
  41. $parentClassMethods = get_class_methods(get_parent_class($this->Controller));
  42. $subClassMethods = get_class_methods($this->Controller);
  43. $classMethods = array_diff($subClassMethods, $parentClassMethods);
  44. foreach ($classMethods as $key => $classMethod) {
  45. if (substr($classMethod, 0, 1) === '_') {
  46. unset($classMethods[$key]);
  47. }
  48. }
  49. return $classMethods;
  50. }
  51. /**
  52. * Convenience method to check on POSTED data.
  53. * Doesn't matter if it's POST, PUT or PATCH.
  54. *
  55. * Note that you can also use request->is(array('post', 'put', 'patch') directly.
  56. *
  57. * @return bool If it is of type POST/PUT/PATCH
  58. */
  59. public function isPosted() {
  60. return $this->Controller->request->is(['post', 'put', 'patch']);
  61. }
  62. /**
  63. * Add component just in time (inside actions - only when needed)
  64. * aware of plugins and config array (if passed)
  65. *
  66. * @param string $component Component
  67. * @param array $config
  68. * @param bool $callbacks (defaults to true)
  69. * @return void
  70. */
  71. public function loadComponent($component, array $config = [], $callbacks = true) {
  72. list($plugin, $componentName) = pluginSplit($component);
  73. $this->Controller->loadComponent($component, $config);
  74. if (!$callbacks) {
  75. return;
  76. }
  77. if (method_exists($this->Controller->{$componentName}, 'beforeFilter')) {
  78. $this->Controller->{$componentName}->beforeFilter(new \Cake\Event\Event('Controller.initialize', $this->Controller->{$componentName}));
  79. }
  80. if (method_exists($this->Controller->{$componentName}, 'startup')) {
  81. $this->Controller->{$componentName}->startup(new \Cake\Event\Event('Controller.startup', $this->Controller->{$componentName}));
  82. }
  83. }
  84. /**
  85. * Add helper just in time (inside actions - only when needed)
  86. * aware of plugins
  87. *
  88. * @deprecated In 3.x, but kept for easier migration for now. Will be removed in the future.
  89. * @param mixed $helpers (single string or multiple array)
  90. * @return void
  91. */
  92. public function loadHelper($helpers = []) {
  93. $this->Controller->helpers = array_merge($this->Controller->helpers, (array)$helpers);
  94. }
  95. /**
  96. * Used to get the value of a passed param.
  97. *
  98. * @param mixed $var
  99. * @param mixed $default
  100. * @return mixed
  101. */
  102. public function getPassedParam($var, $default = null) {
  103. return (isset($this->Controller->request->params['pass'][$var])) ? $this->Controller->request->params['pass'][$var] : $default;
  104. }
  105. /**
  106. * Returns defaultUrlParams including configured prefixes.
  107. *
  108. * @return array URL params
  109. */
  110. public static function defaultUrlParams() {
  111. $defaults = ['plugin' => false];
  112. $prefixes = (array)Configure::read('Routing.prefixes');
  113. foreach ($prefixes as $prefix) {
  114. $defaults[$prefix] = false;
  115. }
  116. return $defaults;
  117. }
  118. /**
  119. * Returns current url (with all missing params automatically added).
  120. * Necessary for Router::url() and comparison of urls to work.
  121. *
  122. * @param bool $asString: defaults to false = array
  123. * @return mixed URL
  124. */
  125. public function currentUrl($asString = false) {
  126. if (isset($this->Controller->request->params['prefix']) && mb_strpos($this->Controller->request->params['action'], $this->Controller->request->params['prefix']) === 0) {
  127. $action = mb_substr($this->Controller->request->params['action'], mb_strlen($this->Controller->request->params['prefix']) + 1);
  128. } else {
  129. $action = $this->Controller->request->params['action'];
  130. }
  131. $url = array_merge($this->Controller->request->params['pass'], ['prefix' => isset($this->Controller->request->params['prefix']) ? $this->Controller->request->params['prefix'] : null,
  132. 'plugin' => $this->Controller->request->params['plugin'], 'action' => $action, 'controller' => $this->Controller->request->params['controller']]);
  133. if ($asString === true) {
  134. return Router::url($url);
  135. }
  136. return $url;
  137. }
  138. /**
  139. * Smart Referer Redirect - will try to use an existing referer first
  140. * otherwise it will use the default url
  141. *
  142. * @param mixed $whereTo URL
  143. * @param bool $allowSelf if redirect to the same controller/action (url) is allowed
  144. * @param int $status
  145. * @return \Cake\Network\Response
  146. */
  147. public function autoRedirect($whereTo, $allowSelf = false, $status = null) {
  148. if ($allowSelf || $this->Controller->referer(null, true) !== '/' . $this->Controller->request->url) {
  149. return $this->Controller->redirect($this->Controller->referer($whereTo, true), $status);
  150. }
  151. return $this->Controller->redirect($whereTo, $status);
  152. }
  153. /**
  154. * Should be a 303, but:
  155. * Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
  156. *
  157. * TODO: change to 303 with backwardscompatability for older browsers...
  158. *
  159. * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
  160. *
  161. * @param mixed $whereTo URL
  162. * @param int $status
  163. * @return \Cake\Network\Response
  164. */
  165. public function postRedirect($whereTo, $status = 302) {
  166. return $this->Controller->redirect($whereTo, $status);
  167. }
  168. /**
  169. * Combine auto with post
  170. * also allows whitelisting certain actions for autoRedirect (use Controller::$autoRedirectActions)
  171. *
  172. * @param mixed $whereTo URL
  173. * @param bool $conditionalAutoRedirect false to skip whitelisting
  174. * @param int $status
  175. * @return \Cake\Network\Response
  176. */
  177. public function autoPostRedirect($whereTo, $conditionalAutoRedirect = true, $status = 302) {
  178. $referer = $this->Controller->referer($whereTo, true);
  179. if (!$conditionalAutoRedirect && !empty($referer)) {
  180. return $this->postRedirect($referer, $status);
  181. }
  182. if (!empty($referer)) {
  183. $referer = Router::parse($referer);
  184. }
  185. if (!$conditionalAutoRedirect || empty($this->Controller->autoRedirectActions) || is_array($referer) && !empty($referer['action'])) {
  186. // Be sure that controller offset exists, otherwise you
  187. // will run into problems, if you use url rewriting.
  188. $refererController = null;
  189. if (isset($referer['controller'])) {
  190. $refererController = $referer['controller'];
  191. }
  192. // fixme
  193. if (!isset($this->Controller->autoRedirectActions)) {
  194. $this->Controller->autoRedirectActions = [];
  195. }
  196. foreach ($this->Controller->autoRedirectActions as $action) {
  197. list($controller, $action) = pluginSplit($action);
  198. if (!empty($controller) && $refererController !== '*' && $refererController != $controller) {
  199. continue;
  200. }
  201. if (empty($controller) && $refererController != $this->Controller->request->params['controller']) {
  202. continue;
  203. }
  204. if (!in_array($referer['action'], $this->Controller->autoRedirectActions, true)) {
  205. continue;
  206. }
  207. return $this->autoRedirect($whereTo, true, $status);
  208. }
  209. }
  210. return $this->postRedirect($whereTo, $status);
  211. }
  212. /**
  213. * Automatically add missing url parts of the current url including
  214. * - querystring (especially for 3.x then)
  215. * - passed params
  216. *
  217. * @param mixed $url
  218. * @param int $status
  219. * @return \Cake\Network\Response
  220. */
  221. public function completeRedirect($url = null, $status = null) {
  222. if ($url === null) {
  223. $url = $this->Controller->request->params;
  224. unset($url['pass']);
  225. unset($url['isAjax']);
  226. }
  227. if (is_array($url)) {
  228. $url += $this->Controller->request->params['pass'];
  229. }
  230. return $this->Controller->redirect($url, $status);
  231. }
  232. /**
  233. * Set headers to cache this request.
  234. * Opposite of Controller::disableCache()
  235. *
  236. * @param int $seconds
  237. * @return void
  238. */
  239. public function forceCache($seconds = HOUR) {
  240. $this->Controller->response->header('Cache-Control', 'public, max-age=' . $seconds);
  241. $this->Controller->response->header('Last-modified', gmdate("D, j M Y H:i:s", time()) . " GMT");
  242. $this->Controller->response->header('Expires', gmdate("D, j M Y H:i:s", time() + $seconds) . " GMT");
  243. }
  244. /**
  245. * Referrer checking (where does the user come from)
  246. * Only returns true for a valid external referrer.
  247. *
  248. * @param string|null $ref Referer
  249. * @return bool Success
  250. */
  251. public function isForeignReferer($ref = null) {
  252. if ($ref === null) {
  253. $ref = env('HTTP_REFERER');
  254. }
  255. if (!$ref) {
  256. return false;
  257. }
  258. $base = Configure::read('App.fullBaseUrl') . '/';
  259. if (strpos($ref, $base) === 0) {
  260. return false;
  261. }
  262. return true;
  263. }
  264. }