CommonComponent.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <?php
  2. namespace Tools\Controller\Component;
  3. use Cake\Controller\Component;
  4. use Cake\Core\Configure;
  5. use Cake\Event\EventInterface;
  6. use Cake\Http\ServerRequest;
  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. * @license MIT
  14. */
  15. class CommonComponent extends Component {
  16. /**
  17. * @var \Cake\Controller\Controller
  18. */
  19. protected $controller;
  20. /**
  21. * @param array $config
  22. * @return void
  23. */
  24. public function initialize(array $config): void {
  25. parent::initialize($config);
  26. $this->controller = $this->getController();
  27. }
  28. /**
  29. * @param \Cake\Event\EventInterface $event
  30. * @return void
  31. */
  32. public function startup(EventInterface $event) {
  33. if (Configure::read('DataPreparation.notrim')) {
  34. return;
  35. }
  36. $request = $this->controller->getRequest();
  37. if ($this->controller->getRequest()->getData()) {
  38. $newData = Utility::trimDeep($request->getData());
  39. foreach ($newData as $k => $v) {
  40. if ($request->getData($k) !== $v) {
  41. $request = $request->withData($k, $v);
  42. }
  43. }
  44. }
  45. if ($request->getQuery()) {
  46. $queryData = Utility::trimDeep($request->getQuery());
  47. if ($queryData !== $request->getQuery()) {
  48. $request = $request->withQueryParams($queryData);
  49. }
  50. }
  51. if ($request->getParam('pass')) {
  52. $passData = Utility::trimDeep($request->getParam('pass'));
  53. if ($passData !== $request->getParam('pass')) {
  54. $request = $request->withParam('pass', $passData);
  55. }
  56. }
  57. if ($request === $this->controller->getRequest()) {
  58. return;
  59. }
  60. $this->controller->setRequest($request);
  61. }
  62. /**
  63. * Returns internal redirect only, otherwise falls back to default.
  64. *
  65. * Lookup order:
  66. * - POST data
  67. * - query string
  68. * - provided default
  69. *
  70. * @param string|array $default
  71. * @param string|array|null $data
  72. * @param string $key
  73. *
  74. * @return string|array
  75. */
  76. public function getSafeRedirectUrl($default, $data = null, $key = 'redirect') {
  77. $redirectUrl = $data ?: ($this->controller->getRequest()->getData($key) ?: $this->controller->getRequest()->getQuery($key));
  78. if ($redirectUrl && (mb_substr($redirectUrl, 0, 1) !== '/' || mb_substr($redirectUrl, 0, 2) === '//')) {
  79. $redirectUrl = null;
  80. }
  81. return $redirectUrl ?: $default;
  82. }
  83. /**
  84. * List all direct actions of a controller
  85. *
  86. * @return array Actions
  87. */
  88. public function listActions() {
  89. $parentClassMethods = get_class_methods(get_parent_class($this->controller));
  90. $subClassMethods = get_class_methods($this->controller);
  91. $classMethods = array_diff($subClassMethods, $parentClassMethods);
  92. foreach ($classMethods as $key => $classMethod) {
  93. if (mb_substr($classMethod, 0, 1) === '_') {
  94. unset($classMethods[$key]);
  95. }
  96. }
  97. return $classMethods;
  98. }
  99. /**
  100. * Convenience method to check on POSTED data.
  101. * Doesn't matter if it's POST, PUT or PATCH.
  102. *
  103. * Note that you can also use request->is(array('post', 'put', 'patch') directly.
  104. *
  105. * @return bool If it is of type POST/PUT/PATCH
  106. */
  107. public function isPosted() {
  108. return $this->controller->getRequest()->is(['post', 'put', 'patch']);
  109. }
  110. /**
  111. * Adds helpers just in time (inside actions - only when needed).
  112. *
  113. * @param array $helpers
  114. * @return void
  115. */
  116. public function addHelpers(array $helpers) {
  117. $this->controller->viewBuilder()->setHelpers($helpers, true);
  118. }
  119. /**
  120. * Used to get the value of a passed param.
  121. *
  122. * @param mixed $var
  123. * @param mixed $default
  124. * @return mixed
  125. */
  126. public function getPassedParam($var, $default = null) {
  127. $passed = $this->controller->getRequest()->getParam('pass');
  128. return isset($passed[$var]) ? $passed[$var] : $default;
  129. }
  130. /**
  131. * Returns defaultUrlParams including configured prefixes.
  132. *
  133. * Deprecated: Routing.prefixes config is not needed anymore as it is always
  134. * just "prefix" now.
  135. *
  136. * @return array URL params
  137. */
  138. public static function defaultUrlParams() {
  139. $defaults = ['plugin' => false];
  140. $prefixes = (array)Configure::read('Routing.prefixes');
  141. if ($prefixes) {
  142. $defaults['prefix'] = false;
  143. }
  144. return $defaults;
  145. }
  146. /**
  147. * Returns current url (with all missing params automatically added).
  148. * Necessary for Router::url() and comparison of urls to work.
  149. *
  150. * @param bool $asString Defaults to false = array
  151. * @return mixed URL
  152. */
  153. public function currentUrl($asString = false) {
  154. $action = $this->controller->getRequest()->getParam('action');
  155. $passed = (array)$this->controller->getRequest()->getParam('pass');
  156. $url = [
  157. 'prefix' => $this->controller->getRequest()->getParam('prefix'),
  158. 'plugin' => $this->controller->getRequest()->getParam('plugin'),
  159. 'action' => $action,
  160. 'controller' => $this->controller->getRequest()->getParam('controller'),
  161. ];
  162. $url = array_merge($passed, $url);
  163. if ($asString === true) {
  164. return Router::url($url);
  165. }
  166. return $url;
  167. }
  168. /**
  169. * Smart Referer Redirect - will try to use an existing referer first
  170. * otherwise it will use the default url
  171. *
  172. * @param mixed $whereTo URL
  173. * @param bool $allowSelf if redirect to the same controller/action (url) is allowed
  174. * @param int $status
  175. * @return \Cake\Http\Response
  176. */
  177. public function autoRedirect($whereTo, $allowSelf = false, $status = 302) {
  178. if ($allowSelf || $this->controller->referer(null, true) !== $this->controller->getRequest()->getRequestTarget()) {
  179. return $this->controller->redirect($this->controller->referer($whereTo, true), $status);
  180. }
  181. return $this->controller->redirect($whereTo, $status);
  182. }
  183. /**
  184. * Should be a 303, but:
  185. * 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.
  186. *
  187. * TODO: change to 303 with backwards-compatibility for older browsers...
  188. *
  189. * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
  190. *
  191. * @param mixed $whereTo URL
  192. * @param int $status
  193. * @return \Cake\Http\Response
  194. */
  195. public function postRedirect($whereTo, $status = 302) {
  196. return $this->controller->redirect($whereTo, $status);
  197. }
  198. /**
  199. * Combine auto with post
  200. * also allows whitelisting certain actions for autoRedirect (use Controller::$autoRedirectActions)
  201. *
  202. * @param mixed $whereTo URL
  203. * @param bool $conditionalAutoRedirect false to skip whitelisting
  204. * @param int $status
  205. * @return \Cake\Http\Response
  206. */
  207. public function autoPostRedirect($whereTo, $conditionalAutoRedirect = true, $status = 302) {
  208. $referer = $this->controller->referer($whereTo, true);
  209. if (!$conditionalAutoRedirect && !empty($referer)) {
  210. return $this->postRedirect($referer, $status);
  211. }
  212. if (!empty($referer)) {
  213. //FIXME
  214. $referer = Router::parseRequest(new ServerRequest(['url' => $referer, 'environment' => ['REQUEST_METHOD' => 'GET']]));
  215. }
  216. if ($conditionalAutoRedirect && !empty($this->controller->autoRedirectActions) && is_array($referer) && !empty($referer['action'])) {
  217. // Be sure that controller offset exists, otherwise you
  218. // will run into problems, if you use url rewriting.
  219. $refererController = null;
  220. if (isset($referer['controller'])) {
  221. $refererController = $referer['controller'];
  222. }
  223. // fixme
  224. if (!isset($this->controller->autoRedirectActions)) {
  225. $this->controller->autoRedirectActions = [];
  226. }
  227. foreach ($this->controller->autoRedirectActions as $action) {
  228. [$controller, $action] = pluginSplit($action);
  229. if (!empty($controller) && $refererController !== '*' && $refererController !== $controller) {
  230. continue;
  231. }
  232. if (empty($controller) && $refererController !== $this->controller->getRequest()->getParam('controller')) {
  233. continue;
  234. }
  235. if (!in_array($referer['action'], (array)$this->controller->autoRedirectActions, true)) {
  236. continue;
  237. }
  238. return $this->autoRedirect($whereTo, true, $status);
  239. }
  240. }
  241. return $this->postRedirect($whereTo, $status);
  242. }
  243. /**
  244. * Automatically add missing URL parts of the current URL including
  245. * - querystring (especially for 3.x then)
  246. * - passed params
  247. *
  248. * @param mixed|null $url
  249. * @param int|null $status
  250. * @return \Cake\Http\Response
  251. */
  252. public function completeRedirect($url = null, $status = 302) {
  253. if ($url === null) {
  254. $url = [
  255. 'plugin' => $this->controller->getRequest()->getParam('plugin'),
  256. 'controller' => $this->controller->getRequest()->getParam('controller'),
  257. 'action' => $this->controller->getRequest()->getParam('action'),
  258. '_ext' => $this->controller->getRequest()->getParam('_ext'),
  259. ];
  260. }
  261. if (is_array($url)) {
  262. $url += $this->controller->getRequest()->getParam('pass');
  263. }
  264. return $this->controller->redirect($url, $status);
  265. }
  266. /**
  267. * Set headers to cache this request.
  268. * Opposite of Controller::disableCache()
  269. *
  270. * @param int $seconds
  271. * @return void
  272. */
  273. public function forceCache($seconds = HOUR) {
  274. $response = $this->controller->getResponse();
  275. $response = $response->withHeader('Cache-Control', 'public, max-age=' . $seconds)
  276. ->withHeader('Last-modified', gmdate('D, j M Y H:i:s', time()) . ' GMT')
  277. ->withHeader('Expires', gmdate('D, j M Y H:i:s', time() + $seconds) . ' GMT');
  278. $this->controller->setResponse($response);
  279. }
  280. /**
  281. * Referrer checking (where does the user come from)
  282. * Only returns true for a valid external referrer.
  283. *
  284. * @param string|null $ref Referer
  285. * @return bool Success
  286. */
  287. public function isForeignReferer($ref = null) {
  288. if ($ref === null) {
  289. $ref = env('HTTP_REFERER');
  290. }
  291. if (!$ref) {
  292. return false;
  293. }
  294. $base = Configure::read('App.fullBaseUrl') . '/';
  295. if (mb_strpos($ref, $base) === 0) {
  296. return false;
  297. }
  298. return true;
  299. }
  300. }