From 58528803ad1f586696a6719f4ed566b78eb7b1d9 Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Thu, 18 May 2017 12:58:50 +0200
Subject: [PATCH] introduce a redirect action

Action can now abort to 'redirect' to explicitly trigger a redirect
back to showing the current page. Automatic triggering for post->show is
now only done when a 'show' action was not explicitly requested.
---
 inc/Action/Cancel.php                    |  2 +-
 inc/Action/Draftdel.php                  |  2 +-
 inc/Action/Exception/ActionException.php | 16 +++++-
 inc/Action/Redirect.php                  | 64 ++++++++++++++++++++++++
 inc/Action/Revert.php                    |  5 +-
 inc/Action/Save.php                      |  2 +-
 inc/ActionRouter.php                     | 11 +---
 inc/actions.php                          | 46 -----------------
 8 files changed, 83 insertions(+), 65 deletions(-)
 create mode 100644 inc/Action/Redirect.php

diff --git a/inc/Action/Cancel.php b/inc/Action/Cancel.php
index 3665f20c0..c72644607 100644
--- a/inc/Action/Cancel.php
+++ b/inc/Action/Cancel.php
@@ -14,7 +14,7 @@ use dokuwiki\Action\Exception\ActionAbort;
 class Cancel extends AbstractAliasAction {
 
     public function preProcess() {
-        throw new ActionAbort();
+        throw new ActionAbort('draftdel');
     }
 
 }
diff --git a/inc/Action/Draftdel.php b/inc/Action/Draftdel.php
index 092369564..d7707b30d 100644
--- a/inc/Action/Draftdel.php
+++ b/inc/Action/Draftdel.php
@@ -20,7 +20,7 @@ class Draftdel extends AbstractAction {
 
     public function preProcess() {
         act_draftdel('fixme'); // FIXME replace this utility function
-        throw new ActionAbort();
+        throw new ActionAbort('redirect');
     }
 
 }
diff --git a/inc/Action/Exception/ActionException.php b/inc/Action/Exception/ActionException.php
index 386364c59..381584c15 100644
--- a/inc/Action/Exception/ActionException.php
+++ b/inc/Action/Exception/ActionException.php
@@ -24,11 +24,23 @@ class ActionException extends \Exception {
     /**
      * ActionException constructor.
      *
-     * @param string $newaction the action that should be used next
+     * When no new action is given 'show' is assumed. For requests that originated in a POST,
+     * a 'redirect' is used which will cause a redirect to the 'show' action.
+     *
+     * @param string|null $newaction the action that should be used next
      * @param string $message optional message, will not be shown except for some dub classes
      */
-    public function __construct($newaction = 'show', $message = '') {
+    public function __construct($newaction = null, $message = '') {
+        global $INPUT;
         parent::__construct($message);
+        if(is_null($newaction)) {
+            if(strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') {
+                $newaction = 'redirect';
+            } else {
+                $newaction = 'show';
+            }
+        }
+
         $this->newaction = $newaction;
     }
 
diff --git a/inc/Action/Redirect.php b/inc/Action/Redirect.php
new file mode 100644
index 000000000..0e989d1ff
--- /dev/null
+++ b/inc/Action/Redirect.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace dokuwiki\Action;
+
+use dokuwiki\Action\Exception\ActionAbort;
+
+/**
+ * Class Redirect
+ *
+ * Used to redirect to the current page with the last edited section as a target if found
+ *
+ * @package dokuwiki\Action
+ */
+class Redirect extends AbstractAliasAction {
+
+    /**
+     * Redirect to the show action, trying to jump to the previously edited section
+     *
+     * @triggers ACTION_SHOW_REDIRECT
+     * @throws ActionAbort
+     */
+    public function preProcess() {
+        global $PRE;
+        global $TEXT;
+        global $INPUT;
+        global $ID;
+        global $ACT;
+
+        $opts = array(
+            'id' => $ID,
+            'preact' => $ACT
+        );
+        //get section name when coming from section edit
+        if($INPUT->has('hid')) {
+            // Use explicitly transmitted header id
+            $opts['fragment'] = $INPUT->str('hid');
+        } else if($PRE && preg_match('/^\s*==+([^=\n]+)/', $TEXT, $match)) {
+            // Fallback to old mechanism
+            $check = false; //Byref
+            $opts['fragment'] = sectionID($match[0], $check);
+        }
+
+        // execute the redirect
+        trigger_event('ACTION_SHOW_REDIRECT', $opts, array($this, 'redirect'));
+
+        // should never be reached
+        throw new ActionAbort();
+    }
+
+    /**
+     * Execute the redirect
+     *
+     * Default action for ACTION_SHOW_REDIRECT
+     *
+     * @param array $opts id and fragment for the redirect and the preact
+     */
+    public function redirect($opts) {
+        $go = wl($opts['id'], '', true);
+        if(isset($opts['fragment'])) $go .= '#' . $opts['fragment'];
+
+        //show it
+        send_redirect($go);
+    }
+}
diff --git a/inc/Action/Revert.php b/inc/Action/Revert.php
index 559121cce..4fd9d622f 100644
--- a/inc/Action/Revert.php
+++ b/inc/Action/Revert.php
@@ -37,7 +37,6 @@ class Revert extends AbstractAction {
         global $ID;
         global $REV;
         global $lang;
-        global $INPUT;
 
         // when no revision is given, delete current one
         // FIXME this feature is not exposed in the GUI currently
@@ -63,10 +62,8 @@ class Revert extends AbstractAction {
         //session_write_close(); // FIXME sessions should be close somewhere higher up, maybe ActionRouter
 
         // when done, show current page
-        $INPUT->server->set('REQUEST_METHOD', 'post'); //should force a redirect // FIXME should we have a RedirectException?
         $REV = '';
-
-        throw new ActionAbort();
+        throw new ActionAbort('redirect');
     }
 
 }
diff --git a/inc/Action/Save.php b/inc/Action/Save.php
index dd6da567b..419108e1a 100644
--- a/inc/Action/Save.php
+++ b/inc/Action/Save.php
@@ -58,7 +58,7 @@ class Save extends AbstractAction {
         //session_write_close(); // FIXME close session higher up
 
         // when done, show page
-        throw new ActionAbort();
+        throw new ActionAbort('redirect');
     }
 
 }
diff --git a/inc/ActionRouter.php b/inc/ActionRouter.php
index 1e8f65bab..cb2df70ad 100644
--- a/inc/ActionRouter.php
+++ b/inc/ActionRouter.php
@@ -120,17 +120,13 @@ class ActionRouter {
     /**
      * Transitions from one action to another
      *
-     * Basically just calls setupAction() again but does some checks before. Also triggers
-     * redirects for POST to show transitions
+     * Basically just calls setupAction() again but does some checks before.
      *
      * @param string $from current action name
      * @param string $to new action name
      * @param null|ActionException $e any previous exception that caused the transition
      */
     protected function transitionAction($from, $to, $e = null) {
-        global $INPUT;
-        global $ID;
-
         $this->transitions++;
 
         // no infinite recursion
@@ -143,11 +139,6 @@ class ActionRouter {
             $this->handleFatalException(new FatalException('Maximum action transitions reached', 500, $e));
         }
 
-        // POST transitions to show should be a redirect
-        if($to == 'show' && strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') {
-            act_redirect($ID, $from); // FIXME we may want to move this function to the class
-        }
-
         // do the recursion
         $this->setupAction($to);
     }
diff --git a/inc/actions.php b/inc/actions.php
index 6aa29f3bf..a384d327d 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -75,49 +75,3 @@ function act_draftdel($act){
     $INFO['draft'] = null;
     return 'show';
 }
-
-/**
- * Do a redirect after receiving post data
- *
- * Tries to add the section id as hash mark after section editing
- *
- * @param string $id page id
- * @param string $preact action command before redirect
- */
-function act_redirect($id,$preact){
-    global $PRE;
-    global $TEXT;
-    global $INPUT;
-
-    $opts = array(
-            'id'       => $id,
-            'preact'   => $preact
-            );
-    //get section name when coming from section edit
-    if ($INPUT->has('hid')) {
-        // Use explicitly transmitted header id
-        $opts['fragment'] = $INPUT->str('hid');
-    } else if($PRE && preg_match('/^\s*==+([^=\n]+)/',$TEXT,$match)){
-        // Fallback to old mechanism
-        $check = false; //Byref
-        $opts['fragment'] = sectionID($match[0], $check);
-    }
-
-    trigger_event('ACTION_SHOW_REDIRECT',$opts,'act_redirect_execute');
-}
-
-/**
- * Execute the redirect
- *
- * @param array $opts id and fragment for the redirect and the preact
- */
-function act_redirect_execute($opts){
-    $go = wl($opts['id'],'',true);
-    if(isset($opts['fragment'])) $go .= '#'.$opts['fragment'];
-
-    //show it
-    send_redirect($go);
-}
-
-
-//Setup VIM: ex: et ts=2 :
-- 
GitLab