Test Failed
Push — master ( 647c72...cd42b5 )
by
unknown
10:25
created

FilesListModule::createId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
require_once __DIR__ . "/../Files/Core/class.accountstore.php";
3
require_once __DIR__ . "/../Files/Backend/class.backendstore.php";
4
5
require_once __DIR__ . "/../Files/Core/class.exception.php";
6
require_once __DIR__ . "/../Files/Backend/class.exception.php";
7
8
require_once __DIR__ . "/../Files/Core/Util/class.logger.php";
9
require_once __DIR__ . "/../Files/Core/Util/class.stringutil.php";
10
11
require_once __DIR__ . "/../lib/phpfastcache/lib/Phpfastcache/Autoload/Autoload.php";
12
13
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...
14
use \Files\Core\Util\StringUtil;
15
use Phpfastcache\CacheManager;
16
use Phpfastcache\Drivers\Redis\Config as RedisConfig;
17
18
/**
19
 * This module handles all list and change requests for the files browser.
20
 *
21
 * @class FilesListModule
22
 * @extends ListModule
23
 */
24
class FilesListModule extends ListModule
25
{
26
	const LOG_CONTEXT = "FilesListModule"; // Context for the Logger
27
28
	// Unauthorized errors of different backends.
29
	const SMB_ERR_UNAUTHORIZED = 13;
30
	const SMB_ERR_FORBIDDEN = 1;
31
	const FTP_WD_OWNCLOUD_ERR_UNAUTHORIZED = 401;
32
	const FTP_WD_OWNCLOUD_ERR_FORBIDDEN = 403;
33
	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
	{
63
		parent::__construct($id, $data);
64
65
		// Initialize the account and backendstore
66
		$this->accountStore = new \Files\Core\AccountStore();
67
		$this->backendStore = \Files\Backend\BackendStore::getInstance();
68
69
		// Setup the cache
70
		$config = new RedisConfig();
71
		$config->setHost(PLUGIN_FILES_REDIS_HOST);
72
		$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

72
		$config->setPort(/** @scrutinizer ignore-type */ PLUGIN_FILES_REDIS_PORT);
Loading history...
73
		$config->setPassword(PLUGIN_FILES_REDIS_AUTH);
74
75
		$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...
76
77
		// For backward compatibility we will check if the Encryption store exists. If not,
78
		// we will fall back to the old way of retrieving the password from the session.
79
		if ( class_exists('EncryptionStore') ) {
80
			// Get the username from the Encryption store
81
			$encryptionStore = \EncryptionStore::getInstance();
82
			$this->uid = $encryptionStore->get('username');
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
	 * @return array return folders array.
99
	 */
100
	function getHierarchyList($isReload = false)
101
	{
102
		$data = array();
103
		$data["item"] = array();
104
		$versions =  $GLOBALS['PluginManager']->getPluginsVersion();
105
		$filesVersion = $versions['files'];
106
107
		// Clear cache when version gets changed and update 'files' version in cache.
108
		if ($isReload ||  version_compare($this->getVersionFromCache('files'), $filesVersion) !== 0) {
109
			$this->clearCache();
110
			$this->setVersionInCache('files', $filesVersion);
111
		}
112
113
		$accounts = $this->accountStore->getAllAccounts();
114
		foreach ($accounts as $account) {
115
			// we have to load all accounts and their folders
116
			// skip accounts that are not valid
117
			if ($account->getStatus() !== \Files\Core\Account::STATUS_OK) {
118
				continue;
119
			}
120
121
			// build the real node id for this folder
122
			$realNodeId = "#R#" . $account->getId() . "/";
123
			$accountName = $account->getName();
124
			$rootId = $this->createId($realNodeId);
125
			$nodes = array(
126
				"store_entryid" => $rootId,
127
				"props" => array(
128
					'entryid' => $rootId,
129
					'subtree_id' => $rootId,
130
					'display_name' => $accountName,
131
					"object_type" => FILES_STORE,
132
					"status" => $account->getStatus(),
133
					"status_description" => $account->getStatusDescription(),
134
					"backend" => $account->getBackend(),
135
					"backend_config" => $account->getBackendConfig(),
136
					'backend_features' => $account->getFeatures(),
137
					'filename' => $accountName,
138
					'account_sequence' => $account->getSequence(),
139
					'cannot_change' => $account->getCannotChangeFlag()
140
				)
141
			);
142
143
			$initializedBackend = $this->initializeBackend($account, true);
144
145
			// Get sub folder of root folder.
146
			$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

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

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