| 
							- <?php
 - 
 - namespace dokuwiki;
 - 
 - use dokuwiki\Extension\Event;
 - use dokuwiki\Action\AbstractAction;
 - use dokuwiki\Action\Exception\ActionDisabledException;
 - use dokuwiki\Action\Exception\ActionException;
 - use dokuwiki\Action\Exception\FatalException;
 - use dokuwiki\Action\Exception\NoActionException;
 - use dokuwiki\Action\Plugin;
 - 
 - /**
 -  * Class ActionRouter
 -  * @package dokuwiki
 -  */
 - class ActionRouter
 - {
 -     /** @var  AbstractAction */
 -     protected $action;
 - 
 -     /** @var  ActionRouter */
 -     protected static $instance;
 - 
 -     /** @var int transition counter */
 -     protected $transitions = 0;
 - 
 -     /** maximum loop */
 -     protected const MAX_TRANSITIONS = 5;
 - 
 -     /** @var string[] the actions disabled in the configuration */
 -     protected $disabled;
 - 
 -     /**
 -      * ActionRouter constructor. Singleton, thus protected!
 -      *
 -      * Sets up the correct action based on the $ACT global. Writes back
 -      * the selected action to $ACT
 -      */
 -     protected function __construct()
 -     {
 -         global $ACT;
 -         global $conf;
 - 
 -         $this->disabled = explode(',', $conf['disableactions']);
 -         $this->disabled = array_map('trim', $this->disabled);
 - 
 -         $ACT = act_clean($ACT);
 -         $this->setupAction($ACT);
 -         $ACT = $this->action->getActionName();
 -     }
 - 
 -     /**
 -      * Get the singleton instance
 -      *
 -      * @param bool $reinit
 -      * @return ActionRouter
 -      */
 -     public static function getInstance($reinit = false)
 -     {
 -         if ((!self::$instance instanceof \dokuwiki\ActionRouter) || $reinit) {
 -             self::$instance = new ActionRouter();
 -         }
 -         return self::$instance;
 -     }
 - 
 -     /**
 -      * Setup the given action
 -      *
 -      * Instantiates the right class, runs permission checks and pre-processing and
 -      * sets $action
 -      *
 -      * @param string $actionname this is passed as a reference to $ACT, for plugin backward compatibility
 -      * @triggers ACTION_ACT_PREPROCESS
 -      */
 -     protected function setupAction(&$actionname)
 -     {
 -         $presetup = $actionname;
 - 
 -         try {
 -             // give plugins an opportunity to process the actionname
 -             $evt = new Event('ACTION_ACT_PREPROCESS', $actionname);
 -             if ($evt->advise_before()) {
 -                 $this->action = $this->loadAction($actionname);
 -                 $this->checkAction($this->action);
 -                 $this->action->preProcess();
 -             } else {
 -                 // event said the action should be kept, assume action plugin will handle it later
 -                 $this->action = new Plugin($actionname);
 -             }
 -             $evt->advise_after();
 -         } catch (ActionException $e) {
 -             // we should have gotten a new action
 -             $actionname = $e->getNewAction();
 - 
 -             // this one should trigger a user message
 -             if ($e instanceof ActionDisabledException) {
 -                 msg('Action disabled: ' . hsc($presetup), -1);
 -             }
 - 
 -             // some actions may request the display of a message
 -             if ($e->displayToUser()) {
 -                 msg(hsc($e->getMessage()), -1);
 -             }
 - 
 -             // do setup for new action
 -             $this->transitionAction($presetup, $actionname);
 -         } catch (NoActionException $e) {
 -             msg('Action unknown: ' . hsc($actionname), -1);
 -             $actionname = 'show';
 -             $this->transitionAction($presetup, $actionname);
 -         } catch (\Exception $e) {
 -             $this->handleFatalException($e);
 -         }
 -     }
 - 
 -     /**
 -      * Transitions from one action to another
 -      *
 -      * Basically just calls setupAction() again but does some checks before.
 -      *
 -      * @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)
 -     {
 -         $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));
 -         }
 - 
 -         // do the recursion
 -         $this->setupAction($to);
 -     }
 - 
 -     /**
 -      * Aborts all processing with a message
 -      *
 -      * When a FataException instanc is passed, the code is treated as Status code
 -      *
 -      * @param \Exception|FatalException $e
 -      * @throws FatalException during unit testing
 -      */
 -     protected function handleFatalException(\Throwable $e)
 -     {
 -         if ($e instanceof FatalException) {
 -             http_status($e->getCode());
 -         } else {
 -             http_status(500);
 -         }
 -         if (defined('DOKU_UNITTEST')) {
 -             throw $e;
 -         }
 -         ErrorHandler::logException($e);
 -         $msg = 'Something unforeseen has happened: ' . $e->getMessage();
 -         nice_die(hsc($msg));
 -     }
 - 
 -     /**
 -      * Load the given action
 -      *
 -      * This translates the given name to a class name by uppercasing the first letter.
 -      * Underscores translate to camelcase names. For actions with underscores, the different
 -      * parts are removed beginning from the end until a matching class is found. The instatiated
 -      * Action will always have the full original action set as Name
 -      *
 -      * Example: 'export_raw' -> ExportRaw then 'export' -> 'Export'
 -      *
 -      * @param $actionname
 -      * @return AbstractAction
 -      * @throws NoActionException
 -      */
 -     public function loadAction($actionname)
 -     {
 -         $actionname = strtolower($actionname); // FIXME is this needed here? should we run a cleanup somewhere else?
 -         $parts = explode('_', $actionname);
 -         while ($parts !== []) {
 -             $load = implode('_', $parts);
 -             $class = 'dokuwiki\\Action\\' . str_replace('_', '', ucwords($load, '_'));
 -             if (class_exists($class)) {
 -                 return new $class($actionname);
 -             }
 -             array_pop($parts);
 -         }
 - 
 -         throw new NoActionException();
 -     }
 - 
 -     /**
 -      * Execute all the checks to see if this action can be executed
 -      *
 -      * @param AbstractAction $action
 -      * @throws ActionDisabledException
 -      * @throws ActionException
 -      */
 -     public function checkAction(AbstractAction $action)
 -     {
 -         global $INFO;
 -         global $ID;
 - 
 -         if (in_array($action->getActionName(), $this->disabled)) {
 -             throw new ActionDisabledException();
 -         }
 - 
 -         $action->checkPreconditions();
 - 
 -         if (isset($INFO)) {
 -             $perm = $INFO['perm'];
 -         } else {
 -             $perm = auth_quickaclcheck($ID);
 -         }
 - 
 -         if ($perm < $action->minimumPermission()) {
 -             throw new ActionException('denied');
 -         }
 -     }
 - 
 -     /**
 -      * Returns the action handling the current request
 -      *
 -      * @return AbstractAction
 -      */
 -     public function getAction()
 -     {
 -         return $this->action;
 -     }
 - }
 
 
  |