Completed
Push — master ( f78db1...a155f8 )
by Morris
34:10 queued 10s
created

ViewController::index()   F

Complexity

Conditions 16
Paths 962

Size

Total Lines 139

Duplication

Lines 5
Ratio 3.6 %

Importance

Changes 0
Metric Value
cc 16
nc 962
nop 4
dl 5
loc 139
rs 1.1621
c 0
b 0
f 0

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
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 * @author Felix Nüsse <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OCA\Files\Controller;
30
31
use OCA\Files\Activity\Helper;
32
use OCP\AppFramework\Controller;
33
use OCP\AppFramework\Http\ContentSecurityPolicy;
34
use OCP\AppFramework\Http\RedirectResponse;
35
use OCP\AppFramework\Http\TemplateResponse;
36
use OCP\App\IAppManager;
37
use OCP\Files\Folder;
38
use OCP\Files\IRootFolder;
39
use OCP\Files\NotFoundException;
40
use OCP\IConfig;
41
use OCP\IL10N;
42
use OCP\IRequest;
43
use OCP\IURLGenerator;
44
use OCP\IUserSession;
45
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
46
use Symfony\Component\EventDispatcher\GenericEvent;
47
48
/**
49
 * Class ViewController
50
 *
51
 * @package OCA\Files\Controller
52
 */
53
class ViewController extends Controller {
54
	/** @var string */
55
	protected $appName;
56
	/** @var IRequest */
57
	protected $request;
58
	/** @var IURLGenerator */
59
	protected $urlGenerator;
60
	/** @var IL10N */
61
	protected $l10n;
62
	/** @var IConfig */
63
	protected $config;
64
	/** @var EventDispatcherInterface */
65
	protected $eventDispatcher;
66
	/** @var IUserSession */
67
	protected $userSession;
68
	/** @var IAppManager */
69
	protected $appManager;
70
	/** @var IRootFolder */
71
	protected $rootFolder;
72
	/** @var Helper */
73
	protected $activityHelper;
74
75
	public function __construct(string $appName,
76
		IRequest $request,
77
		IURLGenerator $urlGenerator,
78
		IL10N $l10n,
79
		IConfig $config,
80
		EventDispatcherInterface $eventDispatcherInterface,
81
		IUserSession $userSession,
82
		IAppManager $appManager,
83
		IRootFolder $rootFolder,
84
		Helper $activityHelper
85
	) {
86
		parent::__construct($appName, $request);
87
		$this->appName         = $appName;
88
		$this->request         = $request;
89
		$this->urlGenerator    = $urlGenerator;
90
		$this->l10n            = $l10n;
91
		$this->config          = $config;
92
		$this->eventDispatcher = $eventDispatcherInterface;
93
		$this->userSession     = $userSession;
94
		$this->appManager      = $appManager;
95
		$this->rootFolder      = $rootFolder;
96
		$this->activityHelper  = $activityHelper;
97
	}
98
99
	/**
100
	 * @param string $appName
101
	 * @param string $scriptName
102
	 * @return string
103
	 */
104
	protected function renderScript($appName, $scriptName) {
105
		$content    = '';
106
		$appPath    = \OC_App::getAppPath($appName);
107
		$scriptPath = $appPath . '/' . $scriptName;
108
		if (file_exists($scriptPath)) {
109
			// TODO: sanitize path / script name ?
110
			ob_start();
111
			include $scriptPath;
112
			$content = ob_get_contents();
113
			@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...
114
		}
115
116
		return $content;
117
	}
118
119
	/**
120
	 * FIXME: Replace with non static code
121
	 *
122
	 * @return array
123
	 * @throws \OCP\Files\NotFoundException
124
	 */
125
	protected function getStorageInfo() {
126
		$dirInfo = \OC\Files\Filesystem::getFileInfo('/', false);
127
128
		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 126 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...
129
	}
130
131
	/**
132
	 * @NoCSRFRequired
133
	 * @NoAdminRequired
134
	 *
135
	 * @param string $dir
136
	 * @param string $view
137
	 * @param string $fileid
138
	 * @return TemplateResponse|RedirectResponse
139
	 */
140
	public function index($dir = '', $view = '', $fileid = null, $fileNotFound = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $dir is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $view is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
141
		if ($fileid !== null) {
142
			try {
143
				return $this->showFile($fileid);
144
			} catch (NotFoundException $e) {
145
				return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', ['fileNotFound' => true]));
146
			}
147
		}
148
149
		$nav = new \OCP\Template('files', 'appnavigation', '');
150
151
		// Load the files we need
152
		\OCP\Util::addStyle('files', 'merged');
153
		\OCP\Util::addScript('files', 'merged-index');
154
155
		// mostly for the home storage's free space
156
		// FIXME: Make non static
157
		$storageInfo = $this->getStorageInfo();
158
159
		$user = $this->userSession->getUser()->getUID();
160
161
		// Get all the user favorites to create a submenu
162
		try {
163
			$favElements = $this->activityHelper->getFavoriteFilePaths($this->userSession->getUser()->getUID());
164
		} catch (\RuntimeException $e) {
165
			$favElements['folders'] = null;
166
		}
167
168
		$collapseClasses = '';
169
		if (count($favElements['folders']) > 0) {
170
			$collapseClasses = 'collapsible';
171
		}
172
173
		$favoritesSublistArray = Array();
174
175
		$navBarPositionPosition = 6;
176
		$currentCount           = 0;
177
		foreach ($favElements['folders'] as $dir) {
0 ignored issues
show
Bug introduced by
The expression $favElements['folders'] of type array|null 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...
178
179
			$id           = substr($dir, strrpos($dir, '/') + 1, strlen($dir));
180
			$link         = $this->urlGenerator->linkToRoute('files.view.index', ['dir' => $dir, 'view' => 'files']);
181
			$sortingValue = ++$currentCount;
182
			$element      = [
183
				'id'                 => str_replace('/', '-', $dir),
184
				'view'               => 'files',
185
				'href'               => $link,
186
				'dir'                => $dir,
187
				'order'              => $navBarPositionPosition,
188
				'folderPosition'     => $sortingValue,
189
				'name'               => $id,
190
				'icon'               => 'files',
191
				'quickaccesselement' => 'true'
192
			];
193
194
			array_push($favoritesSublistArray, $element);
195
			$navBarPositionPosition++;
196
		}
197
198
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
199
200
		// add the favorites entry in menu
201
		$navItems['favorites']['sublist'] = $favoritesSublistArray;
202
		$navItems['favorites']['classes'] = $collapseClasses;
203
204
		// parse every menu and add the expandedState user value
205
		foreach ($navItems as $key => $item) {
206
			if (isset($item['expandedState'])) {
207
				$navItems[$key]['defaultExpandedState'] = $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', $item['expandedState'], '0') === '1';
208
			}
209
		}
210
211
		$nav->assign('navigationItems', $navItems);
212
213
		$nav->assign('usage', \OC_Helper::humanFileSize($storageInfo['used']));
214 View Code Duplication
		if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
215
			$totalSpace = $this->l10n->t('Unlimited');
216
		} else {
217
			$totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
218
		}
219
		$nav->assign('total_space', $totalSpace);
220
		$nav->assign('quota', $storageInfo['quota']);
221
		$nav->assign('usage_relative', $storageInfo['relative']);
222
223
		$contentItems = [];
224
225
		// render the container content for every navigation item
226
		foreach ($navItems as $item) {
227
			$content = '';
228
			if (isset($item['script'])) {
229
				$content = $this->renderScript($item['appname'], $item['script']);
230
			}
231
			// parse submenus
232
			if (isset($item['sublist'])) {
233
				foreach ($item['sublist'] as $subitem) {
234
					$subcontent = '';
235
					if (isset($subitem['script'])) {
236
						$subcontent = $this->renderScript($subitem['appname'], $subitem['script']);
237
					}
238
					$contentItems[$subitem['id']] = [
239
						'id'      => $subitem['id'],
240
						'content' => $subcontent
241
					];
242
				}
243
			}
244
			$contentItems[$item['id']] = [
245
				'id'      => $item['id'],
246
				'content' => $content
247
			];
248
		}
249
250
		$event = new GenericEvent(null, ['hiddenFields' => []]);
251
		$this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts', $event);
252
253
		$params                                = [];
254
		$params['usedSpacePercent']            = (int) $storageInfo['relative'];
255
		$params['owner']                       = $storageInfo['owner'];
256
		$params['ownerDisplayName']            = $storageInfo['ownerDisplayName'];
257
		$params['isPublic']                    = false;
258
		$params['allowShareWithLink']          = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes');
259
		$params['defaultFileSorting']          = $this->config->getUserValue($user, 'files', 'file_sorting', 'name');
260
		$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
261
		$showHidden                            = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
262
		$params['showHiddenFiles']             = $showHidden ? 1 : 0;
263
		$params['fileNotFound']                = $fileNotFound ? 1 : 0;
264
		$params['appNavigation']               = $nav;
265
		$params['appContents']                 = $contentItems;
266
		$params['hiddenFields']                = $event->getArgument('hiddenFields');
267
268
		$response = new TemplateResponse(
269
			$this->appName,
270
			'index',
271
			$params
272
		);
273
		$policy = new ContentSecurityPolicy();
0 ignored issues
show
Deprecated Code introduced by
The class OCP\AppFramework\Http\ContentSecurityPolicy has been deprecated with message: 14.0.0 Use one of our stricter CSP policies

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
274
		$policy->addAllowedFrameDomain('\'self\'');
275
		$response->setContentSecurityPolicy($policy);
276
277
		return $response;
278
	}
279
280
	/**
281
	 * Redirects to the file list and highlight the given file id
282
	 *
283
	 * @param string $fileId file id to show
284
	 * @return RedirectResponse redirect response or not found response
285
	 * @throws \OCP\Files\NotFoundException
286
	 */
287
	private function showFile($fileId) {
288
		$uid        = $this->userSession->getUser()->getUID();
289
		$baseFolder = $this->rootFolder->getUserFolder($uid);
290
		$files      = $baseFolder->getById($fileId);
291
		$params     = [];
292
293
		if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
294
			$baseFolder     = $this->rootFolder->get($uid . '/files_trashbin/files/');
295
			$files          = $baseFolder->getById($fileId);
296
			$params['view'] = 'trashbin';
297
		}
298
299
		if (!empty($files)) {
300
			$file = current($files);
301
			if ($file instanceof Folder) {
302
				// set the full path to enter the folder
303
				$params['dir'] = $baseFolder->getRelativePath($file->getPath());
304
			} else {
305
				// set parent path as dir
306
				$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
307
				// and scroll to the entry
308
				$params['scrollto'] = $file->getName();
309
			}
310
311
			return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
312
		}
313
		throw new \OCP\Files\NotFoundException();
314
	}
315
}
316