xref: /dokuwiki/inc/ActionRouter.php (revision f21dad3906d4ec6b3d86685599409894630abdc1)
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     * @fixme add event handling
53     * @fixme add the action name back to $ACT for plugins relying on it
54     */
55    protected function setupAction($actionname) {
56        try {
57            $this->action = $this->loadAction($actionname);
58            $this->action->checkPermissions();
59            $this->ensureMinimumPermission($this->action->minimumPermission());
60            $this->action->preProcess();
61
62        } catch(ActionException $e) {
63            // we should have gotten a new action
64            $newaction = $e->getNewAction();
65
66            // no infinite recursion
67            if($newaction === $actionname) {
68                // FIXME this doesn't catch larger circles
69                $this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e));
70            }
71
72            // this one should trigger a user message
73            if(is_a($e, ActionDisabledException::class)) {
74                msg('Action disabled: ' . hsc($actionname), -1);
75            }
76
77            // do setup for new action
78            $this->setupAction($newaction);
79
80        } catch(NoActionException $e) {
81            // FIXME here the unknown event needs to be run
82            $this->action = $this->loadAction('show');
83
84        } catch(\Exception $e) {
85            $this->handleFatalException($e);
86        }
87    }
88
89    /**
90     * Check that the given minimum permissions are reached
91     *
92     * @param int $permneed
93     * @throws ActionException
94     */
95    protected function ensureMinimumPermission($permneed) {
96        global $INFO;
97        if($INFO['perm'] < $permneed) {
98            throw new ActionException('denied');
99        }
100    }
101
102    /**
103     * Aborts all processing with a message
104     *
105     * When a FataException instanc is passed, the code is treated as Status code
106     *
107     * @param \Exception|FatalException $e
108     */
109    protected function handleFatalException(\Exception $e) {
110        if(is_a($e, FatalException::class)) {
111            http_status($e->getCode());
112        } else {
113            http_status(500);
114        }
115        $msg = 'Something unforseen has happened: ' . $e->getMessage();
116        nice_die(hsc($msg));
117    }
118
119    /**
120     * Load the given action
121     *
122     * @param $actionname
123     * @return AbstractAction
124     * @throws NoActionException
125     */
126    protected function loadAction($actionname) {
127        $class = 'dokuwiki\\Action\\' . ucfirst(strtolower($actionname));
128        if(class_exists($class)) {
129            return new $class;
130        }
131        throw new NoActionException();
132    }
133
134    /**
135     * Returns the action handling the current request
136     *
137     * @return AbstractAction
138     */
139    public function getAction() {
140        return $this->action;
141    }
142}
143