Completed
Push — master ( 1fb7be...e8e72a )
by Lukas
15:57
created

ViewController::getStorageInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 0
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
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 \OCP\Files\Folder */
66
	protected $userFolder;
67
68
	/**
69
	 * @param string $appName
70
	 * @param IRequest $request
71
	 * @param IURLGenerator $urlGenerator
72
	 * @param INavigationManager $navigationManager
73
	 * @param IL10N $l10n
74
	 * @param IConfig $config
75
	 * @param EventDispatcherInterface $eventDispatcherInterface
76
	 * @param IUserSession $userSession
77
	 * @param Folder $userFolder
78
	 */
79 View Code Duplication
	public function __construct($appName,
80
								IRequest $request,
81
								IURLGenerator $urlGenerator,
82
								INavigationManager $navigationManager,
83
								IL10N $l10n,
84
								IConfig $config,
85
								EventDispatcherInterface $eventDispatcherInterface,
86
								IUserSession $userSession,
87
								Folder $userFolder
88
	) {
89
		parent::__construct($appName, $request);
90
		$this->appName = $appName;
91
		$this->request = $request;
92
		$this->urlGenerator = $urlGenerator;
93
		$this->navigationManager = $navigationManager;
94
		$this->l10n = $l10n;
95
		$this->config = $config;
96
		$this->eventDispatcher = $eventDispatcherInterface;
97
		$this->userSession = $userSession;
98
		$this->userFolder = $userFolder;
99
	}
100
101
	/**
102
	 * @param string $appName
103
	 * @param string $scriptName
104
	 * @return string
105
	 */
106
	protected function renderScript($appName, $scriptName) {
107
		$content = '';
108
		$appPath = \OC_App::getAppPath($appName);
109
		$scriptPath = $appPath . '/' . $scriptName;
110
		if (file_exists($scriptPath)) {
111
			// TODO: sanitize path / script name ?
112
			ob_start();
113
			include $scriptPath;
114
			$content = ob_get_contents();
115
			@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...
116
		}
117
		return $content;
118
	}
119
120
	/**
121
	 * FIXME: Replace with non static code
122
	 *
123
	 * @return array
124
	 * @throws \OCP\Files\NotFoundException
125
	 */
126
	protected function getStorageInfo() {
127
		$dirInfo = \OC\Files\Filesystem::getFileInfo('/', false);
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 127 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
139
	 * @throws \OCP\Files\NotFoundException
140
	 */
141
	public function index($dir = '', $view = '', $fileid = null) {
142
		if ($fileid !== null) {
143
			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...
144
		}
145
146
		$nav = new \OCP\Template('files', 'appnavigation', '');
147
148
		// Load the files we need
149
		\OCP\Util::addStyle('files', 'files');
150
		\OCP\Util::addStyle('files', 'upload');
151
		\OCP\Util::addStyle('files', 'mobile');
152
		\OCP\Util::addscript('files', 'app');
153
		\OCP\Util::addscript('files', 'file-upload');
154
		\OCP\Util::addscript('files', 'newfilemenu');
155
		\OCP\Util::addscript('files', 'jquery.fileupload');
156
		\OCP\Util::addscript('files', 'jquery-visibility');
157
		\OCP\Util::addscript('files', 'fileinfomodel');
158
		\OCP\Util::addscript('files', 'filesummary');
159
		\OCP\Util::addscript('files', 'breadcrumb');
160
		\OCP\Util::addscript('files', 'filelist');
161
		\OCP\Util::addscript('files', 'search');
162
163
		\OCP\Util::addScript('files', 'favoritesfilelist');
164
		\OCP\Util::addScript('files', 'tagsplugin');
165
		\OCP\Util::addScript('files', 'favoritesplugin');
166
167
		\OCP\Util::addScript('files', 'detailfileinfoview');
168
		\OCP\Util::addScript('files', 'detailtabview');
169
		\OCP\Util::addScript('files', 'mainfileinfodetailview');
170
		\OCP\Util::addScript('files', 'detailsview');
171
		\OCP\Util::addStyle('files', 'detailsView');
172
173
		\OC_Util::addVendorScript('core', 'handlebars/handlebars');
174
175
		\OCP\Util::addscript('files', 'fileactions');
176
		\OCP\Util::addscript('files', 'fileactionsmenu');
177
		\OCP\Util::addscript('files', 'files');
178
		\OCP\Util::addscript('files', 'keyboardshortcuts');
179
		\OCP\Util::addscript('files', 'navigation');
180
181
		// if IE8 and "?dir=path&view=someview" was specified, reformat the URL to use a hash like "#?dir=path&view=someview"
182
		$isIE8 = $this->request->isUserAgent([Request::USER_AGENT_IE_8]);
183
		if ($isIE8 && ($dir !== '' || $view !== '')) {
184
			$dir = !empty($dir) ? $dir : '/';
185
			$view = !empty($view) ? $view : 'files';
186
			$hash = '#?dir=' . \OCP\Util::encodePath($dir);
187
			if ($view !== 'files') {
188
				$hash .= '&view=' . urlencode($view);
189
			}
190
			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...
191
		}
192
193
		// mostly for the home storage's free space
194
		// FIXME: Make non static
195
		$storageInfo = $this->getStorageInfo();
196
197
		\OCA\Files\App::getNavigationManager()->add(
198
			[
199
				'id' => 'favorites',
200
				'appname' => 'files',
201
				'script' => 'simplelist.php',
202
				'order' => 5,
203
				'name' => $this->l10n->t('Favorites')
204
			]
205
		);
206
207
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
208
		usort($navItems, function($item1, $item2) {
209
			return $item1['order'] - $item2['order'];
210
		});
211
		$nav->assign('navigationItems', $navItems);
212
213
		$contentItems = [];
214
215
		// render the container content for every navigation item
216
		foreach ($navItems as $item) {
217
			$content = '';
218
			if (isset($item['script'])) {
219
				$content = $this->renderScript($item['appname'], $item['script']);
220
			}
221
			$contentItem = [];
222
			$contentItem['id'] = $item['id'];
223
			$contentItem['content'] = $content;
224
			$contentItems[] = $contentItem;
225
		}
226
227
		$this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts');
228
229
		$params = [];
230
		$params['usedSpacePercent'] = (int)$storageInfo['relative'];
231
		$params['owner'] = $storageInfo['owner'];
232
		$params['ownerDisplayName'] = $storageInfo['ownerDisplayName'];
233
		$params['isPublic'] = false;
234
		$params['mailNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no');
235
		$params['mailPublicNotificationEnabled'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no');
236
		$params['allowShareWithLink'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes');
237
		$user = $this->userSession->getUser()->getUID();
238
		$params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name');
239
		$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
240
		$showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
241
		$params['showHiddenFiles'] = $showHidden ? 1 : 0;
242
		$params['appNavigation'] = $nav;
243
		$params['appContents'] = $contentItems;
244
		$this->navigationManager->setActiveEntry('files_index');
245
246
		$response = new TemplateResponse(
247
			$this->appName,
248
			'index',
249
			$params
250
		);
251
		$policy = new ContentSecurityPolicy();
252
		$policy->addAllowedFrameDomain('\'self\'');
253
		$response->setContentSecurityPolicy($policy);
254
255
		return $response;
256
	}
257
258
	/**
259
	 * Redirects to the file list and highlight the given file id
260
	 *
261
	 * @param string $fileId file id to show
262
	 * @return Response redirect response or not found response
263
	 *
264
	 * @NoCSRFRequired
265
	 * @NoAdminRequired
266
	 */
267
	public function showFile($fileId) {
268
		$files = $this->userFolder->getById($fileId);
269
		$params = [];
270
271
		if (!empty($files)) {
272
			$file = current($files);
273
			if ($file instanceof Folder) {
274
				// set the full path to enter the folder
275
				$params['dir'] = $this->userFolder->getRelativePath($file->getPath());
276
			} else {
277
				// set parent path as dir
278
				$params['dir'] = $this->userFolder->getRelativePath($file->getParent()->getPath());
279
				// and scroll to the entry
280
				$params['scrollto'] = $file->getName();
281
			}
282
			return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
283
		}
284
		return new NotFoundResponse();
285
	}
286
}
287