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