Completed
Push — master ( f94bc6...e5f967 )
by Lukas
13:39
created

OC_Template::__construct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 4
dl 0
loc 21
rs 9.3142
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 Morris Jobke <[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
 * @license AGPL-3.0
23
 *
24
 * This code is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License, version 3,
26
 * as published by the Free Software Foundation.
27
 *
28
 * This program is distributed in the hope that it will be useful,
29
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
 * GNU Affero General Public License for more details.
32
 *
33
 * You should have received a copy of the GNU Affero General Public License, version 3,
34
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
35
 *
36
 */
37
38
use OC\TemplateLayout;
39
40
require_once __DIR__.'/template/functions.php';
41
42
/**
43
 * This class provides the templates for ownCloud.
44
 */
45
class OC_Template extends \OC\Template\Base {
46
47
	/** @var string */
48
	private $renderAs; // Create a full page?
49
50
	/** @var string */
51
	private $path; // The path to the template
52
53
	/** @var array */
54
	private $headers = array(); //custom headers
55
56
	/** @var string */
57
	protected $app; // app id
58
59
	protected static $initTemplateEngineFirstRun = true;
60
61
	/**
62
	 * Constructor
63
	 *
64
	 * @param string $app app providing the template
65
	 * @param string $name of the template file (without suffix)
66
	 * @param string $renderAs If $renderAs is set, OC_Template will try to
67
	 *                         produce a full page in the according layout. For
68
	 *                         now, $renderAs can be set to "guest", "user" or
69
	 *                         "admin".
70
	 * @param bool $registerCall = true
71
	 */
72
	public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
73
		// Read the selected theme from the config file
74
		self::initTemplateEngine($renderAs);
75
76
		$theme = OC_Util::getTheme();
77
78
		$requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
79
80
		$parts = explode('/', $app); // fix translation when app is something like core/lostpassword
81
		$l10n = \OC::$server->getL10N($parts[0]);
82
		$themeDefaults = \OC::$server->getThemingDefaults();
83
84
		list($path, $template) = $this->findTemplate($theme, $app, $name);
85
86
		// Set the private data
87
		$this->renderAs = $renderAs;
88
		$this->path = $path;
89
		$this->app = $app;
90
91
		parent::__construct($template, $requestToken, $l10n, $themeDefaults);
92
	}
93
94
	/**
95
	 * @param string $renderAs
96
	 */
97
	public static function initTemplateEngine($renderAs) {
98
		if (self::$initTemplateEngineFirstRun){
99
100
			//apps that started before the template initialization can load their own scripts/styles
101
			//so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true
102
			//meaning the last script/style in this list will be loaded first
103
			if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) {
104
				if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') {
105
					OC_Util::addScript ( 'backgroundjobs', null, true );
106
				}
107
			}
108
109
			OC_Util::addStyle('jquery-ui-fixes',null,true);
110
			OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
111
			OC_Util::addStyle('server', null, true);
112
113
			// avatars
114
			\OC_Util::addScript('jquery.avatar', null, true);
115
			\OC_Util::addScript('placeholder', null, true);
116
117
			OC_Util::addVendorStyle('select2/select2', null, true);
118
			OC_Util::addScript('select2-toggleselect');
119
120
			OC_Util::addScript('oc-backbone', null, true);
121
			OC_Util::addScript('mimetypelist', null, true);
122
			OC_Util::addScript('mimetype', null, true);
123
			OC_Util::addScript("apps", null, true);
124
			OC_Util::addScript("oc-requesttoken", null, true);
125
			OC_Util::addScript('search', 'search', true);
126
			OC_Util::addScript("config", null, true);
127
			OC_Util::addScript("public/appconfig", null, true);
128
			OC_Util::addScript("eventsource", null, true);
129
			OC_Util::addScript("octemplate", null, true);
130
			OC_Util::addTranslations("core", null, true);
131
			OC_Util::addScript("l10n", null, true);
132
			OC_Util::addScript("js", null, true);
133
			OC_Util::addScript("oc-dialogs", null, true);
134
			OC_Util::addScript("jquery.ocdialog", null, true);
135
			OC_Util::addScript("jquery-ui-fixes");
136
			OC_Util::addStyle("jquery.ocdialog");
137
			OC_Util::addScript('files/fileinfo');
138
			OC_Util::addScript('files/client');
139
140
			if (\OC::$server->getConfig()->getSystemValue('debug')) {
141
				// Add the stuff we need always
142
				// following logic will import all vendor libraries that are
143
				// specified in core/js/core.json
144
				$fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
145
				if($fileContent !== false) {
146
					$coreDependencies = json_decode($fileContent, true);
147
					foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
148
						//remove trailing ".js" as addVendorScript will append it
149
						OC_Util::addVendorScript(
150
							substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true);
151
						}
152
 				} else {
153
					throw new \Exception('Cannot read core/js/core.json');
154
				}
155
			} else {
156
				// Import all (combined) default vendor libraries
157
				OC_Util::addVendorScript('core', null, true);
158
			}
159
160
			if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
161
				// polyfill for btoa/atob for IE friends
162
				OC_Util::addVendorScript('base64/base64');
163
				// shim for the davclient.js library
164
				\OCP\Util::addScript('files/iedavclient');
165
			}
166
167
			self::$initTemplateEngineFirstRun = false;
168
		}
169
170
	}
171
172
173
	/**
174
	 * find the template with the given name
175
	 * @param string $name of the template file (without suffix)
176
	 *
177
	 * Will select the template file for the selected theme.
178
	 * Checking all the possible locations.
179
	 * @param string $theme
180
	 * @param string $app
181
	 * @return string[]
182
	 */
183
	protected function findTemplate($theme, $app, $name) {
184
		// Check if it is a app template or not.
185
		if( $app !== '' ) {
186
			$dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
187
		} else {
188
			$dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
189
		}
190
		$locator = new \OC\Template\TemplateFileLocator( $dirs );
191
		$template = $locator->find($name);
192
		$path = $locator->getPath();
193
		return array($path, $template);
194
	}
195
196
	/**
197
	 * Add a custom element to the header
198
	 * @param string $tag tag name of the element
199
	 * @param array $attributes array of attributes for the element
200
	 * @param string $text the text content for the element. If $text is null then the
201
	 * element will be written as empty element. So use "" to get a closing tag.
202
	 */
203 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...
204
		$this->headers[]= array(
205
			'tag' => $tag,
206
			'attributes' => $attributes,
207
			'text' => $text
208
		);
209
	}
210
211
	/**
212
	 * Process the template
213
	 * @return boolean|string
214
	 *
215
	 * This function process the template. If $this->renderAs is set, it
216
	 * will produce a full page.
217
	 */
218
	public function fetchPage($additionalParams = null) {
219
		$data = parent::fetchPage($additionalParams);
220
221
		if( $this->renderAs ) {
222
			$page = new TemplateLayout($this->renderAs, $this->app);
223
224
			// Add custom headers
225
			$headers = '';
226
			foreach(OC_Util::$headers as $header) {
227
				$headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
228
				foreach($header['attributes'] as $name=>$value) {
229
					$headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
230
				}
231
				if ($header['text'] !== null) {
232
					$headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
233
				} else {
234
					$headers .= '/>';
235
				}
236
			}
237
238
			$page->assign('headers', $headers);
239
240
			$page->assign('content', $data);
241
			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 241 which is incompatible with the return type of the parent method OC\Template\Base::fetchPage of type string.
Loading history...
242
		}
243
244
		return $data;
245
	}
246
247
	/**
248
	 * Include template
249
	 *
250
	 * @param string $file
251
	 * @param array|null $additionalParams
252
	 * @return string returns content of included template
253
	 *
254
	 * Includes another template. use <?php echo $this->inc('template'); ?> to
255
	 * do this.
256
	 */
257
	public function inc( $file, $additionalParams = null ) {
258
		return $this->load($this->path.$file.'.php', $additionalParams);
259
	}
260
261
	/**
262
	 * Shortcut to print a simple page for users
263
	 * @param string $application The application we render the template for
264
	 * @param string $name Name of the template
265
	 * @param array $parameters Parameters for the template
266
	 * @return boolean|null
267
	 */
268 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...
269
		$content = new OC_Template( $application, $name, "user" );
270
		foreach( $parameters as $key => $value ) {
271
			$content->assign( $key, $value );
272
		}
273
		print $content->printPage();
274
	}
275
276
	/**
277
	 * Shortcut to print a simple page for admins
278
	 * @param string $application The application we render the template for
279
	 * @param string $name Name of the template
280
	 * @param array $parameters Parameters for the template
281
	 * @return bool
282
	 */
283 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...
284
		$content = new OC_Template( $application, $name, "admin" );
285
		foreach( $parameters as $key => $value ) {
286
			$content->assign( $key, $value );
287
		}
288
		return $content->printPage();
289
	}
290
291
	/**
292
	 * Shortcut to print a simple page for guests
293
	 * @param string $application The application we render the template for
294
	 * @param string $name Name of the template
295
	 * @param array|string $parameters Parameters for the template
296
	 * @return bool
297
	 */
298 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...
299
		$content = new OC_Template( $application, $name, "guest" );
300
		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...
301
			$content->assign( $key, $value );
302
		}
303
		return $content->printPage();
304
	}
305
306
	/**
307
		* Print a fatal error page and terminates the script
308
		* @param string $error_msg The error message to show
309
		* @param string $hint An optional hint message - needs to be properly escaped
310
		*/
311
	public static function printErrorPage( $error_msg, $hint = '' ) {
312
		if ($error_msg === $hint) {
313
			// If the hint is the same as the message there is no need to display it twice.
314
			$hint = '';
315
		}
316
317
		try {
318
			$content = new \OC_Template( '', 'error', 'error', false );
319
			$errors = array(array('error' => $error_msg, 'hint' => $hint));
320
			$content->assign( 'errors', $errors );
321
			$content->printPage();
322
		} catch (\Exception $e) {
323
			$logger = \OC::$server->getLogger();
324
			$logger->error("$error_msg $hint", ['app' => 'core']);
325
			$logger->logException($e, ['app' => 'core']);
326
327
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
328
			header('Content-Type: text/plain; charset=utf-8');
329
			print("$error_msg $hint");
330
		}
331
		die();
332
	}
333
334
	/**
335
	 * print error page using Exception details
336
	 * @param Exception | Throwable $exception
337
	 */
338
	public static function printExceptionErrorPage($exception, $fetchPage = false) {
339
		try {
340
			$request = \OC::$server->getRequest();
341
			$content = new \OC_Template('', 'exception', 'error', false);
342
			$content->assign('errorClass', get_class($exception));
343
			$content->assign('errorMsg', $exception->getMessage());
344
			$content->assign('errorCode', $exception->getCode());
345
			$content->assign('file', $exception->getFile());
346
			$content->assign('line', $exception->getLine());
347
			$content->assign('trace', $exception->getTraceAsString());
348
			$content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
349
			$content->assign('remoteAddr', $request->getRemoteAddress());
350
			$content->assign('requestID', $request->getId());
351
			if ($fetchPage) {
352
				return $content->fetchPage();
353
			}
354
			$content->printPage();
355
		} catch (\Exception $e) {
356
			$logger = \OC::$server->getLogger();
357
			$logger->logException($exception, ['app' => 'core']);
358
			$logger->logException($e, ['app' => 'core']);
359
360
			header(self::getHttpProtocol() . ' 500 Internal Server Error');
361
			header('Content-Type: text/plain; charset=utf-8');
362
			print("Internal Server Error\n\n");
363
			print("The server encountered an internal error and was unable to complete your request.\n");
364
			print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
365
			print("More details can be found in the server log.\n");
366
		}
367
		die();
368
	}
369
370
	/**
371
	 * This is only here to reduce the dependencies in case of an exception to
372
	 * still be able to print a plain error message.
373
	 *
374
	 * Returns the used HTTP protocol.
375
	 *
376
	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
377
	 * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead
378
	 */
379 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...
380
		$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
381
		$validProtocols = [
382
			'HTTP/1.0',
383
			'HTTP/1.1',
384
			'HTTP/2',
385
		];
386
		if(in_array($claimedProtocol, $validProtocols, true)) {
387
			return $claimedProtocol;
388
		}
389
		return 'HTTP/1.1';
390
	}
391
}
392