164ab5140SAndreas Gohr<?php 264ab5140SAndreas Gohr 364ab5140SAndreas Gohrnamespace dokuwiki; 464ab5140SAndreas Gohr 564ab5140SAndreas Gohruse dokuwiki\Action\AbstractAction; 664ab5140SAndreas Gohruse dokuwiki\Action\Exception\ActionDisabledException; 764ab5140SAndreas Gohruse dokuwiki\Action\Exception\ActionException; 864ab5140SAndreas Gohruse dokuwiki\Action\Exception\FatalException; 964ab5140SAndreas Gohruse dokuwiki\Action\Exception\NoActionException; 10*a3f6fae6SAndreas Gohruse dokuwiki\Action\Plugin; 1164ab5140SAndreas Gohr 12*a3f6fae6SAndreas Gohr/** 13*a3f6fae6SAndreas Gohr * Class ActionRouter 14*a3f6fae6SAndreas Gohr * @package dokuwiki 15*a3f6fae6SAndreas Gohr */ 1664ab5140SAndreas Gohrclass ActionRouter { 1764ab5140SAndreas Gohr 1864ab5140SAndreas Gohr /** @var AbstractAction */ 1964ab5140SAndreas Gohr protected $action; 2064ab5140SAndreas Gohr 2164ab5140SAndreas Gohr /** @var ActionRouter */ 2264ab5140SAndreas Gohr protected $instance; 2364ab5140SAndreas Gohr 2464ab5140SAndreas Gohr /** 2564ab5140SAndreas Gohr * ActionRouter constructor. Singleton, thus protected! 26*a3f6fae6SAndreas Gohr * 27*a3f6fae6SAndreas Gohr * Sets up the correct action based on the $ACT global. Writes back 28*a3f6fae6SAndreas Gohr * the selected action to $ACT 2964ab5140SAndreas Gohr */ 3064ab5140SAndreas Gohr protected function __construct() { 31*a3f6fae6SAndreas Gohr global $ACT; 32*a3f6fae6SAndreas Gohr $ACT = act_clean($ACT); 33*a3f6fae6SAndreas Gohr $this->setupAction($ACT); 34*a3f6fae6SAndreas Gohr $ACT = $this->action->getActionName(); 3564ab5140SAndreas Gohr } 3664ab5140SAndreas Gohr 3764ab5140SAndreas Gohr /** 3864ab5140SAndreas Gohr * Get the singleton instance 3964ab5140SAndreas Gohr * 4064ab5140SAndreas Gohr * @param bool $reinit 4164ab5140SAndreas Gohr * @return ActionRouter 4264ab5140SAndreas Gohr */ 4364ab5140SAndreas Gohr public function getInstance($reinit = false) { 4464ab5140SAndreas Gohr if(($this->instance === null) || $reinit) { 4564ab5140SAndreas Gohr $this->instance = new ActionRouter(); 4664ab5140SAndreas Gohr } 4764ab5140SAndreas Gohr return $this->instance; 4864ab5140SAndreas Gohr } 4964ab5140SAndreas Gohr 5064ab5140SAndreas Gohr /** 5164ab5140SAndreas Gohr * Setup the given action 5264ab5140SAndreas Gohr * 5364ab5140SAndreas Gohr * Instantiates the right class, runs permission checks and pre-processing and 54*a3f6fae6SAndreas Gohr * sets $action 5564ab5140SAndreas Gohr * 5664ab5140SAndreas Gohr * @param string $actionname 57*a3f6fae6SAndreas Gohr * @triggers ACTION_ACT_PREPROCESS 5864ab5140SAndreas Gohr * @fixme implement redirect on action change with post 5964ab5140SAndreas Gohr */ 6064ab5140SAndreas Gohr protected function setupAction($actionname) { 61*a3f6fae6SAndreas Gohr $presetup = $actionname; 62*a3f6fae6SAndreas Gohr 6364ab5140SAndreas Gohr try { 6464ab5140SAndreas Gohr $this->action = $this->loadAction($actionname); 6564ab5140SAndreas Gohr $this->action->checkPermissions(); 6664ab5140SAndreas Gohr $this->ensureMinimumPermission($this->action->minimumPermission()); 6764ab5140SAndreas Gohr $this->action->preProcess(); 6864ab5140SAndreas Gohr 6964ab5140SAndreas Gohr } catch(ActionException $e) { 7064ab5140SAndreas Gohr // we should have gotten a new action 71*a3f6fae6SAndreas Gohr $actionname = $e->getNewAction(); 7264ab5140SAndreas Gohr 7364ab5140SAndreas Gohr // no infinite recursion 74*a3f6fae6SAndreas Gohr if($actionname == $presetup) { 7564ab5140SAndreas Gohr // FIXME this doesn't catch larger circles 7664ab5140SAndreas Gohr $this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e)); 7764ab5140SAndreas Gohr } 7864ab5140SAndreas Gohr 7964ab5140SAndreas Gohr // this one should trigger a user message 8064ab5140SAndreas Gohr if(is_a($e, ActionDisabledException::class)) { 81*a3f6fae6SAndreas Gohr msg('Action disabled: ' . hsc($presetup), -1); 8264ab5140SAndreas Gohr } 8364ab5140SAndreas Gohr 8464ab5140SAndreas Gohr // do setup for new action 85*a3f6fae6SAndreas Gohr $this->setupAction($actionname); 8664ab5140SAndreas Gohr 8764ab5140SAndreas Gohr } catch(NoActionException $e) { 88*a3f6fae6SAndreas Gohr // give plugins an opportunity to process the actionname 89*a3f6fae6SAndreas Gohr $evt = new \Doku_Event('ACTION_ACT_PREPROCESS', $actionname); 90*a3f6fae6SAndreas Gohr if($evt->advise_before()) { 91*a3f6fae6SAndreas Gohr if($actionname == $presetup) { 92*a3f6fae6SAndreas Gohr // no plugin changed the action, complain and switch to show 93*a3f6fae6SAndreas Gohr msg('Action unknown: ' . hsc($actionname), -1); 94*a3f6fae6SAndreas Gohr $actionname = 'show'; 95*a3f6fae6SAndreas Gohr } 96*a3f6fae6SAndreas Gohr $this->setupAction($actionname); 97*a3f6fae6SAndreas Gohr } else { 98*a3f6fae6SAndreas Gohr // event said the action should be kept, assume action plugin will handle it later 99*a3f6fae6SAndreas Gohr $this->action = new Plugin(); 100*a3f6fae6SAndreas Gohr $this->action->setActionName($actionname); 101*a3f6fae6SAndreas Gohr } 102*a3f6fae6SAndreas Gohr $evt->advise_after(); 10364ab5140SAndreas Gohr 10464ab5140SAndreas Gohr } catch(\Exception $e) { 10564ab5140SAndreas Gohr $this->handleFatalException($e); 10664ab5140SAndreas Gohr } 10764ab5140SAndreas Gohr } 10864ab5140SAndreas Gohr 10964ab5140SAndreas Gohr /** 11064ab5140SAndreas Gohr * Check that the given minimum permissions are reached 11164ab5140SAndreas Gohr * 11264ab5140SAndreas Gohr * @param int $permneed 11364ab5140SAndreas Gohr * @throws ActionException 11464ab5140SAndreas Gohr */ 11564ab5140SAndreas Gohr protected function ensureMinimumPermission($permneed) { 11664ab5140SAndreas Gohr global $INFO; 11764ab5140SAndreas Gohr if($INFO['perm'] < $permneed) { 11864ab5140SAndreas Gohr throw new ActionException('denied'); 11964ab5140SAndreas Gohr } 12064ab5140SAndreas Gohr } 12164ab5140SAndreas Gohr 12264ab5140SAndreas Gohr /** 12364ab5140SAndreas Gohr * Aborts all processing with a message 12464ab5140SAndreas Gohr * 12564ab5140SAndreas Gohr * When a FataException instanc is passed, the code is treated as Status code 12664ab5140SAndreas Gohr * 12764ab5140SAndreas Gohr * @param \Exception|FatalException $e 12864ab5140SAndreas Gohr */ 12964ab5140SAndreas Gohr protected function handleFatalException(\Exception $e) { 13064ab5140SAndreas Gohr if(is_a($e, FatalException::class)) { 13164ab5140SAndreas Gohr http_status($e->getCode()); 13264ab5140SAndreas Gohr } else { 13364ab5140SAndreas Gohr http_status(500); 13464ab5140SAndreas Gohr } 13564ab5140SAndreas Gohr $msg = 'Something unforseen has happened: ' . $e->getMessage(); 13664ab5140SAndreas Gohr nice_die(hsc($msg)); 13764ab5140SAndreas Gohr } 13864ab5140SAndreas Gohr 13964ab5140SAndreas Gohr /** 14064ab5140SAndreas Gohr * Load the given action 14164ab5140SAndreas Gohr * 14264ab5140SAndreas Gohr * @param $actionname 14364ab5140SAndreas Gohr * @return AbstractAction 14464ab5140SAndreas Gohr * @throws NoActionException 14564ab5140SAndreas Gohr */ 14664ab5140SAndreas Gohr protected function loadAction($actionname) { 14764ab5140SAndreas Gohr $class = 'dokuwiki\\Action\\' . ucfirst(strtolower($actionname)); 14864ab5140SAndreas Gohr if(class_exists($class)) { 14964ab5140SAndreas Gohr return new $class; 15064ab5140SAndreas Gohr } 15164ab5140SAndreas Gohr throw new NoActionException(); 15264ab5140SAndreas Gohr } 15364ab5140SAndreas Gohr 15464ab5140SAndreas Gohr /** 15564ab5140SAndreas Gohr * Returns the action handling the current request 15664ab5140SAndreas Gohr * 15764ab5140SAndreas Gohr * @return AbstractAction 15864ab5140SAndreas Gohr */ 15964ab5140SAndreas Gohr public function getAction() { 16064ab5140SAndreas Gohr return $this->action; 16164ab5140SAndreas Gohr } 16264ab5140SAndreas Gohr} 163