Browse Source

Add RefererRedirect component.

mscherer 6 years ago
parent
commit
51ee035a31

+ 54 - 0
docs/Component/RefererRedirect.md

@@ -0,0 +1,54 @@
+# RefererRedirect component
+
+## Why this component
+(Referer) Redirecting is a often wrongly/badly implemented pattern.
+Especially the session is here usually the worst possible usability approach.
+As soon as you have two tabs open they will basically kill each other, creating very weird experiences for the user.
+
+This component uses a referer key in query string to redirect back to given referer.
+The neat thing here is that it doesn't require changes to existing actions. This can just be
+added on top, for one or all controllers.
+
+## Alternatives
+You can also pass along all query strings always, but then you need to make sure all URLs in controllers and templates are adjusted here.
+
+## Setting it up for a controller
+Let's set it up. Inside your controller: 
+```php
+    /**
+     * @return void
+     */
+    public function initialize()
+    {
+        parent::initialize();
+        $this->loadComponent('RefererRedirect', [
+            'actions' => ['edit'],
+        ]);
+    }
+```
+We whitelisted the edit action to be auto-referer redirectable.
+
+## Adjust your links to this action
+
+From your paginated and filtered index page you can now point to the edit page like this:
+
+```php
+<?php echo $this->Html->link(
+    $this->Format->icon('edit'), 
+    ['controller' => 'Versions', 'action' => 'edit', $version->id, '?' => ['ref' => $this->getRequest()->getRequestTarget()]], 
+    ['escape' => false]
+); ?>
+```
+
+After successful save it will then redirect to exactly the current URL including all filter query strings, page, sort order etc.
+
+
+## When not to use
+Make sure you are not using such approaches when linking to an action that removes an entity from the view of that entity.
+So do not use it for deleting actions while you are on the view action of this entity, for example.
+
+## Security
+It is advised to whitelist the actions for valid referer redirect.
+
+Also make sure the referer is always a local one (not pointing to external URLs).  
+The component here itself already checks for this.

+ 68 - 0
src/Controller/Component/RefererRedirectComponent.php

@@ -0,0 +1,68 @@
+<?php
+namespace Tools\Controller\Component;
+
+use Cake\Controller\Component;
+use Cake\Event\Event;
+use Cake\Http\Response;
+
+/**
+ * Uses a referer key in query string to redirect to given referer.
+ * Useful for passing to edit forms if you want a different target as redirect than the default.
+ * The neat thing here is that it doesn't require changes to existing actions. This can just be
+ * added on top, for one or all controllers.
+ */
+class RefererRedirectComponent extends Component {
+
+	const QUERY_REFERER = 'ref';
+
+	/**
+	 * @var array
+	 */
+	protected $_defaultConfig = [
+		'actions' => [],
+	];
+
+	/**
+	 * @param \Cake\Event\Event $event
+	 * @param string|array $url A string or array containing the redirect location
+	 * @param \Cake\Http\Response $response The response object.
+	 *
+	 * @return \Cake\Http\Response|null
+	 */
+	public function beforeRedirect(Event $event, $url, Response $response) {
+		$actions = $this->getConfig('actions');
+		$currentAction = $this->getController()->getRequest()->getParam('action');
+
+		if ($actions && !in_array($currentAction, $actions, true)) {
+			return null;
+		}
+
+		$referer = $this->referer();
+		if (!$referer) {
+			return null;
+		}
+
+		return $response->withLocation($referer);
+	}
+
+	/**
+	 * Only accept relative URLs.
+	 *
+	 * @see \Cake\Http\ServerRequest::referer()
+	 *
+	 * @return string|null
+	 */
+	protected function referer() {
+		$referer = $this->getController()->getRequest()->getQuery(static::QUERY_REFERER);
+		if (!$referer) {
+			return null;
+		}
+
+		if (strpos($referer, '/') !== 0) {
+			return null;
+		}
+
+		return $referer;
+	}
+
+}