CommonComponent.php 10 KB

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