Test Failed
Push — master ( cd42b5...841446 )
by
unknown
16:44 queued 06:09
created

FilesListModule::getHierarchyList()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 88
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 62
c 0
b 0
f 0
nc 6
nop 1
dl 0
loc 88
rs 8.5178

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
require_once __DIR__ . "/../Files/Core/class.accountstore.php";
4
require_once __DIR__ . "/../Files/Backend/class.backendstore.php";
5
6
require_once __DIR__ . "/../Files/Core/class.exception.php";
7
require_once __DIR__ . "/../Files/Backend/class.exception.php";
8
9
require_once __DIR__ . "/../Files/Core/Util/class.logger.php";
10
require_once __DIR__ . "/../Files/Core/Util/class.stringutil.php";
11
12
require_once __DIR__ . "/../lib/phpfastcache/lib/Phpfastcache/Autoload/Autoload.php";
13
14
use Files\Core\Util\Logger;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Logger. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use Files\Core\Util\StringUtil;
16
use Phpfastcache\CacheManager;
17
use Phpfastcache\Drivers\Redis\Config as RedisConfig;
18
19
/**
20
 * This module handles all list and change requests for the files browser.
21
 *
22
 * @class FilesListModule
23
 * @extends ListModule
24
 */
25
class FilesListModule extends ListModule {
26
	public const LOG_CONTEXT = "FilesListModule"; // Context for the Logger
27
28
	// Unauthorized errors of different backends.
29
	public const SMB_ERR_UNAUTHORIZED = 13;
30
	public const SMB_ERR_FORBIDDEN = 1;
31
	public const FTP_WD_OWNCLOUD_ERR_UNAUTHORIZED = 401;
32
	public const FTP_WD_OWNCLOUD_ERR_FORBIDDEN = 403;
33
	public const ALL_BACKEND_ERR_NOTFOUND = 404;
34
35
	/**
36
	 * @var \phpFastCache cache handler
37
	 */
38
	public $cache;
39
40
	/**
41
	 * @var string User id of the currently logged in user. Used to generate unique cache id's.
42
	 */
43
	public $uid;
44
45
	/**
46
	 * @var {Object} The account store holding all available accounts
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Object} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Object}.
Loading history...
47
	 */
48
	public $accountStore;
49
50
	/**
51
	 * @var {Object} The backend store holding all available backends
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Object} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Object}.
Loading history...
52
	 */
53
	public $backendStore;
54
55
	/**
56
	 * @constructor
57
	 *
58
	 * @param $id
59
	 * @param $data
60
	 */
61
	public function __construct($id, $data) {
62
		parent::__construct($id, $data);
63
64
		// Initialize the account and backendstore
65
		$this->accountStore = new \Files\Core\AccountStore();
66
		$this->backendStore = \Files\Backend\BackendStore::getInstance();
67
68
		// Setup the cache
69
		$config = new RedisConfig();
70
		$config->setHost(PLUGIN_FILES_REDIS_HOST);
71
		$config->setPort(PLUGIN_FILES_REDIS_PORT);
0 ignored issues
show
Bug introduced by
PLUGIN_FILES_REDIS_PORT of type string is incompatible with the type integer expected by parameter $port of Phpfastcache\Drivers\Redis\Config::setPort(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
		$config->setPort(/** @scrutinizer ignore-type */ PLUGIN_FILES_REDIS_PORT);
Loading history...
72
		$config->setPassword(PLUGIN_FILES_REDIS_AUTH);
73
74
		$this->cache = CacheManager::getInstance('Redis', $config);
0 ignored issues
show
Documentation Bug introduced by
It seems like Phpfastcache\CacheManage...tance('Redis', $config) of type Phpfastcache\Core\Pool\E...dCacheItemPoolInterface is incompatible with the declared type phpFastCache of property $cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
75
76
		// For backward compatibility we will check if the Encryption store exists. If not,
77
		// we will fall back to the old way of retrieving the password from the session.
78
		if (class_exists('EncryptionStore')) {
79
			// Get the username from the Encryption store
80
			$encryptionStore = \EncryptionStore::getInstance();
81
			$this->uid = $encryptionStore->get('username');
82
		}
83
		else {
84
			$this->uid = $_SESSION["username"];
85
		}
86
		// As of the V6, the following characters can not longer being a part of the key identifier: {}()/\@:
87
		// If you try to do so, an \phpFastCache\Exceptions\phpFastCacheInvalidArgumentException will be raised.
88
		// You must replace them with a safe delimiter such as .|-_
89
		// @see https://github.com/PHPSocialNetwork/phpfastcache/blob/8.1.2/docs/migration/MigratingFromV5ToV6.md
90
		$this->uid = str_replace(['{', '}', '(', ')', '/', '\\', '@'], '_', $this->uid);
91
92
		Logger::debug(self::LOG_CONTEXT, "[constructor]: executing the module as uid: " . $this->uid);
93
	}
94
95
	/**
96
	 * Function get the folder data from backend.
97
	 *
98
	 * @param mixed $isReload
99
	 *
100
	 * @return array return folders array
101
	 */
102
	public function getHierarchyList($isReload = false) {
103
		$data = [];
104
		$data["item"] = [];
105
		$versions = $GLOBALS['PluginManager']->getPluginsVersion();
106
		$filesVersion = $versions['files'];
107
108
		// Clear cache when version gets changed and update 'files' version in cache.
109
		if ($isReload || version_compare($this->getVersionFromCache('files'), $filesVersion) !== 0) {
110
			$this->clearCache();
111
			$this->setVersionInCache('files', $filesVersion);
112
		}
113
114
		$accounts = $this->accountStore->getAllAccounts();
115
		foreach ($accounts as $account) {
116
			// we have to load all accounts and their folders
117
			// skip accounts that are not valid
118
			if ($account->getStatus() !== \Files\Core\Account::STATUS_OK) {
119
				continue;
120
			}
121
122
			// build the real node id for this folder
123
			$realNodeId = "#R#" . $account->getId() . "/";
124
			$accountName = $account->getName();
125
			$rootId = $this->createId($realNodeId);
126
			$nodes = [
127
				"store_entryid" => $rootId,
128
				"props" => [
129
					'entryid' => $rootId,
130
					'subtree_id' => $rootId,
131
					'display_name' => $accountName,
132
					"object_type" => FILES_STORE,
133
					"status" => $account->getStatus(),
134
					"status_description" => $account->getStatusDescription(),
135
					"backend" => $account->getBackend(),
136
					"backend_config" => $account->getBackendConfig(),
137
					'backend_features' => $account->getFeatures(),
138
					'filename' => $accountName,
139
					'account_sequence' => $account->getSequence(),
140
					'cannot_change' => $account->getCannotChangeFlag(),
141
				],
142
			];
143
144
			$initializedBackend = $this->initializeBackend($account, true);
145
146
			// Get sub folder of root folder.
147
			$subFolders = $this->getSubFolders($realNodeId, $initializedBackend);
0 ignored issues
show
Bug introduced by
$initializedBackend of type Files\Backend\AbstractBackend is incompatible with the type array expected by parameter $backend of FilesListModule::getSubFolders(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

147
			$subFolders = $this->getSubFolders($realNodeId, /** @scrutinizer ignore-type */ $initializedBackend);
Loading history...
148
149
			array_push($subFolders, [
150
				'id' => $realNodeId,
151
				'folder_id' => $realNodeId,
152
				'entryid' => $rootId,
153
				'parent_entryid' => $rootId,
154
				'store_entryid' => $rootId,
155
				'props' => [
156
					'path' => $realNodeId,
157
					'icon_index' => ICON_FOLDER,
158
					// Fixme : remove text property. we have to use display_name property.
159
					'text' => $accountName,
160
					'has_subfolder' => empty($subFolders) === false,
161
					'object_type' => FILES_FOLDER,
162
					'filename' => $accountName,
163
					'display_name' => $accountName,
164
				],
165
			]);
166
167
			// TODO: dummy folder which used client side to show the account view when user
168
			//  switch to home folder using navigation bar.
169
			array_push($subFolders, [
170
				'id' => "#R#",
171
				'folder_id' => "#R#",
172
				'entryid' => "#R#",
173
				'parent_entryid' => $rootId,
174
				'store_entryid' => $rootId,
175
				'props' => [
176
					'path' => $realNodeId,
177
					'icon_index' => ICON_HOME_FOLDER,
178
					'text' => "Files",
179
					'has_subfolder' => false,
180
					'object_type' => FILES_FOLDER,
181
					'filename' => "Files",
182
					'display_name' => "Files",
183
				],
184
			]);
185
			$nodes["folders"] = ["item" => $subFolders];
186
			array_push($data["item"], $nodes);
187
		}
188
189
		return $data;
190
	}
191
192
	/**
193
	 * Function used to get the sub folders of the given folder id.
194
	 *
195
	 * @param string $nodeId    the folder id which used to get sub folders
196
	 * @param array  $backend   The backend which used to retrieve the folders
197
	 * @param bool   $recursive the recursive true which get the sub folder recursively
198
	 * @param array  $nodes     the nodes contains the array of nodes
199
	 *
200
	 * @return array return the array folders
201
	 */
202
	public function getSubFolders($nodeId, $backend, $recursive = false, $nodes = []) {
203
		// relative node ID. We need to trim off the #R# and account ID
204
		$relNodeId = substr($nodeId, strpos($nodeId, '/'));
205
		$nodeIdPrefix = substr($nodeId, 0, strpos($nodeId, '/'));
206
207
		$accountID = $backend->getAccountID();
208
209
		// remove the trailing slash for the cache key
210
		$cachePath = rtrim($relNodeId, '/');
211
		if ($cachePath === "") {
212
			$cachePath = "/";
213
		}
214
215
		$backendDisplayName = $backend->backendDisplayName;
216
		$backendVersion = $backend->backendVersion;
217
		$cacheVersion = $this->getVersionFromCache($backendDisplayName, $accountID);
218
		$dir = $this->getCache($accountID, $cachePath);
219
220
		// Get new data from backend when cache is empty or the version of backend got changed.
221
		if (is_null($dir) || version_compare($backendVersion, $cacheVersion) !== 0) {
222
			$this->setVersionInCache($backendDisplayName, $backendVersion, $accountID);
223
			$dir = $backend->ls($relNodeId);
224
		}
225
226
		if ($dir) {
227
			$updateCache = false;
228
			foreach ($dir as $id => $node) {
229
				$objectType = strcmp($node['resourcetype'], "collection") !== 0 ? FILES_FILE : FILES_FOLDER;
230
231
				// Only get the Folder item.
232
				if ($objectType !== FILES_FOLDER) {
233
					continue;
234
				}
235
236
				// Check if foldernames have a trailing slash, if not, add one!
237
				if (!StringUtil::endsWith($id, "/")) {
238
					unset($dir[$id]);
239
					$id .= "/";
240
					$dir[$id] = $node;
241
				}
242
243
				$size = $node['getcontentlength'] === null ? -1 : intval($node['getcontentlength']);
244
				// folder's dont have a size
245
				$size = $objectType == FILES_FILE ? $size : -1;
246
247
				$realID = $nodeIdPrefix . $id;
248
				$filename = stringToUTF8Encode(basename($id));
249
250
				if (!isset($node['entryid']) || !isset($node['parent_entryid']) || !isset($node['store_entryid'])) {
251
					$parentNode = $this->getParentNode($cachePath, $accountID);
252
253
					$entryid = $this->createId($realID);
254
					$parentEntryid = $parentNode !== false && isset($parentNode['entryid']) ? $parentNode['entryid'] : $this->createId($nodeId);
255
					$storeEntryid = $this->createId($nodeIdPrefix . '/');
256
257
					$dir[$id]['entryid'] = $entryid;
258
					$dir[$id]['parent_entryid'] = $parentEntryid;
259
					$dir[$id]['store_entryid'] = $storeEntryid;
260
261
					$updateCache = true;
262
				}
263
				else {
264
					$entryid = $node['entryid'];
265
					$parentEntryid = $node['parent_entryid'];
266
					$storeEntryid = $node['store_entryid'];
267
				}
268
269
				$nodeHasSubFolder = $this->hasSubFolder($id, $accountID, $backend);
270
				// Skip displaying folder whose data is unaccesable.
271
				// Also update the cache.
272
				if (is_null($nodeHasSubFolder)) {
273
					unset($dir[$id]);
274
					$updateCache = true;
275
				}
276
				else {
277
					array_push($nodes, [
278
						'id' => $realID,
279
						'folder_id' => $realID,
280
						'entryid' => $entryid,
281
						'parent_entryid' => $parentEntryid,
282
						'store_entryid' => $storeEntryid,
283
						'props' => [
284
							'path' => $nodeId,
285
							'message_size' => $size,
286
							'text' => $filename,
287
							'object_type' => $objectType,
288
							'icon_index' => ICON_FOLDER,
289
							'filename' => $filename,
290
							'display_name' => $filename,
291
							'lastmodified' => strtotime($node['getlastmodified']) * 1000,
292
							'has_subfolder' => $nodeHasSubFolder,
293
						],
294
					]);
295
				}
296
297
				// We need to call this function recursively when user rename the folder.
298
				// we have to send all sub folder as server side notification so grommunio Web
299
				// can update the sub folder as per it's parent folder is renamed.
300
				if ($objectType === FILES_FOLDER && $recursive) {
301
					$nodes = $this->getSubFolders($realID, $backend, true, $nodes);
302
				}
303
			}
304
305
			if ($updateCache) {
306
				$this->setCache($accountID, $cachePath, $dir);
307
			}
308
		}
309
310
		return $nodes;
311
	}
312
313
	/**
314
	 * Function which used to get the parent folder of selected folder.
315
	 *
316
	 * @param string $cachePath the cache path of selected folder
317
	 * @param string $accountID the account ID in which folder is belongs
318
	 *
319
	 * @return array|bool return the parent folder data else false
320
	 */
321
	public function getParentNode($cachePath, $accountID) {
322
		$parentNode = dirname($cachePath, 1);
323
324
		// remove the trailing slash for the cache key
325
		$parentNode = rtrim($parentNode, '/');
326
		if ($parentNode === "") {
327
			$parentNode = "/";
328
		}
329
		$dir = $this->getCache($accountID, $parentNode);
330
331
		if (!is_null($dir) && isset($dir[$cachePath . '/'])) {
332
			return $dir[$cachePath . '/'];
333
		}
334
335
		return false;
336
	}
337
338
	/**
339
	 * Function create the unique id.
340
	 *
341
	 * @param {string} $id The folder id
0 ignored issues
show
Documentation Bug introduced by
The doc comment {string} at position 0 could not be parsed: Unknown type name '{' at position 0 in {string}.
Loading history...
342
	 *
343
	 * @return return generated a hash value
344
	 */
345
	public function createId($id) {
346
		return hash("tiger192,3", $id);
0 ignored issues
show
Bug Best Practice introduced by
The expression return hash('tiger192,3', $id) returns the type string which is incompatible with the documented return type return.
Loading history...
347
	}
348
349
	/**
350
	 * Function will check that given folder has sub folder or not.
351
	 * This will retrurn null when there's an exception retrieving folder data.
352
	 *
353
	 * @param {String} $id The $id is id of selected folder
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
354
	 * @param $accountID
355
	 * @param $backend
356
	 *
357
	 * @return bool or null when unable to access folder data
358
	 */
359
	public function hasSubFolder($id, $accountID, $backend) {
360
		$cachePath = rtrim($id, '/');
361
		if ($cachePath === "") {
362
			$cachePath = "/";
363
		}
364
365
		$dir = $this->getCache($accountID, $cachePath);
366
		if (is_null($dir)) {
367
			try {
368
				$dir = $backend->ls($id);
369
				$this->setCache($accountID, $cachePath, $dir);
370
			}
371
			catch (Exception $e) {
372
				$errorCode = $e->getCode();
373
374
				// If folder not found or folder doesn't have enough access then don't display that folder.
375
				if ($errorCode === self::SMB_ERR_UNAUTHORIZED ||
376
				$errorCode === self::SMB_ERR_FORBIDDEN ||
377
				$errorCode === self::FTP_WD_OWNCLOUD_ERR_UNAUTHORIZED ||
378
				$errorCode === self::FTP_WD_OWNCLOUD_ERR_FORBIDDEN ||
379
				$errorCode === self::ALL_BACKEND_ERR_NOTFOUND) {
380
					if ($errorCode === self::ALL_BACKEND_ERR_NOTFOUND) {
381
						Logger::error(self::LOG_CONTEXT, '[hasSubFolder]: folder ' . $id . ' not found');
382
					}
383
					else {
384
						Logger::error(self::LOG_CONTEXT, '[hasSubFolder]: Access denied for folder ' . $id);
385
					}
386
387
					return null;
388
				}
389
				// rethrow exception if its not related to access permission.
390
				throw $e;
391
			}
392
		}
393
394
		if ($dir) {
395
			foreach ($dir as $id => $node) {
0 ignored issues
show
introduced by
$id is overwriting one of the parameters of this function.
Loading history...
396
				if (strcmp($node['resourcetype'], "collection") === 0) {
397
					// we have a folder
398
					return true;
399
				}
400
			}
401
		}
402
403
		return false;
404
	}
405
406
	/**
407
	 * @param $actionType
408
	 * @param $actionData
409
	 *
410
	 * @throws \Files\Backend\Exception
411
	 */
412
	public function save($actionData) {
413
		$response = [];
414
		$props = $actionData["props"];
415
		$messageProps = [];
416
		if (isset($actionData["entryid"]) && empty($actionData["entryid"])) {
417
			$path = isset($props['path']) && !empty($props['path']) ? $props['path'] : "/";
418
419
			$relDirname = substr($path, strpos($path, '/'));
420
			$relDirname = $relDirname . $props["display_name"] . '/';
421
			$account = $this->accountFromNode($path);
422
423
			// initialize the backend
424
			$initializedBackend = $this->initializeBackend($account, true);
425
			$relDirname = stringToUTF8Encode($relDirname);
426
			$result = $initializedBackend->mkcol($relDirname); // create it !
427
428
			$filesPath = substr($path, strpos($path, '/'));
429
			$dir = $initializedBackend->ls($filesPath);
430
431
			$id = $path . $props["display_name"] . '/';
432
433
			$actionId = $account->getId();
434
435
			$entryid = $this->createId($id);
436
			$parentEntryid = $actionData["parent_entryid"];
437
			$storeEntryid = $this->createId('#R#' . $actionId . '/');
438
439
			$cachePath = rtrim($relDirname, '/');
440
			if ($cachePath === "") {
441
				$cachePath = "/";
442
			}
443
444
			if (isset($dir[$relDirname]) && !empty($dir[$relDirname])) {
445
				$newDir = $dir[$relDirname];
446
				$newDir['entryid'] = $entryid;
447
				$newDir['parent_entryid'] = $parentEntryid;
448
				$newDir['store_entryid'] = $storeEntryid;
449
450
				// Get old cached data.
451
				$cachedDir = $this->getCache($actionId, dirname($cachePath, 1));
452
453
				// Insert newly created folder info with entryid, parentEntryid and storeEntryid
454
				// in already cached data.
455
				$cachedDir[$relDirname] = $newDir;
456
				$dir = $cachedDir;
457
			}
458
459
			// Delete old cache.
460
			$this->deleteCache($actionId, dirname($relDirname));
461
462
			// Set data in cache.
463
			$this->setCache($actionId, dirname($relDirname), $dir);
464
465
			if ($result) {
466
				$folder = [
467
					'props' => [
468
						'path' => $path,
469
						'filename' => $props["display_name"],
470
						'display_name' => $props["display_name"],
471
						'text' => $props["display_name"],
472
						'object_type' => $props['object_type'],
473
						'has_subfolder' => false,
474
					],
475
					'id' => rawurldecode($id),
476
					'folder_id' => rawurldecode($id),
477
					'entryid' => $entryid,
478
					'parent_entryid' => $parentEntryid,
479
					'store_entryid' => $storeEntryid,
480
				];
481
				$response = $folder;
482
			}
483
		}
484
		else {
485
			// Rename/update the folder/file name
486
			$folderId = $actionData['message_action']["source_folder_id"];
487
			// rename/update the folder or files name.
488
			$parentEntryid = $actionData["parent_entryid"];
489
490
			$isfolder = "";
491
			if (substr($folderId, -1) == '/') {
492
				$isfolder = "/"; // we have a folder...
493
			}
494
495
			$src = rtrim($folderId, '/');
496
			$dstdir = dirname($src) == "/" ? "" : dirname($src);
497
			$dst = $dstdir . "/" . rtrim($props['filename'], '/');
498
499
			$relDst = substr($dst, strpos($dst, '/'));
500
			$relSrc = substr($src, strpos($src, '/'));
501
502
			$account = $this->accountFromNode($src);
503
504
			// initialize the backend
505
			$initializedBackend = $this->initializeBackend($account);
506
507
			$result = $initializedBackend->move($relSrc, $relDst, false);
508
509
			// get the cache data of parent directory.
510
			$dir = $this->getCache($account->getId(), dirname($relSrc));
511
			if (isset($dir[$relSrc . "/"]) && !empty($dir[$relSrc . "/"])) {
512
				$srcDir = $dir[$relSrc . "/"];
513
				unset($dir[$relSrc . "/"]);
514
				$dir[$relDst . "/"] = $srcDir;
515
516
				// Update only rename folder info in php cache.
517
				$this->setCache($account->getId(), dirname($relSrc), $dir);
518
519
				$this->updateCacheAfterRename($relSrc, $relDst, $account->getId());
520
			}
521
			else {
522
				// clear the cache
523
				$this->deleteCache($account->getId(), dirname($relSrc));
524
			}
525
526
			if ($result) {
527
				/* create the response object */
528
				$folder = [];
529
530
				// some requests might not contain a new filename... so dont update the store
531
				if (isset($props['filename'])) {
532
					$folder = [
533
						'props' => [
534
							'folder_id' => rawurldecode($dst . $isfolder),
535
							'path' => rawurldecode($dstdir),
536
							'filename' => $props['filename'],
537
							'display_name' => $props['filename'],
538
						],
539
						'entryid' => $actionData["entryid"],
540
						'parent_entryid' => $parentEntryid,
541
						'store_entryid' => $actionData["store_entryid"],
542
					];
543
				}
544
				$response['item'] = $folder;
545
				$messageProps = $folder;
546
			}
547
		}
548
549
		$this->addActionData("update", $response);
550
		$GLOBALS["bus"]->addData($this->getResponseData());
551
552
		return $messageProps;
553
	}
554
555
	/**
556
	 * Update the cache of renamed folder and it's sub folders.
557
	 *
558
	 * @param {String} $oldPath The $oldPath is path of folder before rename
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
559
	 * @param {String} $newPath The $newPath is path of folder after rename
560
	 * @param {String} $accountId The id of an account in which renamed folder is belongs
561
	 */
562
	public function updateCacheAfterRename($oldPath, $newPath, $accountId) {
563
		// remove the trailing slash for the cache key
564
		$cachePath = rtrim($oldPath, '/');
565
		if ($cachePath === "") {
566
			$cachePath = "/";
567
		}
568
569
		$dir = $this->getCache($accountId, $cachePath);
570
		if ($dir) {
571
			foreach ($dir as $id => $node) {
572
				$newId = str_replace(dirname($id), $newPath, $id);
573
				unset($dir[$id]);
574
				$dir[$newId] = $node;
575
576
				$type = FILES_FILE;
577
578
				if (strcmp($node['resourcetype'], "collection") == 0) { // we have a folder
579
					$type = FILES_FOLDER;
580
				}
581
582
				if ($type === FILES_FOLDER) {
583
					$this->updateCacheAfterRename($id, rtrim($newId, '/'), $accountId);
584
				}
585
			}
586
			$this->deleteCache($accountId, $cachePath);
587
			$this->setCache($accountId, $newPath, $dir);
588
		}
589
	}
590
591
	/**
592
	 * Function used to notify the sub folder of selected/modified folder.
593
	 *
594
	 * @param {String} $folderID The $folderID of a folder which is modified
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
595
	 */
596
	public function notifySubFolders($folderID) {
597
		$account = $this->accountFromNode($folderID);
598
		$initializedBackend = $this->initializeBackend($account, true);
599
		$folderData = $this->getSubFolders($folderID, $initializedBackend, true);
0 ignored issues
show
Bug introduced by
$initializedBackend of type Files\Backend\AbstractBackend is incompatible with the type array expected by parameter $backend of FilesListModule::getSubFolders(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

599
		$folderData = $this->getSubFolders($folderID, /** @scrutinizer ignore-type */ $initializedBackend, true);
Loading history...
600
601
		if (!empty($folderData)) {
602
			$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_SAVE, $folderData);
603
		}
604
	}
605
606
	/**
607
	 * Get the account id from a node id.
608
	 *
609
	 * @param {String} $nodeID Id of the file or folder to operate on
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
610
	 *
611
	 * @return {String} The account id extracted from $nodeId
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
612
	 */
613
	public function accountIDFromNode($nodeID) {
614
		return substr($nodeID, 3, (strpos($nodeID, '/') - 3)); // parse account id from node id
615
	}
616
617
	/**
618
	 * Get the account from a node id.
619
	 *
620
	 * @param {String} $nodeId ID of the file or folder to operate on
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
621
	 * @param mixed $nodeID
622
	 *
623
	 * @return {String} The account for $nodeId
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
624
	 */
625
	public function accountFromNode($nodeID) {
626
		return $this->accountStore->getAccount($this->accountIDFromNode($nodeID));
627
	}
628
629
	/**
630
	 * Create a key used to store data in the cache.
631
	 *
632
	 * @param {String} $accountID Id of the account of the data to cache
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
633
	 * @param {String} $path Path of the file or folder to create the cache element for
634
	 *
635
	 * @return {String} The created key
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
636
	 */
637
	public function makeCacheKey($accountID, $path) {
638
		return $this->uid . md5($accountID . $path);
639
	}
640
641
	/**
642
	 * Get version data form the cache.
643
	 *
644
	 * @param {String} $displayName display name of the backend or file plugin
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
645
	 * @param {String} $accountID Id of the account of the data to cache
646
	 *
647
	 * @return {String} version data or null if nothing was found
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
648
	 */
649
	public function getVersionFromCache($displayName, $accountID = '') {
650
		$key = $this->uid . $accountID . $displayName;
651
652
		return $this->cache->getItem($key)->get();
653
	}
654
655
	/**
656
	 * Set version data in the cache only when version data has been changed.
657
	 *
658
	 * @param {String} $displayName display name of the backend or file plugin
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
659
	 * @param {String} $version version info of backend or file plugin which needs to be cached
660
	 * @param {String} $accountID Id of the account of the data to cache
661
	 */
662
	public function setVersionInCache($displayName, $version, $accountID = '') {
663
		$olderVersionFromCache = $this->getVersionFromCache($displayName, $accountID);
664
		// If version of files/backend is same then return.
665
		if (version_compare($olderVersionFromCache, $version) === 0) {
666
			return;
667
		}
668
669
		$key = $this->uid . $accountID . $displayName;
670
		$this->cache->save($this->cache->getItem($key)->set($version));
671
	}
672
673
	/**
674
	 * Initialize the backend for the given account.
675
	 *
676
	 * @param {Object} $account The account object the backend should be initialized for
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Object} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Object}.
Loading history...
677
	 * @param {Bool} $setID Should the accountID be set in the backend object, or not. Defaults to false.
678
	 *
679
	 * @return {Object} The initialized backend
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Object} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Object}.
Loading history...
680
	 */
681
	public function initializeBackend($account, $setID = false) {
682
		$backend = $this->backendStore->getInstanceOfBackend($account->getBackend());
683
		$backend->init_backend($account->getBackendConfig());
684
		if ($setID) {
685
			$backend->setAccountID($account->getId());
686
		}
687
		$backend->open();
688
689
		return $backend;
690
	}
691
692
	/**
693
	 * Save directory data in the cache.
694
	 *
695
	 * @param {String} $accountID Id of the account of the data to cache
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
696
	 * @param {String} $path Path of the file or folder to create the cache element for
697
	 * @param {String} $data Data to be cached
698
	 */
699
	public function setCache($accountID, $path, $data) {
700
		$key = $this->makeCacheKey($accountID, $path);
701
		Logger::debug(self::LOG_CONTEXT, "Setting cache for node: " . $accountID . $path . " ## " . $key);
702
		$this->cache->save($this->cache->getItem($key)->set($data));
703
	}
704
705
	/**
706
	 * Get directotry data form the cache.
707
	 *
708
	 * @param {String} $accountID Id of the account of the data to get
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
709
	 * @param {String} $path Path of the file or folder to retrieve the cache element for
710
	 *
711
	 * @return {String} The directory data or null if nothing was found
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
712
	 */
713
	public function getCache($accountID, $path) {
714
		$key = $this->makeCacheKey($accountID, $path);
715
		Logger::debug(self::LOG_CONTEXT, "Getting cache for node: " . $accountID . $path . " ## " . $key);
716
717
		return $this->cache->getItem($key)->get();
718
	}
719
720
	/**
721
	 * Remove data from the cache.
722
	 *
723
	 * @param {String} $accountID Id of the account to delete the cache for
0 ignored issues
show
Documentation Bug introduced by
The doc comment {String} at position 0 could not be parsed: Unknown type name '{' at position 0 in {String}.
Loading history...
724
	 * @param {String} $path Path of the file or folder to delete the cache element
725
	 */
726
	public function deleteCache($accountID, $path) {
727
		$key = $this->makeCacheKey($accountID, $path);
728
		Logger::debug(self::LOG_CONTEXT, "Removing cache for node: " . $accountID . $path . " ## " . $key);
729
		$this->cache->deleteItem($key);
730
	}
731
732
	/**
733
	 * Function clear the cache.
734
	 */
735
	public function clearCache() {
736
		$this->cache->clear();
737
	}
738
739
	/**
740
	 * Function which returns MAPI Message Store Object. It
741
	 * searches in the variable $action for a storeid.
742
	 *
743
	 * @param array $action the XML data retrieved from the client
744
	 *
745
	 * @return object MAPI Message Store Object, false if storeid is not found in the $action variable
746
	 */
747
	public function getActionStore($action) {
748
		$store = false;
749
750
		if (isset($action["store_entryid"]) && !empty($action["store_entryid"])) {
751
			if (is_array($action["store_entryid"])) {
752
				$store = [];
753
				foreach ($action["store_entryid"] as $store_id) {
754
					array_push($store, $store_id);
755
				}
756
			}
757
			else {
758
				$store = $action["store_entryid"];
759
			}
760
		}
761
762
		return $store;
763
	}
764
}
765