Completed
Pull Request — master (#32)
by Blizzz
09:50
created

ViewController::index()   C

Complexity

Conditions 11
Paths 15

Size

Total Lines 116
Code Lines 84

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 84
c 2
b 0
f 0
nc 15
nop 3
dl 0
loc 116
rs 5.2653

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\Response;
31
use OCP\AppFramework\Http\RedirectResponse;
32
use OCP\AppFramework\Http\TemplateResponse;
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\AppFramework\Http\NotFoundResponse;
41
use OCP\Files\Folder;
42
use OCP\App\IAppManager;
43
44
/**
45
 * Class ViewController
46
 *
47
 * @package OCA\Files\Controller
48
 */
49
class ViewController extends Controller {
50
	/** @var string */
51
	protected $appName;
52
	/** @var IRequest */
53
	protected $request;
54
	/** @var IURLGenerator */
55
	protected $urlGenerator;
56
	/** @var INavigationManager */
57
	protected $navigationManager;
58
	/** @var IL10N */
59
	protected $l10n;
60
	/** @var IConfig */
61
	protected $config;
62
	/** @var EventDispatcherInterface */
63
	protected $eventDispatcher;
64
	/** @var IUserSession */
65
	protected $userSession;
66
	/** @var IAppManager */
67
	protected $appManager;
68
	/** @var \OCP\Files\Folder */
69
	protected $rootFolder;
70
71
	/**
72
	 * @param string $appName
73
	 * @param IRequest $request
74
	 * @param IURLGenerator $urlGenerator
75
	 * @param INavigationManager $navigationManager
76
	 * @param IL10N $l10n
77
	 * @param IConfig $config
78
	 * @param EventDispatcherInterface $eventDispatcherInterface
79
	 * @param IUserSession $userSession
80
	 * @param IAppManager $appManager
81
	 * @param Folder $rootFolder
82
	 */
83
	public function __construct($appName,
84
								IRequest $request,
85
								IURLGenerator $urlGenerator,
86
								INavigationManager $navigationManager,
87
								IL10N $l10n,
88
								IConfig $config,
89
								EventDispatcherInterface $eventDispatcherInterface,
90
								IUserSession $userSession,
91
								IAppManager $appManager,
92
								Folder $rootFolder
93
	) {
94
		parent::__construct($appName, $request);
95
		$this->appName = $appName;
96
		$this->request = $request;
97
		$this->urlGenerator = $urlGenerator;
98
		$this->navigationManager = $navigationManager;
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
	 * @throws \OCP\Files\NotFoundException
146
	 */
147
	public function index($dir = '', $view = '', $fileid = null) {
148
		if ($fileid !== null) {
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\Re...k\Http\NotFoundResponse) 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
		}
151
152
		$nav = new \OCP\Template('files', 'appnavigation', '');
153
154
		// Load the files we need
155
		\OCP\Util::addStyle('files', 'files');
156
		\OCP\Util::addStyle('files', 'upload');
157
		\OCP\Util::addStyle('files', 'mobile');
158
		\OCP\Util::addscript('files', 'app');
159
		\OCP\Util::addscript('files', 'file-upload');
160
		\OCP\Util::addscript('files', 'newfilemenu');
161
		\OCP\Util::addscript('files', 'jquery.fileupload');
162
		\OCP\Util::addscript('files', 'jquery-visibility');
163
		\OCP\Util::addscript('files', 'fileinfomodel');
164
		\OCP\Util::addscript('files', 'filesummary');
165
		\OCP\Util::addscript('files', 'breadcrumb');
166
		\OCP\Util::addscript('files', 'filelist');
167
		\OCP\Util::addscript('files', 'search');
168
169
		\OCP\Util::addScript('files', 'favoritesfilelist');
170
		\OCP\Util::addScript('files', 'tagsplugin');
171
		\OCP\Util::addScript('files', 'favoritesplugin');
172
173
		\OCP\Util::addScript('files', 'detailfileinfoview');
174
		\OCP\Util::addScript('files', 'detailtabview');
175
		\OCP\Util::addScript('files', 'mainfileinfodetailview');
176
		\OCP\Util::addScript('files', 'detailsview');
177
		\OCP\Util::addStyle('files', 'detailsView');
178
179
		\OC_Util::addVendorScript('core', 'handlebars/handlebars');
180
181
		\OCP\Util::addscript('files', 'fileactions');
182
		\OCP\Util::addscript('files', 'fileactionsmenu');
183
		\OCP\Util::addscript('files', 'files');
184
		\OCP\Util::addscript('files', 'keyboardshortcuts');
185
		\OCP\Util::addscript('files', 'navigation');
186
187
		// if IE8 and "?dir=path&view=someview" was specified, reformat the URL to use a hash like "#?dir=path&view=someview"
188
		$isIE8 = $this->request->isUserAgent([Request::USER_AGENT_IE_8]);
189
		if ($isIE8 && ($dir !== '' || $view !== '')) {
190
			$dir = !empty($dir) ? $dir : '/';
191
			$view = !empty($view) ? $view : 'files';
192
			$hash = '#?dir=' . \OCP\Util::encodePath($dir);
193
			if ($view !== 'files') {
194
				$hash .= '&view=' . urlencode($view);
195
			}
196
			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...
197
		}
198
199
		// mostly for the home storage's free space
200
		// FIXME: Make non static
201
		$storageInfo = $this->getStorageInfo();
202
203
		\OCA\Files\App::getNavigationManager()->add(
204
			[
205
				'id' => 'favorites',
206
				'appname' => 'files',
207
				'script' => 'simplelist.php',
208
				'order' => 5,
209
				'name' => $this->l10n->t('Favorites')
210
			]
211
		);
212
213
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
214
		usort($navItems, function($item1, $item2) {
215
			return $item1['order'] - $item2['order'];
216
		});
217
		$nav->assign('navigationItems', $navItems);
218
219
		$contentItems = [];
220
221
		// render the container content for every navigation item
222
		foreach ($navItems as $item) {
223
			$content = '';
224
			if (isset($item['script'])) {
225
				$content = $this->renderScript($item['appname'], $item['script']);
226
			}
227
			$contentItem = [];
228
			$contentItem['id'] = $item['id'];
229
			$contentItem['content'] = $content;
230
			$contentItems[] = $contentItem;
231
		}
232
233
		$this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts');
234
235
		$params = [];
236
		$params['usedSpacePercent'] = (int)$storageInfo['relative'];
237
		$params['owner'] = $storageInfo['owner'];
238
		$params['ownerDisplayName'] = $storageInfo['ownerDisplayName'];
239
		$params['isPublic'] = false;
240
		$params['mailNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no');
241
		$params['mailPublicNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no');
242
		$params['allowShareWithLink'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes');
243
		$user = $this->userSession->getUser()->getUID();
244
		$params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name');
245
		$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
246
		$showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
247
		$params['showHiddenFiles'] = $showHidden ? 1 : 0;
248
		$params['appNavigation'] = $nav;
249
		$params['appContents'] = $contentItems;
250
		$this->navigationManager->setActiveEntry('files_index');
251
252
		$response = new TemplateResponse(
253
			$this->appName,
254
			'index',
255
			$params
256
		);
257
		$policy = new ContentSecurityPolicy();
258
		$policy->addAllowedFrameDomain('\'self\'');
259
		$response->setContentSecurityPolicy($policy);
260
261
		return $response;
262
	}
263
264
	/**
265
	 * Redirects to the file list and highlight the given file id
266
	 *
267
	 * @param string $fileId file id to show
268
	 * @return Response redirect response or not found response
269
	 *
270
	 * @NoCSRFRequired
271
	 * @NoAdminRequired
272
	 */
273
	public function showFile($fileId) {
274
		try {
275
			$uid = $this->userSession->getUser()->getUID();
276
			$baseFolder = $this->rootFolder->get($uid . '/files/');
277
			$files = $baseFolder->getById($fileId);
278
			$params = [];
279
280
			if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
281
				$baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
282
				$files = $baseFolder->getById($fileId);
283
				$params['view'] = 'trashbin';
284
			}
285
286
			if (!empty($files)) {
287
				$file = current($files);
288
				if ($file instanceof Folder) {
289
					// set the full path to enter the folder
290
					$params['dir'] = $baseFolder->getRelativePath($file->getPath());
291
				} else {
292
					// set parent path as dir
293
					$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
294
					// and scroll to the entry
295
					$params['scrollto'] = $file->getName();
296
				}
297
				return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
298
			}
299
		} catch (\OCP\Files\NotFoundException $e) {
300
			return new NotFoundResponse();
301
		}
302
		return new NotFoundResponse();
303
	}
304
}
305