Passed
Push — master ( 5cdc85...37718d )
by Morris
38:53 queued 21:57
created

OC_Template::printExceptionErrorPage()   A

Complexity

Conditions 3
Paths 49

Size

Total Lines 39
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 32
nc 49
nop 2
dl 0
loc 39
rs 9.408
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bart Visscher <[email protected]>
6
 * @author Brice Maron <[email protected]>
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Frank Karlitschek <[email protected]>
9
 * @author Individual IT Services <[email protected]>
10
 * @author Jakob Sack <[email protected]>
11
 * @author Jan-Christoph Borchardt <[email protected]>
12
 * @author Joas Schilling <[email protected]>
13
 * @author John Molakvoæ <[email protected]>
14
 * @author Jörn Friedrich Dreyer <[email protected]>
15
 * @author Lukas Reschke <[email protected]>
16
 * @author Marin Treselj <[email protected]>
17
 * @author Michael Letzgus <[email protected]>
18
 * @author Morris Jobke <[email protected]>
19
 * @author Robin Appelman <[email protected]>
20
 * @author Roeland Jago Douma <[email protected]>
21
 * @author Thomas Müller <[email protected]>
22
 * @author Vincent Petry <[email protected]>
23
 *
24
 * @license AGPL-3.0
25
 *
26
 * This code is free software: you can redistribute it and/or modify
27
 * it under the terms of the GNU Affero General Public License, version 3,
28
 * as published by the Free Software Foundation.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
 * GNU Affero General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Affero General Public License, version 3,
36
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
37
 *
38
 */
39
40
use OC\TemplateLayout;
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
	/** @var string */
50
	private $renderAs; // Create a full page?
51
52
	/** @var string */
53
	private $path; // The path to the template
54
55
	/** @var array */
56
	private $headers = array(); //custom headers
57
58
	/** @var string */
59
	protected $app; // app id
60
61
	protected static $initTemplateEngineFirstRun = true;
62
63
	/**
64
	 * Constructor
65
	 *
66
	 * @param string $app app providing the template
67
	 * @param string $name of the template file (without suffix)
68
	 * @param string $renderAs If $renderAs is set, OC_Template will try to
69
	 *                         produce a full page in the according layout. For
70
	 *                         now, $renderAs can be set to "guest", "user" or
71
	 *                         "admin".
72
	 * @param bool $registerCall = true
73
	 */
74
	public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
75
		// Read the selected theme from the config file
76
		self::initTemplateEngine($renderAs);
77
78
		$theme = OC_Util::getTheme();
79
80
		$requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
81
82
		$parts = explode('/', $app); // fix translation when app is something like core/lostpassword
83
		$l10n = \OC::$server->getL10N($parts[0]);
84
		/** @var \OCP\Defaults $themeDefaults */
85
		$themeDefaults = \OC::$server->query(\OCP\Defaults::class);
86
87
		list($path, $template) = $this->findTemplate($theme, $app, $name);
88
89
		// Set the private data
90
		$this->renderAs = $renderAs;
91
		$this->path = $path;
92
		$this->app = $app;
93
94
		parent::__construct($template, $requestToken, $l10n, $themeDefaults);
95
	}
96
97
	/**
98
	 * @param string $renderAs
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('css-variables', null, true);
113
			OC_Util::addStyle('server', null, true);
114
			OC_Util::addStyle('jquery-ui-fixes',null,true);
115
			OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
116
			OC_Util::addVendorStyle('select2/select2', null, true);
117
			OC_Util::addStyle('jquery.ocdialog');
118
			OC_Util::addTranslations("core", null, true);
119
			OC_Util::addStyle('search', 'results');
120
			OC_Util::addScript('search', 'search', true);
121
			OC_Util::addScript('search', 'searchprovider');
122
			OC_Util::addScript('merged-template-prepend', null, true);
123
			OC_Util::addScript('jquery-ui-fixes');
124
			OC_Util::addScript('files/fileinfo');
125
			OC_Util::addScript('files/client');
126
			OC_Util::addScript('contactsmenu');
127
			OC_Util::addScript('contactsmenu_templates');
128
129
			if (\OC::$server->getConfig()->getSystemValue('debug')) {
130
				// Add the stuff we need always
131
				// following logic will import all vendor libraries that are
132
				// specified in core/js/core.json
133
				$fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
134
				if($fileContent !== false) {
135
					$coreDependencies = json_decode($fileContent, true);
136
					foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
137
						//remove trailing ".js" as addVendorScript will append it
138
						OC_Util::addVendorScript(
139
							substr($vendorLibrary, 0, -3),null,true);
140
						}
141
 				} else {
142
					throw new \Exception('Cannot read core/js/core.json');
143
				}
144
			} else {
145
				// Import all (combined) default vendor libraries
146
				OC_Util::addVendorScript('core', null, true);
147
			}
148
149
			if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
150
				// polyfill for btoa/atob for IE friends
151
				OC_Util::addVendorScript('base64/base64');
152
				// shim for the davclient.js library
153
				\OCP\Util::addScript('files/iedavclient');
154
			}
155
156
			self::$initTemplateEngineFirstRun = false;
157
		}
158
159
	}
160
161
162
	/**
163
	 * find the template with the given name
164
	 * @param string $name of the template file (without suffix)
165
	 *
166
	 * Will select the template file for the selected theme.
167
	 * Checking all the possible locations.
168
	 * @param string $theme
169
	 * @param string $app
170
	 * @return string[]
171
	 */
172
	protected function findTemplate($theme, $app, $name) {
173
		// Check if it is a app template or not.
174
		if( $app !== '' ) {
175
			$dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
176
		} else {
177
			$dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
178
		}
179
		$locator = new \OC\Template\TemplateFileLocator( $dirs );
180
		$template = $locator->find($name);
181
		$path = $locator->getPath();
182
		return array($path, $template);
183
	}
184
185
	/**
186
	 * Add a custom element to the header
187
	 * @param string $tag tag name of the element
188
	 * @param array $attributes array of attributes for the element
189
	 * @param string $text the text content for the element. If $text is null then the
190
	 * element will be written as empty element. So use "" to get a closing tag.
191
	 */
192
	public function addHeader($tag, $attributes, $text=null) {
193
		$this->headers[]= array(
194
			'tag' => $tag,
195
			'attributes' => $attributes,
196
			'text' => $text
197
		);
198
	}
199
200
	/**
201
	 * Process the template
202
	 * @return boolean|string
203
	 *
204
	 * This function process the template. If $this->renderAs is set, it
205
	 * will produce a full page.
206
	 */
207
	public function fetchPage($additionalParams = null) {
208
		$data = parent::fetchPage($additionalParams);
209
210
		if( $this->renderAs ) {
211
			$page = new TemplateLayout($this->renderAs, $this->app);
212
213
			if(is_array($additionalParams)) {
214
				foreach ($additionalParams as $key => $value) {
215
					$page->assign($key, $value);
216
				}
217
			}
218
219
			// Add custom headers
220
			$headers = '';
221
			foreach(OC_Util::$headers as $header) {
222
				$headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
0 ignored issues
show
Bug introduced by
Are you sure OCP\Util::sanitizeHTML($header['tag']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

222
				$headers .= '<'./** @scrutinizer ignore-type */ \OCP\Util::sanitizeHTML($header['tag']);
Loading history...
223
				if ( strcasecmp($header['tag'], 'script') === 0 && in_array('src', array_map('strtolower', array_keys($header['attributes']))) ) {
224
					$headers .= ' defer';
225
				}
226
				foreach($header['attributes'] as $name=>$value) {
227
					$headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
0 ignored issues
show
Bug introduced by
Are you sure OCP\Util::sanitizeHTML($value) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
					$headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'./** @scrutinizer ignore-type */ \OCP\Util::sanitizeHTML($value).'"';
Loading history...
Bug introduced by
Are you sure OCP\Util::sanitizeHTML($name) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
					$headers .= ' './** @scrutinizer ignore-type */ \OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
Loading history...
228
				}
229
				if ($header['text'] !== null) {
230
					$headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
0 ignored issues
show
Bug introduced by
Are you sure OCP\Util::sanitizeHTML($header['text']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

230
					$headers .= '>'./** @scrutinizer ignore-type */ \OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
Loading history...
231
				} else {
232
					$headers .= '/>';
233
				}
234
			}
235
236
			$page->assign('headers', $headers);
237
238
			$page->assign('content', $data);
239
			return $page->fetchPage($additionalParams);
240
		}
241
242
		return $data;
243
	}
244
245
	/**
246
	 * Include template
247
	 *
248
	 * @param string $file
249
	 * @param array|null $additionalParams
250
	 * @return string returns content of included template
251
	 *
252
	 * Includes another template. use <?php echo $this->inc('template'); ?> to
253
	 * do this.
254
	 */
255
	public function inc( $file, $additionalParams = null ) {
256
		return $this->load($this->path.$file.'.php', $additionalParams);
257
	}
258
259
	/**
260
	 * Shortcut to print a simple page for users
261
	 * @param string $application The application we render the template for
262
	 * @param string $name Name of the template
263
	 * @param array $parameters Parameters for the template
264
	 * @return boolean|null
265
	 */
266
	public static function printUserPage( $application, $name, $parameters = array() ) {
267
		$content = new OC_Template( $application, $name, "user" );
268
		foreach( $parameters as $key => $value ) {
269
			$content->assign( $key, $value );
270
		}
271
		print $content->printPage();
272
	}
273
274
	/**
275
	 * Shortcut to print a simple page for admins
276
	 * @param string $application The application we render the template for
277
	 * @param string $name Name of the template
278
	 * @param array $parameters Parameters for the template
279
	 * @return bool
280
	 */
281
	public static function printAdminPage( $application, $name, $parameters = array() ) {
282
		$content = new OC_Template( $application, $name, "admin" );
283
		foreach( $parameters as $key => $value ) {
284
			$content->assign( $key, $value );
285
		}
286
		return $content->printPage();
287
	}
288
289
	/**
290
	 * Shortcut to print a simple page for guests
291
	 * @param string $application The application we render the template for
292
	 * @param string $name Name of the template
293
	 * @param array|string $parameters Parameters for the template
294
	 * @return bool
295
	 */
296
	public static function printGuestPage( $application, $name, $parameters = array() ) {
297
		$content = new OC_Template( $application, $name, "guest" );
298
		foreach( $parameters as $key => $value ) {
299
			$content->assign( $key, $value );
300
		}
301
		return $content->printPage();
302
	}
303
304
	/**
305
	 * Print a fatal error page and terminates the script
306
	 * @param string $error_msg The error message to show
307
	 * @param string $hint An optional hint message - needs to be properly escape
308
	 * @param int $statusCode
309
	 * @suppress PhanAccessMethodInternal
310
	 */
311
	public static function printErrorPage( $error_msg, $hint = '', $statusCode = 500) {
312
		if (\OC::$server->getAppManager()->isEnabledForUser('theming') && !\OC_App::isAppLoaded('theming')) {
313
			\OC_App::loadApp('theming');
314
		}
315
316
317
		if ($error_msg === $hint) {
318
			// If the hint is the same as the message there is no need to display it twice.
319
			$hint = '';
320
		}
321
322
		http_response_code($statusCode);
323
		try {
324
			$content = new \OC_Template( '', 'error', 'error', false );
325
			$errors = array(array('error' => $error_msg, 'hint' => $hint));
326
			$content->assign( 'errors', $errors );
327
			$content->printPage();
328
		} catch (\Exception $e) {
329
			$logger = \OC::$server->getLogger();
330
			$logger->error("$error_msg $hint", ['app' => 'core']);
331
			$logger->logException($e, ['app' => 'core']);
332
333
			header('Content-Type: text/plain; charset=utf-8');
334
			print("$error_msg $hint");
335
		}
336
		die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
337
	}
338
339
	/**
340
	 * print error page using Exception details
341
	 * @param Exception|Throwable $exception
342
	 * @param int $statusCode
343
	 * @return bool|string
344
	 * @suppress PhanAccessMethodInternal
345
	 */
346
	public static function printExceptionErrorPage($exception, $statusCode = 503) {
347
		http_response_code($statusCode);
348
		try {
349
			$request = \OC::$server->getRequest();
350
			$content = new \OC_Template('', 'exception', 'error', false);
351
			$content->assign('errorClass', get_class($exception));
352
			$content->assign('errorMsg', $exception->getMessage());
353
			$content->assign('errorCode', $exception->getCode());
354
			$content->assign('file', $exception->getFile());
355
			$content->assign('line', $exception->getLine());
356
			$content->assign('trace', $exception->getTraceAsString());
357
			$content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
358
			$content->assign('remoteAddr', $request->getRemoteAddress());
359
			$content->assign('requestID', $request->getId());
360
			$content->printPage();
361
		} catch (\Exception $e) {
362
			try {
363
				$logger = \OC::$server->getLogger();
364
				$logger->logException($exception, ['app' => 'core']);
365
				$logger->logException($e, ['app' => 'core']);
366
			} catch (Throwable $e) {
367
				// no way to log it properly - but to avoid a white page of death we send some output
368
				header('Content-Type: text/plain; charset=utf-8');
369
				print("Internal Server Error\n\n");
370
				print("The server encountered an internal error and was unable to complete your request.\n");
371
				print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
372
				print("More details can be found in the server log.\n");
373
374
				// and then throw it again to log it at least to the web server error log
375
				throw $e;
376
			}
377
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();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
385
	}
386
}
387