Completed
Pull Request — master (#28401)
by Pauli
12:29
created

OC_Template::printErrorPage()   B

Complexity

Conditions 4
Paths 16

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 18
nc 16
nop 3
dl 0
loc 25
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Adam Williamson <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Brice Maron <[email protected]>
6
 * @author Frank Karlitschek <[email protected]>
7
 * @author Hendrik Leppelsack <[email protected]>
8
 * @author Individual IT Services <[email protected]>
9
 * @author Jakob Sack <[email protected]>
10
 * @author Jan-Christoph Borchardt <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Jörn Friedrich Dreyer <[email protected]>
13
 * @author Lukas Reschke <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author phisch <[email protected]>
16
 * @author Raghu Nayyar <[email protected]>
17
 * @author Robin Appelman <[email protected]>
18
 * @author Roeland Jago Douma <[email protected]>
19
 * @author Thomas Müller <[email protected]>
20
 * @author Vincent Petry <[email protected]>
21
 *
22
 * @copyright Copyright (c) 2017, ownCloud GmbH
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
39
use OC\TemplateLayout;
40
use OC\Theme\Theme;
41
42
require_once __DIR__.'/template/functions.php';
43
44
/**
45
 * This class provides the templates for ownCloud.
46
 */
47
class OC_Template extends \OC\Template\Base {
48
49
	/**
50
	 * @var string
51
	 */
52
	private $renderAs;
53
54
	/**
55
	 * @var array
56
	 */
57
	private $headers = [];
58
59
	/**
60
	 * @var string
61
	 */
62
	protected $app;
63
64
	/**
65
	 * @var bool
66
	 */
67
	protected static $initTemplateEngineFirstRun = true;
68
69
	/**
70
	 * Constructor
71
	 *
72
	 * @param string $app app providing the template
73
	 * @param string $name of the template file (without suffix)
74
	 * @param string $renderAs If $renderAs is set, OC_Template will try to
75
	 *                         produce a full page in the according layout. For
76
	 *                         now, $renderAs can be set to "guest", "user" or
77
	 *                         "admin".
78
	 * @param bool $registerCall = true
79
	 */
80
	public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
81
		self::initTemplateEngine($renderAs);
82
		$requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
83
84
		$parts = explode('/', $app); // fix translation when app is something like core/lostpassword
85
		$l10n = \OC::$server->getL10N($parts[0]);
86
87
		$theme = OC_Util::getTheme();
88
		$template = $this->findTemplate($theme, $app, $name);
89
90
		$this->renderAs = $renderAs;
91
		$this->app = $app;
92
93
		parent::__construct($template, $requestToken, $l10n, $theme, new OC_Defaults());
94
	}
95
96
	/**
97
	 * @param string $renderAs
98
	 * @throws Exception
99
	 */
100
	public static function initTemplateEngine($renderAs) {
101
		if (self::$initTemplateEngineFirstRun){
102
103
			//apps that started before the template initialization can load their own scripts/styles
104
			//so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true
105
			//meaning the last script/style in this list will be loaded first
106
			if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) {
107
				if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') {
108
					OC_Util::addScript ( 'backgroundjobs', null, true );
109
				}
110
			}
111
112
			OC_Util::addStyle("tooltip",null,true);
113
			OC_Util::addStyle('jquery-ui-fixes',null,true);
114
			OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
115
			OC_Util::addStyle("mobile",null,true);
116
			OC_Util::addStyle("multiselect",null,true);
117
			OC_Util::addStyle("fixes",null,true);
118
			OC_Util::addStyle("global",null,true);
119
			OC_Util::addStyle("apps",null,true);
120
			OC_Util::addStyle("fonts",null,true);
121
			OC_Util::addStyle("icons",null,true);
122
			OC_Util::addStyle("header",null,true);
123
			OC_Util::addStyle("inputs",null,true);
124
			OC_Util::addStyle("styles",null,true);
125
126
			// avatars
127
			if (\OC::$server->getSystemConfig()->getValue('enable_avatars', true) === true) {
128
				\OC_Util::addScript('jquery.avatar', null, true);
129
				\OC_Util::addScript('placeholder', null, true);
130
			}
131
132
			OC_Util::addScript('oc-backbone', null, true);
133
			OC_Util::addVendorScript('core', 'backbone/backbone', true);
134
			OC_Util::addVendorScript('snapjs/dist/latest/snap', null, true);
135
			OC_Util::addScript('mimetypelist', null, true);
136
			OC_Util::addScript('mimetype', null, true);
137
			OC_Util::addScript("apps", null, true);
138
			OC_Util::addScript("oc-requesttoken", null, true);
139
			OC_Util::addScript('search', 'search', true);
140
			OC_Util::addScript("config", null, true);
141
			OC_Util::addScript("eventsource", null, true);
142
			OC_Util::addScript("octemplate", null, true);
143
			OC_Util::addTranslations("core", null, true);
144
			OC_Util::addScript("l10n", null, true);
145
			OC_Util::addScript("js", null, true);
146
			OC_Util::addScript("oc-dialogs", null, true);
147
			OC_Util::addScript("jquery.ocdialog", null, true);
148
			OC_Util::addStyle("jquery.ocdialog");
149
			OC_Util::addScript('files/fileinfo');
150
			OC_Util::addScript('files/client');
151
152
			// Add the stuff we need always
153
			// following logic will import all vendor libraries that are
154
			// specified in core/js/core.json
155
			$fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
156
			if($fileContent !== false) {
157
				$coreDependencies = json_decode($fileContent, true);
158
				foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
159
					// remove trailing ".js" as addVendorScript will append it
160
					OC_Util::addVendorScript(
161
							substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true);
162
				}
163
			} else {
164
				throw new \Exception('Cannot read core/js/core.json');
165
			}
166
167
			if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
168
				// polyfill for btoa/atob for IE friends
169
				OC_Util::addVendorScript('base64/base64');
170
				// shim for the davclient.js library
171
				\OCP\Util::addScript('files/iedavclient');
172
			}
173
174
			self::$initTemplateEngineFirstRun = false;
175
		}
176
177
	}
178
179
180
	/**
181
	 * find the template with the given name
182
	 * @param string $name of the template file (without suffix)
183
	 *
184
	 * Will select the template file for the selected theme.
185
	 * Checking all the possible locations.
186
	 * @param Theme $theme
187
	 * @param string $app
188
	 * @return string
189
	 */
190
	protected function findTemplate($theme, $app, $name) {
191
		// Check if it is a app template or not.
192
		if( $app !== '' && $app !== 'core' ) {
193
			$dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
194
		} else {
195
			$dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
196
		}
197
198
		$locator = new \OC\Template\TemplateFileLocator( $dirs );
199
		$template = $locator->find($name);
200
201
		return $template;
202
	}
203
204
	/**
205
	 * Add a custom element to the header
206
	 * @param string $tag tag name of the element
207
	 * @param array $attributes array of attributes for the element
208
	 * @param string $text the text content for the element. If $text is null then the
209
	 * element will be written as empty element. So use "" to get a closing tag.
210
	 */
211 View Code Duplication
	public function addHeader($tag, $attributes, $text=null) {
212
		$this->headers[]= [
213
			'tag' => $tag,
214
			'attributes' => $attributes,
215
			'text' => $text
216
		];
217
	}
218
219
	/**
220
	 * Process the template
221
	 *
222
	 * @param array|null $additionalParams
223
	 * @return bool|string This function process the template. If $this->renderAs is set, it
224
	 *
225
	 * This function process the template. If $this->renderAs is set, it
226
	 * will produce a full page.
227
	 */
228
	public function fetchPage($additionalParams = null) {
229
		$data = parent::fetchPage($additionalParams);
230
231
		if( $this->renderAs ) {
232
			$page = new TemplateLayout($this->renderAs, $this->app);
233
234
			// Add custom headers
235
			$headers = '';
236
			foreach(OC_Util::$headers as $header) {
237
				$headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
238
				foreach($header['attributes'] as $name=>$value) {
239
					$headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
240
				}
241
				if ($header['text'] !== null) {
242
					$headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
243
				} else {
244
					$headers .= '/>';
245
				}
246
			}
247
248
			$page->assign('headers', $headers);
249
250
			$page->assign('content', $data);
251
			return $page->fetchPage();
0 ignored issues
show
Bug Compatibility introduced by
The expression $page->fetchPage(); of type boolean|string adds the type boolean to the return on line 251 which is incompatible with the return type of the parent method OC\Template\Base::fetchPage of type string.
Loading history...
252
		}
253
254
		return $data;
255
	}
256
257
	/**
258
	 * Include template
259
	 *
260
	 * @param string $file
261
	 * @param array|null $additionalParams
262
	 * @return string returns content of included template
263
	 *
264
	 * Includes another template. use <?php echo $this->inc('template'); ?> to
265
	 * do this.
266
	 */
267
	public function inc($file, $additionalParams = null) {
268
		$template = $this->findTemplate($this->theme, $this->app, $file);
269
		return $this->load($template, $additionalParams);
270
	}
271
272
	/**
273
	 * Shortcut to print a simple page for users
274
	 * @param string $application The application we render the template for
275
	 * @param string $name Name of the template
276
	 * @param array $parameters Parameters for the template
277
	 * @return boolean|null
278
	 */
279 View Code Duplication
	public static function printUserPage( $application, $name, $parameters = []) {
280
		$content = new OC_Template( $application, $name, "user" );
281
		foreach( $parameters as $key => $value ) {
282
			$content->assign( $key, $value );
283
		}
284
		print $content->printPage();
285
	}
286
287
	/**
288
	 * Shortcut to print a simple page for admins
289
	 * @param string $application The application we render the template for
290
	 * @param string $name Name of the template
291
	 * @param array $parameters Parameters for the template
292
	 * @return bool
293
	 */
294 View Code Duplication
	public static function printAdminPage( $application, $name, $parameters = []) {
295
		$content = new OC_Template( $application, $name, "admin" );
296
		foreach( $parameters as $key => $value ) {
297
			$content->assign( $key, $value );
298
		}
299
		return $content->printPage();
300
	}
301
302
	/**
303
	 * Shortcut to print a simple page for guests
304
	 * @param string $application The application we render the template for
305
	 * @param string $name Name of the template
306
	 * @param array|string $parameters Parameters for the template
307
	 * @return bool
308
	 */
309 View Code Duplication
	public static function printGuestPage( $application, $name, $parameters = []) {
310
		$content = new OC_Template( $application, $name, "guest" );
311
		foreach( $parameters as $key => $value ) {
0 ignored issues
show
Bug introduced by
The expression $parameters of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
312
			$content->assign( $key, $value );
313
		}
314
		return $content->printPage();
315
	}
316
317
	/**
318
		* Print a fatal error page and terminates the script
319
		* @param string $error_msg The error message to show
320
		* @param string $hint An optional hint message - needs to be properly escaped
321
		* @param int HTTP Status Code
322
		*/
323
	public static function printErrorPage( $error_msg, $hint = '', $httpStatusCode = null ) {
324
		if ($error_msg === $hint) {
325
			// If the hint is the same as the message there is no need to display it twice.
326
			$hint = '';
327
		}
328
329
		try {
330
			$content = new \OC_Template( '', 'error', 'error', false );
331
			$errors = [['error' => \OCP\Util::sanitizeHTML($error_msg), 'hint' => \OCP\Util::sanitizeHTML($hint)]];
332
			$content->assign( 'errors', $errors );
333
			if ($httpStatusCode !== null) {
334
				http_response_code((int)$httpStatusCode);
335
			}
336
			$content->printPage();
337
		} catch (\Exception $e) {
338
			$logger = \OC::$server->getLogger();
339
			$logger->error("$error_msg $hint", ['app' => 'core']);
340
			$logger->logException($e, ['app' => 'core']);
341
342
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
343
			header('Content-Type: text/plain; charset=utf-8');
344
			print("$error_msg $hint");
345
		}
346
		die();
347
	}
348
349
	/**
350
	 * print error page using Exception details
351
	 * @param Exception | Throwable $exception
352
	 * @param bool $fetchPage
353
	 * @return bool|string
354
	 */
355
	public static function printExceptionErrorPage($exception, $fetchPage = false) {
356
		try {
357
			$request = \OC::$server->getRequest();
358
			$content = new \OC_Template('', 'exception', 'error', false);
359
			$content->assign('errorClass', get_class($exception));
360
			$content->assign('errorMsg', $exception->getMessage());
361
			$content->assign('errorCode', $exception->getCode());
362
			$content->assign('file', $exception->getFile());
363
			$content->assign('line', $exception->getLine());
364
			$content->assign('trace', $exception->getTraceAsString());
365
			$content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
366
			$content->assign('remoteAddr', $request->getRemoteAddress());
367
			$content->assign('requestID', $request->getId());
368
			if ($fetchPage) {
369
				return $content->fetchPage();
370
			}
371
			$content->printPage();
372
		} catch (\Exception $e) {
373
			$logger = \OC::$server->getLogger();
374
			$logger->logException($exception, ['app' => 'core']);
375
			$logger->logException($e, ['app' => 'core']);
376
377
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
378
			header('Content-Type: text/plain; charset=utf-8');
379
			print("Internal Server Error\n\n");
380
			print("The server encountered an internal error and was unable to complete your request.\n");
381
			print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
382
			print("More details can be found in the server log.\n");
383
		}
384
		die();
385
	}
386
387
	/**
388
	 * This is only here to reduce the dependencies in case of an exception to
389
	 * still be able to print a plain error message.
390
	 *
391
	 * Returns the used HTTP protocol.
392
	 *
393
	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
394
	 * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead
395
	 */
396 View Code Duplication
	protected static function getHttpProtocol() {
397
		$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
398
		$validProtocols = [
399
			'HTTP/1.0',
400
			'HTTP/1.1',
401
			'HTTP/2',
402
		];
403
		if(in_array($claimedProtocol, $validProtocols, true)) {
404
			return $claimedProtocol;
405
		}
406
		return 'HTTP/1.1';
407
	}
408
}
409