xref: /dokuwiki/inc/ActionRouter.php (revision 64ab5140f7b1c996873fbfe9bab26d9202fbb773)
1<?php
2/**
3 * Created by IntelliJ IDEA.
4 * User: andi
5 * Date: 2/10/17
6 * Time: 3:18 PM
7 */
8
9namespace dokuwiki;
10
11use dokuwiki\Action\AbstractAction;
12use dokuwiki\Action\Exception\ActionDisabledException;
13use dokuwiki\Action\Exception\ActionException;
14use dokuwiki\Action\Exception\FatalException;
15use dokuwiki\Action\Exception\NoActionException;
16
17class ActionRouter {
18
19    /** @var  AbstractAction */
20    protected $action;
21
22    /** @var  ActionRouter */
23    protected $instance;
24
25    /**
26     * ActionRouter constructor. Singleton, thus protected!
27     */
28    protected function __construct() {
29    }
30
31    /**
32     * Get the singleton instance
33     *
34     * @param bool $reinit
35     * @return ActionRouter
36     */
37    public function getInstance($reinit = false) {
38        if(($this->instance === null) || $reinit) {
39            $this->instance = new ActionRouter();
40        }
41        return $this->instance;
42    }
43
44    /**
45     * Setup the given action
46     *
47     * Instantiates the right class, runs permission checks and pre-processing and
48     * seta $action
49     *
50     * @param string $actionname
51     * @fixme implement redirect on action change with post
52     */
53    protected function setupAction($actionname) {
54        try {
55            $this->action = $this->loadAction($actionname);
56            $this->action->checkPermissions();
57            $this->ensureMinimumPermission($this->action->minimumPermission());
58            $this->action->preProcess();
59
60        } catch(ActionException $e) {
61            // we should have gotten a new action
62            $newaction = $e->getNewAction();
63
64            // no infinite recursion
65            if($newaction === $actionname) {
66                // FIXME this doesn't catch larger circles
67                $this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e));
68            }
69
70            // this one should trigger a user message
71            if(is_a($e, ActionDisabledException::class)) {
72                msg('Action disabled: ' . hsc($actionname), -1);
73            }
74
75            // do setup for new action
76            $this->setupAction($newaction);
77
78        } catch(NoActionException $e) {
79            // FIXME here the unknown event needs to be run
80            $this->action = $this->loadAction('show');
81
82        } catch(\Exception $e) {
83            $this->handleFatalException($e);
84        }
85    }
86
87    /**
88     * Check that the given minimum permissions are reached
89     *
90     * @param int $permneed
91     * @throws ActionException
92     */
93    protected function ensureMinimumPermission($permneed) {
94        global $INFO;
95        if($INFO['perm'] < $permneed) {
96            throw new ActionException('denied');
97        }
98    }
99
100    /**
101     * Aborts all processing with a message
102     *
103     * When a FataException instanc is passed, the code is treated as Status code
104     *
105     * @param \Exception|FatalException $e
106     */
107    protected function handleFatalException(\Exception $e) {
108        if(is_a($e, FatalException::class)) {
109            http_status($e->getCode());
110        } else {
111            http_status(500);
112        }
113        $msg = 'Something unforseen has happened: ' . $e->getMessage();
114        nice_die(hsc($msg));
115    }
116
117    /**
118     * Load the given action
119     *
120     * @param $actionname
121     * @return AbstractAction
122     * @throws NoActionException
123     */
124    protected function loadAction($actionname) {
125        $class = 'dokuwiki\\Action\\' . ucfirst(strtolower($actionname));
126        if(class_exists($class)) {
127            return new $class;
128        }
129        throw new NoActionException();
130    }
131
132    /**
133     * Returns the action handling the current request
134     *
135     * @return AbstractAction
136     */
137    public function getAction() {
138        return $this->action;
139    }
140}
141