Completed
Push — master ( 281937...5a0dc1 )
by Blizzz
34s
created

ViewController::index()   D

Complexity

Conditions 13
Paths 41

Size

Total Lines 122
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 13
eloc 89
c 3
b 0
f 0
nc 41
nop 3
dl 0
loc 122
rs 4.9922

How to fix   Long Method    Complexity   

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 Lukas Reschke <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 * @author Vincent Petry <[email protected]>
7
 *
8
 * @copyright Copyright (c) 2016, ownCloud, Inc.
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OCA\Files\Controller;
26
27
use OC\AppFramework\Http\Request;
28
use OCP\AppFramework\Controller;
29
use OCP\AppFramework\Http\ContentSecurityPolicy;
30
use OCP\AppFramework\Http\RedirectResponse;
31
use OCP\AppFramework\Http\TemplateResponse;
32
use OCP\Files\NotFoundException;
33
use OCP\IConfig;
34
use OCP\IL10N;
35
use OCP\INavigationManager;
36
use OCP\IRequest;
37
use OCP\IURLGenerator;
38
use OCP\IUserSession;
39
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
40
use OCP\Files\Folder;
41
use OCP\App\IAppManager;
42
43
/**
44
 * Class ViewController
45
 *
46
 * @package OCA\Files\Controller
47
 */
48
class ViewController extends Controller {
49
	/** @var string */
50
	protected $appName;
51
	/** @var IRequest */
52
	protected $request;
53
	/** @var IURLGenerator */
54
	protected $urlGenerator;
55
	/** @var INavigationManager */
56
	protected $navigationManager;
57
	/** @var IL10N */
58
	protected $l10n;
59
	/** @var IConfig */
60
	protected $config;
61
	/** @var EventDispatcherInterface */
62
	protected $eventDispatcher;
63
	/** @var IUserSession */
64
	protected $userSession;
65
	/** @var IAppManager */
66
	protected $appManager;
67
	/** @var \OCP\Files\Folder */
68
	protected $rootFolder;
69
70
	/**
71
	 * @param string $appName
72
	 * @param IRequest $request
73
	 * @param IURLGenerator $urlGenerator
74
	 * @param INavigationManager $navigationManager
75
	 * @param IL10N $l10n
76
	 * @param IConfig $config
77
	 * @param EventDispatcherInterface $eventDispatcherInterface
78
	 * @param IUserSession $userSession
79
	 * @param IAppManager $appManager
80
	 * @param Folder $rootFolder
81
	 */
82
	public function __construct($appName,
83
								IRequest $request,
84
								IURLGenerator $urlGenerator,
85
								INavigationManager $navigationManager,
86
								IL10N $l10n,
87
								IConfig $config,
88
								EventDispatcherInterface $eventDispatcherInterface,
89
								IUserSession $userSession,
90
								IAppManager $appManager,
91
								Folder $rootFolder
92
	) {
93
		parent::__construct($appName, $request);
94
		$this->appName = $appName;
95
		$this->request = $request;
96
		$this->urlGenerator = $urlGenerator;
97
		$this->navigationManager = $navigationManager;
98
		$this->l10n = $l10n;
99
		$this->config = $config;
100
		$this->eventDispatcher = $eventDispatcherInterface;
101
		$this->userSession = $userSession;
102
		$this->appManager = $appManager;
103
		$this->rootFolder = $rootFolder;
104
	}
105
106
	/**
107
	 * @param string $appName
108
	 * @param string $scriptName
109
	 * @return string
110
	 */
111
	protected function renderScript($appName, $scriptName) {
112
		$content = '';
113
		$appPath = \OC_App::getAppPath($appName);
114
		$scriptPath = $appPath . '/' . $scriptName;
115
		if (file_exists($scriptPath)) {
116
			// TODO: sanitize path / script name ?
117
			ob_start();
118
			include $scriptPath;
119
			$content = ob_get_contents();
120
			@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...
121
		}
122
		return $content;
123
	}
124
125
	/**
126
	 * FIXME: Replace with non static code
127
	 *
128
	 * @return array
129
	 * @throws \OCP\Files\NotFoundException
130
	 */
131
	protected function getStorageInfo() {
132
		$dirInfo = \OC\Files\Filesystem::getFileInfo('/', false);
133
		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 132 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...
134
	}
135
136
	/**
137
	 * @NoCSRFRequired
138
	 * @NoAdminRequired
139
	 *
140
	 * @param string $dir
141
	 * @param string $view
142
	 * @param string $fileid
143
	 * @return TemplateResponse
144
	 */
145
	public function index($dir = '', $view = '', $fileid = null) {
146
		$fileNotFound = false;
147
		if ($fileid !== null) {
148
			try {
149
				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...
150
			} catch (NotFoundException $e) {
151
				$fileNotFound = true;
152
			}
153
		}
154
155
		$nav = new \OCP\Template('files', 'appnavigation', '');
156
157
		// Load the files we need
158
		\OCP\Util::addStyle('files', 'files');
159
		\OCP\Util::addStyle('files', 'upload');
160
		\OCP\Util::addStyle('files', 'mobile');
161
		\OCP\Util::addscript('files', 'app');
162
		\OCP\Util::addscript('files', 'file-upload');
163
		\OCP\Util::addscript('files', 'newfilemenu');
164
		\OCP\Util::addscript('files', 'jquery.fileupload');
165
		\OCP\Util::addscript('files', 'jquery-visibility');
166
		\OCP\Util::addscript('files', 'fileinfomodel');
167
		\OCP\Util::addscript('files', 'filesummary');
168
		\OCP\Util::addscript('files', 'breadcrumb');
169
		\OCP\Util::addscript('files', 'filelist');
170
		\OCP\Util::addscript('files', 'search');
171
172
		\OCP\Util::addScript('files', 'favoritesfilelist');
173
		\OCP\Util::addScript('files', 'tagsplugin');
174
		\OCP\Util::addScript('files', 'favoritesplugin');
175
176
		\OCP\Util::addScript('files', 'detailfileinfoview');
177
		\OCP\Util::addScript('files', 'detailtabview');
178
		\OCP\Util::addScript('files', 'mainfileinfodetailview');
179
		\OCP\Util::addScript('files', 'detailsview');
180
		\OCP\Util::addStyle('files', 'detailsView');
181
182
		\OC_Util::addVendorScript('core', 'handlebars/handlebars');
183
184
		\OCP\Util::addscript('files', 'fileactions');
185
		\OCP\Util::addscript('files', 'fileactionsmenu');
186
		\OCP\Util::addscript('files', 'files');
187
		\OCP\Util::addscript('files', 'keyboardshortcuts');
188
		\OCP\Util::addscript('files', 'navigation');
189
190
		// if IE8 and "?dir=path&view=someview" was specified, reformat the URL to use a hash like "#?dir=path&view=someview"
191
		$isIE8 = $this->request->isUserAgent([Request::USER_AGENT_IE_8]);
192
		if ($isIE8 && ($dir !== '' || $view !== '')) {
193
			$dir = !empty($dir) ? $dir : '/';
194
			$view = !empty($view) ? $view : 'files';
195
			$hash = '#?dir=' . \OCP\Util::encodePath($dir);
196
			if ($view !== 'files') {
197
				$hash .= '&view=' . urlencode($view);
198
			}
199
			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...
200
		}
201
202
		// mostly for the home storage's free space
203
		// FIXME: Make non static
204
		$storageInfo = $this->getStorageInfo();
205
206
		\OCA\Files\App::getNavigationManager()->add(
207
			[
208
				'id' => 'favorites',
209
				'appname' => 'files',
210
				'script' => 'simplelist.php',
211
				'order' => 5,
212
				'name' => $this->l10n->t('Favorites')
213
			]
214
		);
215
216
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
217
		usort($navItems, function($item1, $item2) {
218
			return $item1['order'] - $item2['order'];
219
		});
220
		$nav->assign('navigationItems', $navItems);
221
222
		$contentItems = [];
223
224
		// render the container content for every navigation item
225
		foreach ($navItems as $item) {
226
			$content = '';
227
			if (isset($item['script'])) {
228
				$content = $this->renderScript($item['appname'], $item['script']);
229
			}
230
			$contentItem = [];
231
			$contentItem['id'] = $item['id'];
232
			$contentItem['content'] = $content;
233
			$contentItems[] = $contentItem;
234
		}
235
236
		$this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts');
237
238
		$params = [];
239
		$params['usedSpacePercent'] = (int)$storageInfo['relative'];
240
		$params['owner'] = $storageInfo['owner'];
241
		$params['ownerDisplayName'] = $storageInfo['ownerDisplayName'];
242
		$params['isPublic'] = false;
243
		$params['mailNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no');
244
		$params['mailPublicNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no');
245
		$params['allowShareWithLink'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes');
246
		$user = $this->userSession->getUser()->getUID();
247
		$params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name');
248
		$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
249
		$showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
250
		$params['showHiddenFiles'] = $showHidden ? 1 : 0;
251
		$params['fileNotFound'] = $fileNotFound ? 1 : 0;
252
		$params['appNavigation'] = $nav;
253
		$params['appContents'] = $contentItems;
254
		$this->navigationManager->setActiveEntry('files_index');
255
256
		$response = new TemplateResponse(
257
			$this->appName,
258
			'index',
259
			$params
260
		);
261
		$policy = new ContentSecurityPolicy();
262
		$policy->addAllowedFrameDomain('\'self\'');
263
		$response->setContentSecurityPolicy($policy);
264
265
		return $response;
266
	}
267
268
	/**
269
	 * Redirects to the file list and highlight the given file id
270
	 *
271
	 * @param string $fileId file id to show
272
	 * @return RedirectResponse redirect response or not found response
273
	 * @throws \OCP\Files\NotFoundException
274
	 *
275
	 * @NoCSRFRequired
276
	 * @NoAdminRequired
277
	 */
278
	public function showFile($fileId) {
279
		$uid = $this->userSession->getUser()->getUID();
280
		$baseFolder = $this->rootFolder->get($uid . '/files/');
281
		$files = $baseFolder->getById($fileId);
282
		$params = [];
283
284
		if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
285
			$baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
286
			$files = $baseFolder->getById($fileId);
287
			$params['view'] = 'trashbin';
288
		}
289
290
		if (!empty($files)) {
291
			$file = current($files);
292
			if ($file instanceof Folder) {
293
				// set the full path to enter the folder
294
				$params['dir'] = $baseFolder->getRelativePath($file->getPath());
295
			} else {
296
				// set parent path as dir
297
				$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
298
				// and scroll to the entry
299
				$params['scrollto'] = $file->getName();
300
			}
301
			return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
302
		}
303
		throw new \OCP\Files\NotFoundException();
304
	}
305
}
306