File modules/cms/output/UIOutput.class.php

Last commit: Sat Oct 17 03:14:01 2026 +0200	Jan Dankert	stacktrace may contain HTML chars,so we have to escape them.
1 <?php 2 3 namespace cms\output; 4 5 use BadMethodCallException; 6 use cms\action\RequestParams; 7 use cms\base\Configuration;use cms\base\Language as L; 8 use cms\base\Startup;use cms\Dispatcher; 9 use cms\output\BaseOutput; 10 use Exception; 11 use template_engine\engine\TemplateRunner; 12 use util\Http; 13 use logger\Logger; 14 use LogicException; 15 use \util\exception\ObjectNotFoundException; 16 use util\exception\UIException; 17 use util\exception\SecurityException; 18 use template_engine\engine\TemplateEngine; 19 use util\Request;use util\Session;use util\text\TextMessage; 20 21 22 /** 23 * The HTML output is calling a template for the user interface. 24 */ 25 class UIOutput extends BaseOutput 26 { 27 /** 28 * Preparing the client... 29 */ 30 protected function beforeAction($request) 31 { 32 // Sending the Content-Security-Policy. 33 self::setContentSecurityPolicy(); 34 35 if ( @$_REQUEST['scope']=='openid' ) { 36 $request->redirectActionAndMethod('login','oidc'); 37 } 38 elseif (empty($request->action)) { 39 $request->redirectActionAndMethod('index','show' ); 40 } 41 42 if ( $request->isAction ) 43 throw new \RuntimeException('The HTML output driver does not accept POST requests'); 44 45 } 46 47 48 49 protected function outputData($request, $data): void 50 { 51 // The action is able to change its method and action name. 52 $subaction = $request->method; 53 $action = $request->action; 54 55 56 $this::outputTemplate($request,$action, $subaction, $data ); 57 } 58 59 60 61 /** 62 * Sets the timezone so that dates in the UI are displayed in the correct timezone. 63 */ 64 protected static function setTimezone() { 65 66 if ( $timezone = self::getCustomTimezone() ) { 67 68 date_default_timezone_set( $timezone ); 69 70 if ( DEVELOPMENT ) 71 header('X-OR-custom-timezone: '.$timezone ); 72 } 73 74 // Default: Unchanged, so /etc/timezone or /etc/localtime is used by PHP. 75 } 76 77 78 /** 79 * Gets the custom timezone of the user or the configured timezone. 80 */ 81 protected static function getCustomTimezone() { 82 83 $user = Request::getUser(); // the user timezone has precedence. 84 if ( $user && $user->timezone && in_array( $user->timezone,timezone_identifiers_list() ) ) // user is set and a timezone is set and timezone is valid 85 return( $user->timezone ); // Timezone from user setting 86 elseif ( $configuredTimezone = Configuration::subset('ui')->get('timezone') ) 87 return $configuredTimezone; // Timezone from configuration 88 89 return null; 90 } 91 92 93 /** 94 * Executes and outputs an HTML template. 95 * 96 * @param $request RequestParams 97 * @param $action string action 98 * @param $subaction string method 99 * @param $outputData array Output data 100 */ 101 private static function outputTemplate($request, $action, $subaction, $outputData) 102 { 103 $outputData += [ 104 // the following special data is used in FormComponent for filling the form. 105 '_id' => $request->id, 106 '_token' => Session::token(), 107 ]; 108 109 $templateFile = Startup::MODULE_DIR . 'cms/ui/themes/default/html/views/' . $action.'/'.$subaction . '.php'; 110 111 self::setTimezone(); 112 113 if ( DEVELOPMENT ) { 114 header('X-OR-Template: '.$templateFile ); 115 116 $outputDataClone = $outputData; 117 array_walk_recursive($outputDataClone, function(&$v) { 118 if ( ! $v ) 119 return $v; 120 $v = htmlspecialchars($v); 121 }); 122 //array_walk_recursive($outputDataClone, function(&$v) { if(is_string($v)) $v = htmlspecialchars($v); }); 123 echo "<!-- Output:\n\n".print_r($outputDataClone,true)."\n\n-->"; 124 } 125 126 $engine = new TemplateRunner(); 127 //$engine->request = $request; 128 $engine->executeTemplate( $templateFile, $outputData ); 129 } 130 131 132 /** 133 * Content-Security-Policy. 134 */ 135 private static function setContentSecurityPolicy() 136 { 137 // config is not loaded yet. Allow nothing... 138 header('Content-Security-Policy: default-src \'none\'' ); 139 140 // This will be overwritten by the index action 141 } 142 143 144 /** 145 * @param string $text 146 * @param $cause Exception the cause, or <code>null</code> if not available. 147 */ 148 protected function setError(string $text, Exception $cause):void 149 { 150 if ( !headers_sent() ) 151 // The following HTML contains inline CSS code, so we have to allow inline CSS. 152 header('Content-Security-Policy: style-src: inline; default: self'); 153 154 ?><!DOCTYPE html> 155 <html lang="en"> 156 <head> 157 <meta charset="utf-8"/> 158 <meta name="viewport" content="width=device-width, initial-scale=1"/> 159 <title><?php echo $text ?></title> 160 <style type="text/css"> 161 162 header, main { 163 display: block 164 } 165 166 body { 167 width: 100%; 168 height: 100%; 169 background-color: rgba(13,8,5,0.58); 170 color: white; 171 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 172 line-height: 1.4; 173 font-size: 1.5em; 174 text-align: center; 175 } 176 177 pre { 178 margin:10%; 179 width: 80%; 180 overflow: visible; 181 height: 40%; 182 color: silver; 183 text-align: left; 184 font-size: 0.6rem; 185 } 186 187 h1 { 188 font-size: 2em; 189 } 190 </style> 191 </head> 192 <body> 193 194 <header> 195 <h1 id="cms-error-message"><?php echo $text ?></h1> 196 </header> 197 198 <main> 199 <p>Something went terribly wrong &#x1F61E;</p> 200 201 <pre id="cms-error-log"><?php // Display exceptions only in development mode, because they may contain sensitive internal information like passwords. 202 if ($cause) { 203 if (defined('DEVELOPMENT') && DEVELOPMENT ) { 204 echo htmlentities($cause->__toString()); 205 } 206 else { 207 while (true) { 208 echo($cause->getMessage()); 209 $previous = $cause->getPrevious(); 210 if ($previous) { 211 echo "\n\n" . 'Caused by:'."\n"; 212 $cause = $previous; 213 } else 214 break; 215 } 216 } 217 } 218 ?></pre> 219 </main> 220 221 </body> 222 </html><?php 223 224 } 225 226 public function getContentType(): string|null 227 { 228 return 'text/html'; 229 } 230 }
Download modules/cms/output/UIOutput.class.php
History Sat, 17 Oct 2026 03:14:01 +0200 Jan Dankert stacktrace may contain HTML chars,so we have to escape them. Sat, 17 Oct 2026 01:14:30 +0200 Jan Dankert New: Using a structured class for the API-Response. The output for GET-Requests is now without the "output" layer. Tue, 13 Oct 2026 13:38:43 +0200 Jan Dankert PHP8: Some fixes for PHP8. Tue, 14 Jan 2025 01:27:20 +0100 Jan Dankert Fix: Debug output should be human readable. Mon, 13 Feb 2023 22:46:58 +0100 Jan Dankert Show server error messages in the UI notice window. Sat, 28 Jan 2023 19:10:47 +0100 Jan Dankert New: Templates may be rendered as Mustache, Script and Script template. Fri, 15 Apr 2022 14:51:22 +0200 dankert Refactoring: User,Config and Database info is now stored in the Request, because so there is no session required for clients which are using Basic Authorization. Fri, 18 Mar 2022 00:30:30 +0100 dankert Fix: Test if stored timezone value is valid in the actual server environment. Tue, 15 Mar 2022 20:00:51 +0100 dankert Deleted commented code. Fri, 11 Mar 2022 19:33:58 +0100 dankert Better solution to escape the output data. Fri, 11 Mar 2022 19:25:58 +0100 dankert Fix: HTML in Comments must be escaped. Wed, 9 Mar 2022 01:57:45 +0100 dankert Fix: Do not write the language to a cookie. Wed, 9 Mar 2022 00:53:10 +0100 dankert Fix: Setting the correct timezone from the user property. Mon, 14 Feb 2022 00:06:35 +0100 dankert Documentation... Mon, 14 Feb 2022 00:01:10 +0100 dankert Fix: UI needs output data "_token" and "_id". Sun, 13 Feb 2022 19:39:49 +0100 dankert Refactoring: Special output type "preview" for previewing pages and files.