Completed
Push — master ( 921667...4f4873 )
by Thomas
10:52
created

ViewController::showFile()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 54
Code Lines 35

Duplication

Lines 9
Ratio 16.67 %

Importance

Changes 0
Metric Value
cc 7
eloc 35
nc 12
nop 1
dl 9
loc 54
rs 7.8331
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Christoph Wurst <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Jörn Friedrich Dreyer <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2018, ownCloud GmbH
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OCA\Files\Controller;
28
29
use OC\AppFramework\Http\Request;
30
use OCP\App\IAppManager;
31
use OCP\AppFramework\Controller;
32
use OCP\AppFramework\Http\ContentSecurityPolicy;
33
use OCP\AppFramework\Http\RedirectResponse;
34
use OCP\AppFramework\Http\TemplateResponse;
35
use OCP\Files\Folder;
36
use OCP\Files\NotFoundException;
37
use OCP\IConfig;
38
use OCP\IL10N;
39
use OCP\INavigationManager;
40
use OCP\IRequest;
41
use OCP\IURLGenerator;
42
use OCP\IUserSession;
43
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
44
use OCP\AppFramework\Http;
45
use Symfony\Component\EventDispatcher\GenericEvent;
46
47
/**
48
 * Class ViewController
49
 *
50
 * @package OCA\Files\Controller
51
 */
52
class ViewController extends Controller {
53
	/** @var string */
54
	protected $appName;
55
	/** @var IRequest */
56
	protected $request;
57
	/** @var IURLGenerator */
58
	protected $urlGenerator;
59
	/** @var INavigationManager */
60
	protected $navigationManager;
61
	/** @var IL10N */
62
	protected $l10n;
63
	/** @var IConfig */
64
	protected $config;
65
	/** @var EventDispatcherInterface */
66
	protected $eventDispatcher;
67
	/** @var IUserSession */
68
	protected $userSession;
69
	/** @var IAppManager */
70
	protected $appManager;
71
	/** @var \OCP\Files\Folder */
72
	protected $rootFolder;
73
74
	/**
75
	 * @param string $appName
76
	 * @param IRequest $request
77
	 * @param IURLGenerator $urlGenerator
78
	 * @param IL10N $l10n
79
	 * @param IConfig $config
80
	 * @param EventDispatcherInterface $eventDispatcherInterface
81
	 * @param IUserSession $userSession
82
	 * @param IAppManager $appManager
83
	 * @param Folder $rootFolder
84
	 */
85
	public function __construct($appName,
86
								IRequest $request,
87
								IURLGenerator $urlGenerator,
88
								IL10N $l10n,
89
								IConfig $config,
90
								EventDispatcherInterface $eventDispatcherInterface,
91
								IUserSession $userSession,
92
								IAppManager $appManager,
93
								Folder $rootFolder
94
	) {
95
		parent::__construct($appName, $request);
96
		$this->appName = $appName;
97
		$this->request = $request;
98
		$this->urlGenerator = $urlGenerator;
99
		$this->l10n = $l10n;
100
		$this->config = $config;
101
		$this->eventDispatcher = $eventDispatcherInterface;
102
		$this->userSession = $userSession;
103
		$this->appManager = $appManager;
104
		$this->rootFolder = $rootFolder;
105
	}
106
107
	/**
108
	 * @param string $appName
109
	 * @param string $scriptName
110
	 * @return string
111
	 */
112
	protected function renderScript($appName, $scriptName) {
113
		$content = '';
114
		$appPath = \OC_App::getAppPath($appName);
115
		$scriptPath = $appPath . '/' . $scriptName;
116
		if (file_exists($scriptPath)) {
117
			// TODO: sanitize path / script name ?
118
			ob_start();
119
			include $scriptPath;
120
			$content = ob_get_contents();
121
			@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
122
		}
123
		return $content;
124
	}
125
126
	/**
127
	 * FIXME: Replace with non static code
128
	 *
129
	 * @return array
130
	 * @throws \OCP\Files\NotFoundException
131
	 */
132
	protected function getStorageInfo() {
133
		$dirInfo = \OC\Files\Filesystem::getFileInfo('/', false);
134
		return \OC_Helper::getStorageInfo('/', $dirInfo);
0 ignored issues
show
Security Bug introduced by
It seems like $dirInfo defined by \OC\Files\Filesystem::getFileInfo('/', false) on line 133 can also be of type false; however, OC_Helper::getStorageInfo() does only seem to accept object<OCP\Files\FileInfo>|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
135
	}
136
137
	/**
138
	 * @NoCSRFRequired
139
	 * @NoAdminRequired
140
	 *
141
	 * @param string $dir
142
	 * @param string $view
143
	 * @param string $fileid
144
	 * @return TemplateResponse
145
	 */
146
	public function index($dir = '', $view = '', $fileid = null) {
147
		$fileNotFound = false;
148
		if ($fileid !== null) {
149
			try {
150
				return $this->showFile($fileid);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->showFile($fileid); (OCP\AppFramework\Http\RedirectResponse) is incompatible with the return type documented by OCA\Files\Controller\ViewController::index of type OCP\AppFramework\Http\TemplateResponse.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
151
			} catch (NotFoundException $e) {
152
				$fileNotFound = true;
153
			}
154
		}
155
156
		$nav = new \OCP\Template('files', 'appnavigation', '');
157
158
		// Load the files we need
159
		\OCP\Util::addStyle('files', 'files');
160
		\OCP\Util::addStyle('files', 'upload');
161
		\OCP\Util::addStyle('files', 'mobile');
162
		\OCP\Util::addScript('files', 'app');
163
		\OCP\Util::addScript('files', 'file-upload');
164
		\OCP\Util::addScript('files', 'newfilemenu');
165
		\OCP\Util::addScript('files', 'jquery.fileupload');
166
		\OCP\Util::addScript('files', 'jquery-visibility');
167
		\OCP\Util::addScript('files', 'fileinfomodel');
168
		\OCP\Util::addScript('files', 'filesummary');
169
		\OCP\Util::addScript('files', 'breadcrumb');
170
		\OCP\Util::addScript('files', 'filelist');
171
		\OCP\Util::addScript('files', 'search');
172
173
		\OCP\Util::addScript('files', 'favoritesfilelist');
174
		\OCP\Util::addScript('files', 'tagsplugin');
175
		\OCP\Util::addScript('files', 'favoritesplugin');
176
177
		\OCP\Util::addScript('files', 'detailfileinfoview');
178
		\OCP\Util::addScript('files', 'detailtabview');
179
		\OCP\Util::addScript('files', 'mainfileinfodetailview');
180
		\OCP\Util::addScript('files', 'detailsview');
181
		\OCP\Util::addStyle('files', 'detailsView');
182
183
		\OC_Util::addVendorScript('core', 'handlebars/handlebars');
184
185
		\OCP\Util::addScript('files', 'fileactions');
186
		\OCP\Util::addScript('files', 'fileactionsmenu');
187
		\OCP\Util::addScript('files', 'files');
188
		\OCP\Util::addScript('files', 'keyboardshortcuts');
189
		\OCP\Util::addScript('files', 'navigation');
190
191
		// if IE8 and "?dir=path&view=someview" was specified, reformat the URL to use a hash like "#?dir=path&view=someview"
192
		$isIE8 = $this->request->isUserAgent([Request::USER_AGENT_IE_8]);
193
		if ($isIE8 && ($dir !== '' || $view !== '')) {
194
			$dir = !empty($dir) ? $dir : '/';
195
			$view = !empty($view) ? $view : 'files';
196
			$hash = '#?dir=' . \OCP\Util::encodePath($dir);
197
			if ($view !== 'files') {
198
				$hash .= '&view=' . urlencode($view);
199
			}
200
			return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index') . $hash);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \OCP\AppFrame....view.index') . $hash); (OCP\AppFramework\Http\RedirectResponse) is incompatible with the return type documented by OCA\Files\Controller\ViewController::index of type OCP\AppFramework\Http\TemplateResponse.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
201
		}
202
203
		// mostly for the home storage's free space
204
		// FIXME: Make non static
205
		$storageInfo = $this->getStorageInfo();
206
207
		\OCA\Files\App::getNavigationManager()->add(
208
			[
209
				'id' => 'favorites',
210
				'appname' => 'files',
211
				'script' => 'simplelist.php',
212
				'order' => 5,
213
				'name' => $this->l10n->t('Favorites')
214
			]
215
		);
216
217
		$user = $this->userSession->getUser()->getUID();
218
219
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
220
		usort($navItems, function($item1, $item2) {
221
			return $item1['order'] - $item2['order'];
222
		});
223
		$nav->assign('navigationItems', $navItems);
224
		$nav->assign('webdavUrl', $this->urlGenerator->getAbsoluteUrl($this->urlGenerator->linkTo('', 'remote.php') . '/dav/files/' . rawurlencode($user) . '/'));
225
226
		$contentItems = [];
227
228
		// render the container content for every navigation item
229
		foreach ($navItems as $item) {
230
			$content = '';
231
			if (isset($item['script'])) {
232
				$content = $this->renderScript($item['appname'], $item['script']);
233
			}
234
			$contentItem = [];
235
			$contentItem['id'] = $item['id'];
236
			$contentItem['content'] = $content;
237
			$contentItems[] = $contentItem;
238
		}
239
240
		$this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts');
241
242
		$params = [];
243
		$params['usedSpacePercent'] = (int)$storageInfo['relative'];
244
		$params['owner'] = $storageInfo['owner'];
245
		$params['ownerDisplayName'] = $storageInfo['ownerDisplayName'];
246
		$params['isPublic'] = false;
247
		$params['mailNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no');
248
		$params['mailPublicNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no');
249
		$params['socialShareEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_social_share', 'yes');
250
		$params['allowShareWithLink'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes');
251
		$params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name');
252
		$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
253
		$showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
254
		$params['showHiddenFiles'] = $showHidden ? 1 : 0;
255
		$params['fileNotFound'] = $fileNotFound ? 1 : 0;
256
		$params['appNavigation'] = $nav;
257
		$params['appContents'] = $contentItems;
258
259
		$response = new TemplateResponse(
260
			$this->appName,
261
			'index',
262
			$params
263
		);
264
		$policy = new ContentSecurityPolicy();
265
		$policy->addAllowedFrameDomain('\'self\'');
266
		$response->setContentSecurityPolicy($policy);
267
268
		return $response;
269
	}
270
271
	/**
272
	 * Redirects to the file list and highlight the given file id
273
	 *
274
	 * @param string $fileId file id to show
275
	 * @return RedirectResponse redirect response or not found response
276
	 * @throws \OCP\Files\NotFoundException
277
	 *
278
	 * @NoCSRFRequired
279
	 * @NoAdminRequired
280
	 */
281
	public function showFile($fileId) {
282
		$uid = $this->userSession->getUser()->getUID();
283
		$baseFolder = $this->rootFolder->get($uid . '/files/');
284
		$files = $baseFolder->getById($fileId);
285
		$params = [];
286
287
		if (empty($files)) {
288
			// probe apps to see if the file is in a different state and can be accessed
289
			// through another URL
290
			$event = new GenericEvent(null, [
291
				'fileid' => $fileId,
292
				'uid' => $uid,
293
				'resolvedWebLink' => null,
294
				'resolvedDavLink' => null,
295
			]);
296
			$this->eventDispatcher->dispatch('files.resolvePrivateLink', $event);
297
298
			$webUrl = $event->getArgument('resolvedWebLink');
299
			$webdavUrl = $event->getArgument('resolvedDavLink');
300
		} else {
301
			$file = current($files);
302 View Code Duplication
			if ($file instanceof Folder) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
303
				// set the full path to enter the folder
304
				$params['dir'] = $baseFolder->getRelativePath($file->getPath());
305
			} else {
306
				// set parent path as dir
307
				$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
308
				// and scroll to the entry
309
				$params['scrollto'] = $file->getName();
310
			}
311
			$webUrl = $this->urlGenerator->linkToRoute('files.view.index', $params);
312
313
			$webdavUrl = $this->urlGenerator->linkTo('', 'remote.php') . '/dav/files/' . rawurlencode($uid) . '/';
314
			$webdavUrl .= \OCP\Util::encodePath(ltrim($baseFolder->getRelativePath($file->getPath()), '/'));
315
		}
316
317
		if ($webUrl) {
318
			$response = new RedirectResponse($webUrl);
319
			if ($webdavUrl !== null) {
320
				$response->addHeader('Webdav-Location', $webdavUrl);
321
			}
322
			return $response;
323
		}
324
325
		if ($this->userSession->isLoggedIn() and empty($files)) {
326
			$param["error"] = $this->l10n->t("You don't have permissions to access this file/folder - Please contact the owner to share it with you.");
0 ignored issues
show
Coding Style Comprehensibility introduced by
$param was never initialized. Although not strictly required by PHP, it is generally a good practice to add $param = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
327
			$response = new TemplateResponse("core", 'error', ["errors" => [$param]], 'guest');
328
			$response->setStatus(Http::STATUS_NOT_FOUND);
329
			return $response;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $response; (OCP\AppFramework\Http\TemplateResponse) is incompatible with the return type documented by OCA\Files\Controller\ViewController::showFile of type OCP\AppFramework\Http\RedirectResponse.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
330
		}
331
332
		// FIXME: potentially dead code as the user is normally always logged in non-public routes
333
		throw new \OCP\Files\NotFoundException();
334
	}
335
}
336