1<?php 2 3namespace dokuwiki\template\twigstarter; 4 5use BadFunctionCallException; 6use BadMethodCallException; 7use dokuwiki\Menu\MenuInterface; 8use Exception; 9use RuntimeException; 10use Twig\Environment; 11 12require_once __DIR__ . '/vendor/autoload.php'; 13 14/** 15 * Controls all functionality of the TwigStarter Template 16 */ 17class TemplateController 18{ 19 protected $view; 20 protected $twig; 21 22 /** 23 * TemplateController constructor. 24 * @param string $view The current view (main, detail, mediamanager) 25 */ 26 public function __construct($view) 27 { 28 global $conf; 29 30 // better compatibility 31 header('X-UA-Compatible: IE=edge,chrome=1'); 32 33 // what view is currently displayed? 34 $this->view = $view; 35 36 // lookup templates in the twigstarter and the actual template 37 $paths = []; 38 if (is_dir(tpl_incdir() . 'templates')) { 39 $paths[] = tpl_incdir() . 'templates'; 40 } 41 $paths[] = __DIR__ . '/templates'; 42 $loader = new TwigStarterLoader($paths); 43 $cache = $conf['cachedir'] . '/twig'; 44 io_mkdir_p($cache); 45 $this->twig = new Environment($loader, [ 46 'cache' => $conf['allowdebug'] ? false : $cache, 47 'debug' => $conf['allowdebug'], 48 'auto_reload' => true, 49 ]); 50 51 } 52 53 /** 54 * Render the template for the current view 55 * 56 * @param array $vars optional additional variables to set 57 */ 58 public function render($vars = []) 59 { 60 global $conf; 61 62 // register all globals to be available in twig 63 $data = $GLOBALS; 64 $data['_SERVER'] = $_SERVER; 65 66 // make this controller available in twig as TPL 67 $data['TPL'] = $this; 68 69 // autoregister a custom controller as a Twig variable 70 $classname = '\\dokuwiki\\template\\' . $conf['template'] . '\\CustomController'; 71 if (class_exists($classname) && is_a($classname, CustomControllerInterface::class, true)) { 72 $data['SELF'] = new $classname($this); 73 } 74 75 // add user supplied data 76 $data = array_merge($data, $vars); 77 78 // render the current view template 79 try { 80 echo $this->twig->render($this->view . '.twig', $data); 81 } catch (Exception $e) { 82 $msg = hsc($e->getMessage()); 83 if ($conf['allowdebug']) { 84 $msg .= '<pre>' . hsc($e->getTraceAsString()) . '</pre>'; 85 } 86 87 nice_die($msg); 88 } 89 } 90 91 /** 92 * Initializes and returns one of the menus 93 * 94 * @param string $type 95 * @return MenuInterface 96 */ 97 public function menu($type) 98 { 99 $class = '\\dokuwiki\\Menu\\' . ucfirst($type) . 'Menu'; 100 if (class_exists($class)) { 101 return new $class(); 102 } 103 104 throw new BadMethodCallException("No such menu $type"); 105 } 106 107 /** 108 * Initializes a new object 109 * 110 * This basically exposes the 'new' keyword to Twig. If the given class can't be found 111 * the current template's namespace is prepended and the lookup is tried again. 112 * 113 * @param string $class 114 * @param array $arguments 115 * @return Object 116 */ 117 public function newObj($class, $arguments = []) 118 { 119 global $conf; 120 if (class_exists($class)) { 121 $classname = $class; 122 } else { 123 $classname = '\\dokuwiki\\template\\' . $conf['template'] . '\\' . $class; 124 } 125 if (!class_exists($classname)) { 126 throw new RuntimeException("No such class $class"); 127 } 128 129 return new $classname(...$arguments); 130 } 131 132 /** 133 * Calls a static method on the given class 134 * 135 * This exposes any static method to Twig. If the given class can't be found 136 * the current template's namespace is prepended and the lookup is tried again. 137 * 138 * @param string $class 139 * @param string $function 140 * @param array $arguments 141 * @return mixed 142 */ 143 public function callStatic($class, $function, $arguments = []) 144 { 145 global $conf; 146 if (class_exists($class)) { 147 $classname = $class; 148 } else { 149 $classname = '\\dokuwiki\\template\\' . $conf['template'] . '\\' . $class; 150 } 151 if (!class_exists($classname)) { 152 throw new RuntimeException("No such class $class"); 153 } 154 155 if (!is_callable([$classname, $function])) { 156 throw new BadMethodCallException("No such method $class::$function"); 157 } 158 159 return call_user_func_array([$classname, $function], $arguments); 160 } 161 162 /** 163 * Return the current view as set in the constructor 164 * 165 * @return string 166 */ 167 public function getView() 168 { 169 return $this->view; 170 } 171 172 /** 173 * We make all our functions available to the template as methods of this object 174 * 175 * We always need the functions to return their data, not print it so we use output buffering to 176 * catch any possible output. 177 * 178 * @param string $name 179 * @param array $arguments 180 * @return mixed 181 */ 182 public function __call($name, $arguments) 183 { 184 if (function_exists($name)) { 185 ob_start(); 186 $return = call_user_func_array($name, $arguments); 187 $output = ob_get_clean(); 188 if ($output !== '' && $output !== false) { 189 return $output; 190 } 191 return $return; 192 } 193 194 throw new BadFunctionCallException("Function $name() does not exist"); 195 } 196 197} 198