Completed
Pull Request — master (#8557)
by Morris
17:26
created

URLGenerator::linkToDocs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Bart Visscher <[email protected]>
7
 * @author Felix Epp <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Julius Haertl <[email protected]>
10
 * @author Julius Härtl <[email protected]>
11
 * @author Jörn Friedrich Dreyer <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author mmccarn <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author Robin Appelman <[email protected]>
16
 * @author Robin McCorkell <[email protected]>
17
 * @author Thomas Müller <[email protected]>
18
 * @author Thomas Tanghus <[email protected]>
19
 *
20
 * @license AGPL-3.0
21
 *
22
 * This code is free software: you can redistribute it and/or modify
23
 * it under the terms of the GNU Affero General Public License, version 3,
24
 * as published by the Free Software Foundation.
25
 *
26
 * This program is distributed in the hope that it will be useful,
27
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
 * GNU Affero General Public License for more details.
30
 *
31
 * You should have received a copy of the GNU Affero General Public License, version 3,
32
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
33
 *
34
 */
35
36
namespace OC;
37
38
use OCA\Theming\ThemingDefaults;
39
use OCP\ICacheFactory;
40
use OCP\IConfig;
41
use OCP\IRequest;
42
use OCP\IURLGenerator;
43
use RuntimeException;
44
45
/**
46
 * Class to generate URLs
47
 */
48
class URLGenerator implements IURLGenerator {
49
	/** @var IConfig */
50
	private $config;
51
	/** @var ICacheFactory */
52
	private $cacheFactory;
53
	/** @var IRequest */
54
	private $request;
55
56
	/**
57
	 * @param IConfig $config
58
	 * @param ICacheFactory $cacheFactory
59
	 * @param IRequest $request
60
	 */
61
	public function __construct(IConfig $config,
62
								ICacheFactory $cacheFactory,
63
								IRequest $request) {
64
		$this->config = $config;
65
		$this->cacheFactory = $cacheFactory;
66
		$this->request = $request;
67
	}
68
69
	/**
70
	 * Creates an url using a defined route
71
	 * @param string $route
72
	 * @param array $parameters args with param=>value, will be appended to the returned url
73
	 * @return string the url
74
	 *
75
	 * Returns a url to the given route.
76
	 */
77
	public function linkToRoute(string $route, array $parameters = array()): string {
78
		// TODO: mock router
79
		return \OC::$server->getRouter()->generate($route, $parameters);
80
	}
81
82
	/**
83
	 * Creates an absolute url using a defined route
84
	 * @param string $routeName
85
	 * @param array $arguments args with param=>value, will be appended to the returned url
86
	 * @return string the url
87
	 *
88
	 * Returns an absolute url to the given route.
89
	 */
90
	public function linkToRouteAbsolute(string $routeName, array $arguments = array()): string {
91
		return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments));
92
	}
93
94
	/**
95
	 * Creates an url
96
	 * @param string $app app
97
	 * @param string $file file
98
	 * @param array $args array with param=>value, will be appended to the returned url
99
	 *    The value of $args will be urlencoded
100
	 * @return string the url
101
	 *
102
	 * Returns a url to the given app and file.
103
	 */
104
	public function linkTo(string $app, string $file, array $args = array()): string {
105
		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
106
107
		if( $app !== '' ) {
108
			$app_path = \OC_App::getAppPath($app);
109
			// Check if the app is in the app folder
110
			if ($app_path && file_exists($app_path . '/' . $file)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $app_path of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
111
				if (substr($file, -3) === 'php') {
112
113
					$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app;
114
					if ($frontControllerActive) {
115
						$urlLinkTo = \OC::$WEBROOT . '/apps/' . $app;
116
					}
117
					$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
118
				} else {
119
					$urlLinkTo = \OC_App::getAppWebPath($app) . '/' . $file;
120
				}
121
			} else {
122
				$urlLinkTo = \OC::$WEBROOT . '/' . $app . '/' . $file;
123
			}
124
		} else {
125
			if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
126
				$urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
127
			} else {
128
				if ($frontControllerActive && $file === 'index.php') {
129
					$urlLinkTo = \OC::$WEBROOT . '/';
130
				} else {
131
					$urlLinkTo = \OC::$WEBROOT . '/' . $file;
132
				}
133
			}
134
		}
135
136
		if ($args && $query = http_build_query($args, '', '&')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
137
			$urlLinkTo .= '?' . $query;
138
		}
139
140
		return $urlLinkTo;
141
	}
142
143
	/**
144
	 * Creates path to an image
145
	 * @param string $app app
146
	 * @param string $image image name
147
	 * @throws \RuntimeException If the image does not exist
148
	 * @return string the url
149
	 *
150
	 * Returns the path to the image.
151
	 */
152
	public function imagePath(string $app, string $image): string {
153
		$cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-');
154
		$cacheKey = $app.'-'.$image;
155
		if($key = $cache->get($cacheKey)) {
156
			return $key;
157
		}
158
159
		// Read the selected theme from the config file
160
		$theme = \OC_Util::getTheme();
161
162
		//if a theme has a png but not an svg always use the png
163
		$basename = substr(basename($image),0,-4);
164
165
		$appPath = \OC_App::getAppPath($app);
166
167
		// Check if the app is in the app folder
168
		$path = '';
169
		$themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming');
170
		$themingImagePath = false;
171
		if($themingEnabled) {
172
			$themingDefaults = \OC::$server->getThemingDefaults();
173
			if ($themingDefaults instanceof ThemingDefaults) {
174
				$themingImagePath = $themingDefaults->replaceImagePath($app, $image);
175
			}
176
		}
177
178
		if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) {
179
			$path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image";
180
		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg")
181
			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) {
182
			$path =  \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png";
183
		} elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$image")) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
184
			$path =  \OC::$WEBROOT . "/themes/$theme/$app/img/$image";
185 View Code Duplication
		} elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
186
			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) {
187
			$path =  \OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png";
188
		} elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$image")) {
189
			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$image";
190 View Code Duplication
		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
191
			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
192
			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
193
		} elseif($themingEnabled && $themingImagePath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $themingImagePath of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
194
			$path = $themingImagePath;
195
		} elseif ($appPath && file_exists($appPath . "/img/$image")) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $appPath of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
196
			$path =  \OC_App::getAppWebPath($app) . "/img/$image";
197
		} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
0 ignored issues
show
Bug Best Practice introduced by
The expression $appPath of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
198
			&& file_exists($appPath . "/img/$basename.png")) {
199
			$path =  \OC_App::getAppWebPath($app) . "/img/$basename.png";
200
		} elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/$app/img/$image")) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
201
			$path =  \OC::$WEBROOT . "/$app/img/$image";
202 View Code Duplication
		} elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/$app/img/$basename.svg")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
203
				&& file_exists(\OC::$SERVERROOT . "/$app/img/$basename.png"))) {
204
			$path =  \OC::$WEBROOT . "/$app/img/$basename.png";
205
		} elseif (file_exists(\OC::$SERVERROOT . "/core/img/$image")) {
206
			$path =  \OC::$WEBROOT . "/core/img/$image";
207 View Code Duplication
		} elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
208
			&& file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
209
			$path =  \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
210
		}
211
212
		if($path !== '') {
213
			$cache->set($cacheKey, $path);
214
			return $path;
215
		}
216
217
		throw new RuntimeException('image not found: image:' . $image . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
218
	}
219
220
221
	/**
222
	 * Makes an URL absolute
223
	 * @param string $url the url in the ownCloud host
224
	 * @return string the absolute version of the url
225
	 */
226
	public function getAbsoluteURL(string $url): string {
227
		$separator = $url[0] === '/' ? '' : '/';
228
229
		if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
230
			return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
231
		}
232
		// The ownCloud web root can already be prepended.
233
		if(substr($url, 0, \strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) {
234
			$url = substr($url, \strlen(\OC::$WEBROOT));
235
		}
236
237
		return $this->getBaseUrl() . $separator . $url;
238
	}
239
240
	/**
241
	 * @param string $key
242
	 * @return string url to the online documentation
243
	 */
244
	public function linkToDocs(string $key): string {
245
		$theme = \OC::$server->getThemingDefaults();
246
		return $theme->buildDocLinkToKey($key);
247
	}
248
249
	/**
250
	 * @return string base url of the current request
251
	 */
252
	public function getBaseUrl(): string {
253
		return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
254
	}
255
}
256