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