setupAction($ACT); $ACT = $this->action->getActionName(); } /** * Get the singleton instance * * @param bool $reinit * @return ActionRouter */ public function getInstance($reinit = false) { if(($this->instance === null) || $reinit) { $this->instance = new ActionRouter(); } return $this->instance; } /** * Setup the given action * * Instantiates the right class, runs permission checks and pre-processing and * sets $action * * @param string $actionname * @triggers ACTION_ACT_PREPROCESS */ protected function setupAction($actionname) { $presetup = $actionname; try { $this->action = $this->loadAction($actionname); $this->action->checkPermissions(); $this->ensureMinimumPermission($this->action->minimumPermission()); $this->action->preProcess(); } catch(ActionException $e) { // we should have gotten a new action $actionname = $e->getNewAction(); // this one should trigger a user message if(is_a($e, ActionDisabledException::class)) { msg('Action disabled: ' . hsc($presetup), -1); } // do setup for new action $this->transitionAction($presetup, $actionname); } catch(NoActionException $e) { // give plugins an opportunity to process the actionname $evt = new \Doku_Event('ACTION_ACT_PREPROCESS', $actionname); if($evt->advise_before()) { if($actionname == $presetup) { // no plugin changed the action, complain and switch to show msg('Action unknown: ' . hsc($actionname), -1); $actionname = 'show'; } $this->transitionAction($presetup, $actionname); } else { // event said the action should be kept, assume action plugin will handle it later $this->action = new Plugin(); $this->action->setActionName($actionname); } $evt->advise_after(); } catch(\Exception $e) { $this->handleFatalException($e); } } /** * Transitions from one action to another * * Basically just calls setupAction() again but does some checks before. Also triggers * redirects for POST to show transitions * * @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 if($from == $to) { $this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e)); } // larger loops will be caught here if($this->transitions >= self::MAX_TRANSITIONS) { $this->handleFatalException(new FatalException('Maximum action transitions reached', 500, $e)); } // POST transitions to show should be a redirect if($to == 'show' && $from != $to && 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); } /** * Check that the given minimum permissions are reached * * @param int $permneed * @throws ActionException */ protected function ensureMinimumPermission($permneed) { global $INFO; if($INFO['perm'] < $permneed) { throw new ActionException('denied'); } } /** * Aborts all processing with a message * * When a FataException instanc is passed, the code is treated as Status code * * @param \Exception|FatalException $e */ protected function handleFatalException(\Exception $e) { if(is_a($e, FatalException::class)) { http_status($e->getCode()); } else { http_status(500); } $msg = 'Something unforseen has happened: ' . $e->getMessage(); nice_die(hsc($msg)); } /** * Load the given action * * @param $actionname * @return AbstractAction * @throws NoActionException */ protected function loadAction($actionname) { $class = 'dokuwiki\\Action\\' . ucfirst(strtolower($actionname)); if(class_exists($class)) { return new $class; } throw new NoActionException(); } /** * Returns the action handling the current request * * @return AbstractAction */ public function getAction() { return $this->action; } }