Completed
Push — master ( dfe944...0dae49 )
by Morris
12:59
created

OC_Template::printErrorPage()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 18
nc 16
nop 2
dl 0
loc 27
rs 8.439
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 Frank Karlitschek <[email protected]>
8
 * @author Hendrik Leppelsack <[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 Jörn Friedrich Dreyer <[email protected]>
14
 * @author Lukas Reschke <[email protected]>
15
 * @author Michael Letzgus <[email protected]>
16
 * @author Morris Jobke <[email protected]>
17
 * @author Raghu Nayyar <[email protected]>
18
 * @author Robin Appelman <[email protected]>
19
 * @author Roeland Jago Douma <[email protected]>
20
 * @author Thomas Müller <[email protected]>
21
 * @author Vincent Petry <[email protected]>
22
 *
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
41
require_once __DIR__.'/template/functions.php';
42
43
/**
44
 * This class provides the templates for ownCloud.
45
 */
46
class OC_Template extends \OC\Template\Base {
47
48
	/** @var string */
49
	private $renderAs; // Create a full page?
50
51
	/** @var string */
52
	private $path; // The path to the template
53
54
	/** @var array */
55
	private $headers = array(); //custom headers
56
57
	/** @var string */
58
	protected $app; // app id
59
60
	protected static $initTemplateEngineFirstRun = true;
61
62
	/**
63
	 * Constructor
64
	 *
65
	 * @param string $app app providing the template
66
	 * @param string $name of the template file (without suffix)
67
	 * @param string $renderAs If $renderAs is set, OC_Template will try to
68
	 *                         produce a full page in the according layout. For
69
	 *                         now, $renderAs can be set to "guest", "user" or
70
	 *                         "admin".
71
	 * @param bool $registerCall = true
72
	 */
73
	public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
74
		// Read the selected theme from the config file
75
		self::initTemplateEngine($renderAs);
76
77
		$theme = OC_Util::getTheme();
78
79
		$requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
80
81
		$parts = explode('/', $app); // fix translation when app is something like core/lostpassword
82
		$l10n = \OC::$server->getL10N($parts[0]);
83
		/** @var \OCP\Defaults $themeDefaults */
84
		$themeDefaults = \OC::$server->query(\OCP\Defaults::class);
85
86
		list($path, $template) = $this->findTemplate($theme, $app, $name);
87
88
		// Set the private data
89
		$this->renderAs = $renderAs;
90
		$this->path = $path;
91
		$this->app = $app;
92
93
		parent::__construct($template, $requestToken, $l10n, $themeDefaults);
94
	}
95
96
	/**
97
	 * @param string $renderAs
98
	 */
99
	public static function initTemplateEngine($renderAs) {
100
		if (self::$initTemplateEngineFirstRun){
101
102
			//apps that started before the template initialization can load their own scripts/styles
103
			//so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true
104
			//meaning the last script/style in this list will be loaded first
105
			if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) {
106
				if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') {
107
					OC_Util::addScript ( 'backgroundjobs', null, true );
108
				}
109
			}
110
111
			OC_Util::addStyle('jquery-ui-fixes',null,true);
112
			OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
113
			OC_Util::addStyle('server', null, true);
114
			OC_Util::addVendorStyle('select2/select2', null, true);
115
			OC_Util::addStyle('jquery.ocdialog');
116
			OC_Util::addTranslations("core", null, true);
117
			OC_Util::addScript('search', 'search', true);
118
			OC_Util::addScript('merged-template-prepend', null, true);
119
			OC_Util::addScript('jquery-ui-fixes');
120
			OC_Util::addScript('files/fileinfo');
121
			OC_Util::addScript('files/client');
122
			OC_Util::addScript('contactsmenu');
123
124
			if (\OC::$server->getConfig()->getSystemValue('debug')) {
125
				// Add the stuff we need always
126
				// following logic will import all vendor libraries that are
127
				// specified in core/js/core.json
128
				$fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
129
				if($fileContent !== false) {
130
					$coreDependencies = json_decode($fileContent, true);
131
					foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
132
						//remove trailing ".js" as addVendorScript will append it
133
						OC_Util::addVendorScript(
134
							substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true);
135
						}
136
 				} else {
137
					throw new \Exception('Cannot read core/js/core.json');
138
				}
139
			} else {
140
				// Import all (combined) default vendor libraries
141
				OC_Util::addVendorScript('core', null, true);
142
			}
143
144
			if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
145
				// polyfill for btoa/atob for IE friends
146
				OC_Util::addVendorScript('base64/base64');
147
				// shim for the davclient.js library
148
				\OCP\Util::addScript('files/iedavclient');
149
			}
150
151
			self::$initTemplateEngineFirstRun = false;
152
		}
153
154
	}
155
156
157
	/**
158
	 * find the template with the given name
159
	 * @param string $name of the template file (without suffix)
160
	 *
161
	 * Will select the template file for the selected theme.
162
	 * Checking all the possible locations.
163
	 * @param string $theme
164
	 * @param string $app
165
	 * @return string[]
166
	 */
167
	protected function findTemplate($theme, $app, $name) {
168
		// Check if it is a app template or not.
169
		if( $app !== '' ) {
170
			$dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
171
		} else {
172
			$dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
173
		}
174
		$locator = new \OC\Template\TemplateFileLocator( $dirs );
175
		$template = $locator->find($name);
176
		$path = $locator->getPath();
177
		return array($path, $template);
178
	}
179
180
	/**
181
	 * Add a custom element to the header
182
	 * @param string $tag tag name of the element
183
	 * @param array $attributes array of attributes for the element
184
	 * @param string $text the text content for the element. If $text is null then the
185
	 * element will be written as empty element. So use "" to get a closing tag.
186
	 */
187 View Code Duplication
	public function addHeader($tag, $attributes, $text=null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
		$this->headers[]= array(
189
			'tag' => $tag,
190
			'attributes' => $attributes,
191
			'text' => $text
192
		);
193
	}
194
195
	/**
196
	 * Process the template
197
	 * @return boolean|string
198
	 *
199
	 * This function process the template. If $this->renderAs is set, it
200
	 * will produce a full page.
201
	 */
202
	public function fetchPage($additionalParams = null) {
203
		$data = parent::fetchPage($additionalParams);
204
205
		if( $this->renderAs ) {
206
			$page = new TemplateLayout($this->renderAs, $this->app);
207
208
			// Add custom headers
209
			$headers = '';
210
			foreach(OC_Util::$headers as $header) {
211
				$headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
212
				if ( strcasecmp($header['tag'], 'script') === 0 && in_array('src', array_map('strtolower', array_keys($header['attributes']))) ) {
213
					$headers .= ' defer';
214
				}
215
				foreach($header['attributes'] as $name=>$value) {
216
					$headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
217
				}
218
				if ($header['text'] !== null) {
219
					$headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
220
				} else {
221
					$headers .= '/>';
222
				}
223
			}
224
225
			$page->assign('headers', $headers);
226
227
			$page->assign('content', $data);
228
			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 228 which is incompatible with the return type of the parent method OC\Template\Base::fetchPage of type string.
Loading history...
229
		}
230
231
		return $data;
232
	}
233
234
	/**
235
	 * Include template
236
	 *
237
	 * @param string $file
238
	 * @param array|null $additionalParams
239
	 * @return string returns content of included template
240
	 *
241
	 * Includes another template. use <?php echo $this->inc('template'); ?> to
242
	 * do this.
243
	 */
244
	public function inc( $file, $additionalParams = null ) {
245
		return $this->load($this->path.$file.'.php', $additionalParams);
246
	}
247
248
	/**
249
	 * Shortcut to print a simple page for users
250
	 * @param string $application The application we render the template for
251
	 * @param string $name Name of the template
252
	 * @param array $parameters Parameters for the template
253
	 * @return boolean|null
254
	 */
255 View Code Duplication
	public static function printUserPage( $application, $name, $parameters = array() ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
		$content = new OC_Template( $application, $name, "user" );
257
		foreach( $parameters as $key => $value ) {
258
			$content->assign( $key, $value );
259
		}
260
		print $content->printPage();
261
	}
262
263
	/**
264
	 * Shortcut to print a simple page for admins
265
	 * @param string $application The application we render the template for
266
	 * @param string $name Name of the template
267
	 * @param array $parameters Parameters for the template
268
	 * @return bool
269
	 */
270 View Code Duplication
	public static function printAdminPage( $application, $name, $parameters = array() ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
		$content = new OC_Template( $application, $name, "admin" );
272
		foreach( $parameters as $key => $value ) {
273
			$content->assign( $key, $value );
274
		}
275
		return $content->printPage();
276
	}
277
278
	/**
279
	 * Shortcut to print a simple page for guests
280
	 * @param string $application The application we render the template for
281
	 * @param string $name Name of the template
282
	 * @param array|string $parameters Parameters for the template
283
	 * @return bool
284
	 */
285 View Code Duplication
	public static function printGuestPage( $application, $name, $parameters = array() ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
		$content = new OC_Template( $application, $name, "guest" );
287
		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...
288
			$content->assign( $key, $value );
289
		}
290
		return $content->printPage();
291
	}
292
293
	/**
294
		* Print a fatal error page and terminates the script
295
		* @param string $error_msg The error message to show
296
		* @param string $hint An optional hint message - needs to be properly escaped
297
		*/
298
	public static function printErrorPage( $error_msg, $hint = '' ) {
299
		if (\OC_App::isEnabled('theming') && !\OC_App::isAppLoaded('theming')) {
300
			\OC_App::loadApp('theming');
301
		}
302
303
304
		if ($error_msg === $hint) {
305
			// If the hint is the same as the message there is no need to display it twice.
306
			$hint = '';
307
		}
308
309
		try {
310
			$content = new \OC_Template( '', 'error', 'error', false );
311
			$errors = array(array('error' => $error_msg, 'hint' => $hint));
312
			$content->assign( 'errors', $errors );
313
			$content->printPage();
314
		} catch (\Exception $e) {
315
			$logger = \OC::$server->getLogger();
316
			$logger->error("$error_msg $hint", ['app' => 'core']);
317
			$logger->logException($e, ['app' => 'core']);
318
319
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
320
			header('Content-Type: text/plain; charset=utf-8');
321
			print("$error_msg $hint");
322
		}
323
		die();
324
	}
325
326
	/**
327
	 * print error page using Exception details
328
	 * @param Exception | Throwable $exception
329
	 */
330
	public static function printExceptionErrorPage($exception, $fetchPage = false) {
331
		try {
332
			$request = \OC::$server->getRequest();
333
			$content = new \OC_Template('', 'exception', 'error', false);
334
			$content->assign('errorClass', get_class($exception));
335
			$content->assign('errorMsg', $exception->getMessage());
336
			$content->assign('errorCode', $exception->getCode());
337
			$content->assign('file', $exception->getFile());
338
			$content->assign('line', $exception->getLine());
339
			$content->assign('trace', $exception->getTraceAsString());
340
			$content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
341
			$content->assign('remoteAddr', $request->getRemoteAddress());
342
			$content->assign('requestID', $request->getId());
343
			if ($fetchPage) {
344
				return $content->fetchPage();
345
			}
346
			$content->printPage();
347
		} catch (\Exception $e) {
348
			$logger = \OC::$server->getLogger();
349
			$logger->logException($exception, ['app' => 'core']);
350
			$logger->logException($e, ['app' => 'core']);
351
352
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
353
			header('Content-Type: text/plain; charset=utf-8');
354
			print("Internal Server Error\n\n");
355
			print("The server encountered an internal error and was unable to complete your request.\n");
356
			print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
357
			print("More details can be found in the server log.\n");
358
		}
359
		die();
360
	}
361
362
	/**
363
	 * This is only here to reduce the dependencies in case of an exception to
364
	 * still be able to print a plain error message.
365
	 *
366
	 * Returns the used HTTP protocol.
367
	 *
368
	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
369
	 * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead
370
	 */
371 View Code Duplication
	protected static function getHttpProtocol() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
		$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
373
		$validProtocols = [
374
			'HTTP/1.0',
375
			'HTTP/1.1',
376
			'HTTP/2',
377
		];
378
		if(in_array($claimedProtocol, $validProtocols, true)) {
379
			return $claimedProtocol;
380
		}
381
		return 'HTTP/1.1';
382
	}
383
}
384