Completed
Pull Request — master (#3838)
by Vars
12:18
created
apps/files_versions/lib/Storage.php 1 patch
Indentation   +776 added lines, -776 removed lines patch added patch discarded remove patch
@@ -53,781 +53,781 @@
 block discarded – undo
53 53
 
54 54
 class Storage {
55 55
 
56
-	const DEFAULTENABLED=true;
57
-	const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
58
-	const VERSIONS_ROOT = 'files_versions/';
59
-
60
-	const DELETE_TRIGGER_MASTER_REMOVED = 0;
61
-	const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1;
62
-	const DELETE_TRIGGER_QUOTA_EXCEEDED = 2;
63
-
64
-	// files for which we can remove the versions after the delete operation was successful
65
-	private static $deletedFiles = array();
66
-
67
-	private static $sourcePathAndUser = array();
68
-
69
-	private static $max_versions_per_interval = array(
70
-		//first 10sec, one version every 2sec
71
-		1 => array('intervalEndsAfter' => 10,      'step' => 2),
72
-		//next minute, one version every 10sec
73
-		2 => array('intervalEndsAfter' => 60,      'step' => 10),
74
-		//next hour, one version every minute
75
-		3 => array('intervalEndsAfter' => 3600,    'step' => 60),
76
-		//next 24h, one version every hour
77
-		4 => array('intervalEndsAfter' => 86400,   'step' => 3600),
78
-		//next 30days, one version per day
79
-		5 => array('intervalEndsAfter' => 2592000, 'step' => 86400),
80
-		//until the end one version per week
81
-		6 => array('intervalEndsAfter' => -1,      'step' => 604800),
82
-	);
83
-
84
-	/** @var \OCA\Files_Versions\AppInfo\Application */
85
-	private static $application;
86
-
87
-	/**
88
-	 * get the UID of the owner of the file and the path to the file relative to
89
-	 * owners files folder
90
-	 *
91
-	 * @param string $filename
92
-	 * @return array
93
-	 * @throws \OC\User\NoUserException
94
-	 */
95
-	public static function getUidAndFilename($filename) {
96
-		$uid = Filesystem::getOwner($filename);
97
-		$userManager = \OC::$server->getUserManager();
98
-		// if the user with the UID doesn't exists, e.g. because the UID points
99
-		// to a remote user with a federated cloud ID we use the current logged-in
100
-		// user. We need a valid local user to create the versions
101
-		if (!$userManager->userExists($uid)) {
102
-			$uid = User::getUser();
103
-		}
104
-		Filesystem::initMountPoints($uid);
105
-		if ( $uid != User::getUser() ) {
106
-			$info = Filesystem::getFileInfo($filename);
107
-			$ownerView = new View('/'.$uid.'/files');
108
-			try {
109
-				$filename = $ownerView->getPath($info['fileid']);
110
-				// make sure that the file name doesn't end with a trailing slash
111
-				// can for example happen single files shared across servers
112
-				$filename = rtrim($filename, '/');
113
-			} catch (NotFoundException $e) {
114
-				$filename = null;
115
-			}
116
-		}
117
-		return [$uid, $filename];
118
-	}
119
-
120
-	/**
121
-	 * Remember the owner and the owner path of the source file
122
-	 *
123
-	 * @param string $source source path
124
-	 */
125
-	public static function setSourcePathAndUser($source) {
126
-		list($uid, $path) = self::getUidAndFilename($source);
127
-		self::$sourcePathAndUser[$source] = array('uid' => $uid, 'path' => $path);
128
-	}
129
-
130
-	/**
131
-	 * Gets the owner and the owner path from the source path
132
-	 *
133
-	 * @param string $source source path
134
-	 * @return array with user id and path
135
-	 */
136
-	public static function getSourcePathAndUser($source) {
137
-
138
-		if (isset(self::$sourcePathAndUser[$source])) {
139
-			$uid = self::$sourcePathAndUser[$source]['uid'];
140
-			$path = self::$sourcePathAndUser[$source]['path'];
141
-			unset(self::$sourcePathAndUser[$source]);
142
-		} else {
143
-			$uid = $path = false;
144
-		}
145
-		return array($uid, $path);
146
-	}
147
-
148
-	/**
149
-	 * get current size of all versions from a given user
150
-	 *
151
-	 * @param string $user user who owns the versions
152
-	 * @return int versions size
153
-	 */
154
-	private static function getVersionsSize($user) {
155
-		$view = new View('/' . $user);
156
-		$fileInfo = $view->getFileInfo('/files_versions');
157
-		return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
158
-	}
159
-
160
-	/**
161
-	 * store a new version of a file.
162
-	 */
163
-	public static function store($filename) {
164
-		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
165
-
166
-			// if the file gets streamed we need to remove the .part extension
167
-			// to get the right target
168
-			$ext = pathinfo($filename, PATHINFO_EXTENSION);
169
-			if ($ext === 'part') {
170
-				$filename = substr($filename, 0, strlen($filename) - 5);
171
-			}
172
-
173
-			// we only handle existing files
174
-			if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
175
-				return false;
176
-			}
177
-
178
-			list($uid, $filename) = self::getUidAndFilename($filename);
179
-
180
-			$files_view = new View('/'.$uid .'/files');
181
-			$users_view = new View('/'.$uid);
182
-
183
-			// no use making versions for empty files
184
-			if ($files_view->filesize($filename) === 0) {
185
-				return false;
186
-			}
187
-
188
-			// create all parent folders
189
-			self::createMissingDirectories($filename, $users_view);
190
-
191
-			self::scheduleExpire($uid, $filename);
192
-
193
-			// store a new version of a file
194
-			$mtime = $users_view->filemtime('files/' . $filename);
195
-			$users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime);
196
-			// call getFileInfo to enforce a file cache entry for the new version
197
-			$users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
198
-		}
199
-	}
200
-
201
-
202
-	/**
203
-	 * mark file as deleted so that we can remove the versions if the file is gone
204
-	 * @param string $path
205
-	 */
206
-	public static function markDeletedFile($path) {
207
-		list($uid, $filename) = self::getUidAndFilename($path);
208
-		self::$deletedFiles[$path] = array(
209
-			'uid' => $uid,
210
-			'filename' => $filename);
211
-	}
212
-
213
-	/**
214
-	 * delete the version from the storage and cache
215
-	 *
216
-	 * @param View $view
217
-	 * @param string $path
218
-	 */
219
-	protected static function deleteVersion($view, $path) {
220
-		$view->unlink($path);
221
-		/**
222
-		 * @var \OC\Files\Storage\Storage $storage
223
-		 * @var string $internalPath
224
-		 */
225
-		list($storage, $internalPath) = $view->resolvePath($path);
226
-		$cache = $storage->getCache($internalPath);
227
-		$cache->remove($internalPath);
228
-	}
229
-
230
-	/**
231
-	 * Delete versions of a file
232
-	 */
233
-	public static function delete($path) {
234
-
235
-		$deletedFile = self::$deletedFiles[$path];
236
-		$uid = $deletedFile['uid'];
237
-		$filename = $deletedFile['filename'];
238
-
239
-		if (!Filesystem::file_exists($path)) {
240
-
241
-			$view = new View('/' . $uid . '/files_versions');
242
-
243
-			$versions = self::getVersions($uid, $filename);
244
-			if (!empty($versions)) {
245
-				foreach ($versions as $v) {
246
-					\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED));
247
-					self::deleteVersion($view, $filename . '.v' . $v['version']);
248
-					\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED));
249
-				}
250
-			}
251
-		}
252
-		unset(self::$deletedFiles[$path]);
253
-	}
254
-
255
-	/**
256
-	 * Rename or copy versions of a file of the given paths
257
-	 *
258
-	 * @param string $sourcePath source path of the file to move, relative to
259
-	 * the currently logged in user's "files" folder
260
-	 * @param string $targetPath target path of the file to move, relative to
261
-	 * the currently logged in user's "files" folder
262
-	 * @param string $operation can be 'copy' or 'rename'
263
-	 */
264
-	public static function renameOrCopy($sourcePath, $targetPath, $operation) {
265
-		list($sourceOwner, $sourcePath) = self::getSourcePathAndUser($sourcePath);
266
-
267
-		// it was a upload of a existing file if no old path exists
268
-		// in this case the pre-hook already called the store method and we can
269
-		// stop here
270
-		if ($sourcePath === false) {
271
-			return true;
272
-		}
273
-
274
-		list($targetOwner, $targetPath) = self::getUidAndFilename($targetPath);
275
-
276
-		$sourcePath = ltrim($sourcePath, '/');
277
-		$targetPath = ltrim($targetPath, '/');
278
-
279
-		$rootView = new View('');
280
-
281
-		// did we move a directory ?
282
-		if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
283
-			// does the directory exists for versions too ?
284
-			if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) {
285
-				// create missing dirs if necessary
286
-				self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
287
-
288
-				// move the directory containing the versions
289
-				$rootView->$operation(
290
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath,
291
-					'/' . $targetOwner . '/files_versions/' . $targetPath
292
-				);
293
-			}
294
-		} else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) {
295
-			// create missing dirs if necessary
296
-			self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
297
-
298
-			foreach ($versions as $v) {
299
-				// move each version one by one to the target directory
300
-				$rootView->$operation(
301
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
302
-					'/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
303
-				);
304
-			}
305
-		}
306
-
307
-		// if we moved versions directly for a file, schedule expiration check for that file
308
-		if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
309
-			self::scheduleExpire($targetOwner, $targetPath);
310
-		}
311
-
312
-	}
313
-
314
-	/**
315
-	 * Rollback to an old version of a file.
316
-	 *
317
-	 * @param string $file file name
318
-	 * @param int $revision revision timestamp
319
-	 * @return bool
320
-	 */
321
-	public static function rollback($file, $revision) {
322
-
323
-		if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
324
-			// add expected leading slash
325
-			$file = '/' . ltrim($file, '/');
326
-			list($uid, $filename) = self::getUidAndFilename($file);
327
-			if ($uid === null || trim($filename, '/') === '') {
328
-				return false;
329
-			}
330
-
331
-			$users_view = new View('/'.$uid);
332
-			$files_view = new View('/'. User::getUser().'/files');
333
-
334
-			$versionCreated = false;
335
-
336
-			$fileInfo = $files_view->getFileInfo($file);
337
-
338
-			// check if user has the permissions to revert a version
339
-			if (!$fileInfo->isUpdateable()) {
340
-				return false;
341
-			}
342
-
343
-			//first create a new version
344
-			$version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
345
-			if (!$users_view->file_exists($version)) {
346
-				$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
347
-				$versionCreated = true;
348
-			}
349
-
350
-			$fileToRestore =  'files_versions' . $filename . '.v' . $revision;
351
-
352
-			// Restore encrypted version of the old file for the newly restored file
353
-			// This has to happen manually here since the file is manually copied below
354
-			$oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
355
-			$oldFileInfo = $users_view->getFileInfo($fileToRestore);
356
-			$cache = $fileInfo->getStorage()->getCache();
357
-			$cache->update(
358
-				$fileInfo->getId(), [
359
-					'encrypted' => $oldVersion,
360
-					'encryptedVersion' => $oldVersion,
361
-					'size' => $oldFileInfo->getSize()
362
-				]
363
-			);
364
-
365
-			// rollback
366
-			if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
367
-				$files_view->touch($file, $revision);
368
-				Storage::scheduleExpire($uid, $file);
369
-				\OC_Hook::emit('\OCP\Versions', 'rollback', array(
370
-					'path' => $filename,
371
-					'revision' => $revision,
372
-				));
373
-				return true;
374
-			} else if ($versionCreated) {
375
-				self::deleteVersion($users_view, $version);
376
-			}
377
-		}
378
-		return false;
379
-
380
-	}
381
-
382
-	/**
383
-	 * Stream copy file contents from $path1 to $path2
384
-	 *
385
-	 * @param View $view view to use for copying
386
-	 * @param string $path1 source file to copy
387
-	 * @param string $path2 target file
388
-	 *
389
-	 * @return bool true for success, false otherwise
390
-	 */
391
-	private static function copyFileContents($view, $path1, $path2) {
392
-		/** @var \OC\Files\Storage\Storage $storage1 */
393
-		list($storage1, $internalPath1) = $view->resolvePath($path1);
394
-		/** @var \OC\Files\Storage\Storage $storage2 */
395
-		list($storage2, $internalPath2) = $view->resolvePath($path2);
396
-
397
-		$view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
398
-		$view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
399
-
400
-		// TODO add a proper way of overwriting a file while maintaining file ids
401
-		if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
402
-			$source = $storage1->fopen($internalPath1, 'r');
403
-			$target = $storage2->fopen($internalPath2, 'w');
404
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
405
-			fclose($source);
406
-			fclose($target);
407
-
408
-			if ($result !== false) {
409
-				$storage1->unlink($internalPath1);
410
-			}
411
-		} else {
412
-			$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
413
-		}
414
-
415
-		$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
416
-		$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
417
-
418
-		return ($result !== false);
419
-	}
420
-
421
-	/**
422
-	 * get a list of all available versions of a file in descending chronological order
423
-	 * @param string $uid user id from the owner of the file
424
-	 * @param string $filename file to find versions of, relative to the user files dir
425
-	 * @param string $userFullPath
426
-	 * @return array versions newest version first
427
-	 */
428
-	public static function getVersions($uid, $filename, $userFullPath = '') {
429
-		$versions = array();
430
-		if (empty($filename)) {
431
-			return $versions;
432
-		}
433
-		// fetch for old versions
434
-		$view = new View('/' . $uid . '/');
435
-
436
-		$pathinfo = pathinfo($filename);
437
-		$versionedFile = $pathinfo['basename'];
438
-
439
-		$dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']);
440
-
441
-		$dirContent = false;
442
-		if ($view->is_dir($dir)) {
443
-			$dirContent = $view->opendir($dir);
444
-		}
445
-
446
-		if ($dirContent === false) {
447
-			return $versions;
448
-		}
449
-
450
-		if (is_resource($dirContent)) {
451
-			while (($entryName = readdir($dirContent)) !== false) {
452
-				if (!Filesystem::isIgnoredDir($entryName)) {
453
-					$pathparts = pathinfo($entryName);
454
-					$filename = $pathparts['filename'];
455
-					if ($filename === $versionedFile) {
456
-						$pathparts = pathinfo($entryName);
457
-						$timestamp = substr($pathparts['extension'], 1);
458
-						$filename = $pathparts['filename'];
459
-						$key = $timestamp . '#' . $filename;
460
-						$versions[$key]['version'] = $timestamp;
461
-						$versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
462
-						if (empty($userFullPath)) {
463
-							$versions[$key]['preview'] = '';
464
-						} else {
465
-							$versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
466
-						}
467
-						$versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
468
-						$versions[$key]['name'] = $versionedFile;
469
-						$versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
470
-						$versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile);
471
-					}
472
-				}
473
-			}
474
-			closedir($dirContent);
475
-		}
476
-
477
-		// sort with newest version first
478
-		krsort($versions);
479
-
480
-		return $versions;
481
-	}
482
-
483
-	/**
484
-	 * Expire versions that older than max version retention time
485
-	 * @param string $uid
486
-	 */
487
-	public static function expireOlderThanMaxForUser($uid){
488
-		$expiration = self::getExpiration();
489
-		$threshold = $expiration->getMaxAgeAsTimestamp();
490
-		$versions = self::getAllVersions($uid);
491
-		if (!$threshold || !array_key_exists('all', $versions)) {
492
-			return;
493
-		}
494
-
495
-		$toDelete = [];
496
-		foreach (array_reverse($versions['all']) as $key => $version) {
497
-			if (intval($version['version'])<$threshold) {
498
-				$toDelete[$key] = $version;
499
-			} else {
500
-				//Versions are sorted by time - nothing mo to iterate.
501
-				break;
502
-			}
503
-		}
504
-
505
-		$view = new View('/' . $uid . '/files_versions');
506
-		if (!empty($toDelete)) {
507
-			foreach ($toDelete as $version) {
508
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT));
509
-				self::deleteVersion($view, $version['path'] . '.v' . $version['version']);
510
-				\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT));
511
-			}
512
-		}
513
-	}
514
-
515
-	/**
516
-	 * translate a timestamp into a string like "5 days ago"
517
-	 * @param int $timestamp
518
-	 * @return string for example "5 days ago"
519
-	 */
520
-	private static function getHumanReadableTimestamp($timestamp) {
521
-
522
-		$diff = time() - $timestamp;
523
-
524
-		if ($diff < 60) { // first minute
525
-			return  $diff . " seconds ago";
526
-		} elseif ($diff < 3600) { //first hour
527
-			return round($diff / 60) . " minutes ago";
528
-		} elseif ($diff < 86400) { // first day
529
-			return round($diff / 3600) . " hours ago";
530
-		} elseif ($diff < 604800) { //first week
531
-			return round($diff / 86400) . " days ago";
532
-		} elseif ($diff < 2419200) { //first month
533
-			return round($diff / 604800) . " weeks ago";
534
-		} elseif ($diff < 29030400) { // first year
535
-			return round($diff / 2419200) . " months ago";
536
-		} else {
537
-			return round($diff / 29030400) . " years ago";
538
-		}
539
-
540
-	}
541
-
542
-	/**
543
-	 * returns all stored file versions from a given user
544
-	 * @param string $uid id of the user
545
-	 * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
546
-	 */
547
-	private static function getAllVersions($uid) {
548
-		$view = new View('/' . $uid . '/');
549
-		$dirs = array(self::VERSIONS_ROOT);
550
-		$versions = array();
551
-
552
-		while (!empty($dirs)) {
553
-			$dir = array_pop($dirs);
554
-			$files = $view->getDirectoryContent($dir);
555
-
556
-			foreach ($files as $file) {
557
-				$fileData = $file->getData();
558
-				$filePath = $dir . '/' . $fileData['name'];
559
-				if ($file['type'] === 'dir') {
560
-					array_push($dirs, $filePath);
561
-				} else {
562
-					$versionsBegin = strrpos($filePath, '.v');
563
-					$relPathStart = strlen(self::VERSIONS_ROOT);
564
-					$version = substr($filePath, $versionsBegin + 2);
565
-					$relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart);
566
-					$key = $version . '#' . $relpath;
567
-					$versions[$key] = array('path' => $relpath, 'timestamp' => $version);
568
-				}
569
-			}
570
-		}
571
-
572
-		// newest version first
573
-		krsort($versions);
574
-
575
-		$result = array();
576
-
577
-		foreach ($versions as $key => $value) {
578
-			$size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']);
579
-			$filename = $value['path'];
580
-
581
-			$result['all'][$key]['version'] = $value['timestamp'];
582
-			$result['all'][$key]['path'] = $filename;
583
-			$result['all'][$key]['size'] = $size;
584
-
585
-			$result['by_file'][$filename][$key]['version'] = $value['timestamp'];
586
-			$result['by_file'][$filename][$key]['path'] = $filename;
587
-			$result['by_file'][$filename][$key]['size'] = $size;
588
-		}
589
-
590
-		return $result;
591
-	}
592
-
593
-	/**
594
-	 * get list of files we want to expire
595
-	 * @param array $versions list of versions
596
-	 * @param integer $time
597
-	 * @param bool $quotaExceeded is versions storage limit reached
598
-	 * @return array containing the list of to deleted versions and the size of them
599
-	 */
600
-	protected static function getExpireList($time, $versions, $quotaExceeded = false) {
601
-		$expiration = self::getExpiration();
602
-
603
-		if ($expiration->shouldAutoExpire()) {
604
-			list($toDelete, $size) = self::getAutoExpireList($time, $versions);
605
-		} else {
606
-			$size = 0;
607
-			$toDelete = [];  // versions we want to delete
608
-		}
609
-
610
-		foreach ($versions as $key => $version) {
611
-			if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) {
612
-				$size += $version['size'];
613
-				$toDelete[$key] = $version['path'] . '.v' . $version['version'];
614
-			}
615
-		}
616
-
617
-		return [$toDelete, $size];
618
-	}
619
-
620
-	/**
621
-	 * get list of files we want to expire
622
-	 * @param array $versions list of versions
623
-	 * @param integer $time
624
-	 * @return array containing the list of to deleted versions and the size of them
625
-	 */
626
-	protected static function getAutoExpireList($time, $versions) {
627
-		$size = 0;
628
-		$toDelete = array();  // versions we want to delete
629
-
630
-		$interval = 1;
631
-		$step = Storage::$max_versions_per_interval[$interval]['step'];
632
-		if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) {
633
-			$nextInterval = -1;
634
-		} else {
635
-			$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
636
-		}
637
-
638
-		$firstVersion = reset($versions);
639
-		$firstKey = key($versions);
640
-		$prevTimestamp = $firstVersion['version'];
641
-		$nextVersion = $firstVersion['version'] - $step;
642
-		unset($versions[$firstKey]);
643
-
644
-		foreach ($versions as $key => $version) {
645
-			$newInterval = true;
646
-			while ($newInterval) {
647
-				if ($nextInterval == -1 || $prevTimestamp > $nextInterval) {
648
-					if ($version['version'] > $nextVersion) {
649
-						//distance between two version too small, mark to delete
650
-						$toDelete[$key] = $version['path'] . '.v' . $version['version'];
651
-						$size += $version['size'];
652
-						\OCP\Util::writeLog('files_versions', 'Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, \OCP\Util::INFO);
653
-					} else {
654
-						$nextVersion = $version['version'] - $step;
655
-						$prevTimestamp = $version['version'];
656
-					}
657
-					$newInterval = false; // version checked so we can move to the next one
658
-				} else { // time to move on to the next interval
659
-					$interval++;
660
-					$step = Storage::$max_versions_per_interval[$interval]['step'];
661
-					$nextVersion = $prevTimestamp - $step;
662
-					if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) {
663
-						$nextInterval = -1;
664
-					} else {
665
-						$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
666
-					}
667
-					$newInterval = true; // we changed the interval -> check same version with new interval
668
-				}
669
-			}
670
-		}
671
-
672
-		return array($toDelete, $size);
673
-	}
674
-
675
-	/**
676
-	 * Schedule versions expiration for the given file
677
-	 *
678
-	 * @param string $uid owner of the file
679
-	 * @param string $fileName file/folder for which to schedule expiration
680
-	 */
681
-	private static function scheduleExpire($uid, $fileName) {
682
-		// let the admin disable auto expire
683
-		$expiration = self::getExpiration();
684
-		if ($expiration->isEnabled()) {
685
-			$command = new Expire($uid, $fileName);
686
-			\OC::$server->getCommandBus()->push($command);
687
-		}
688
-	}
689
-
690
-	/**
691
-	 * Expire versions which exceed the quota
692
-	 *
693
-	 * @param string $filename
694
-	 * @return bool|int|null
695
-	 */
696
-	public static function expire($filename) {
697
-		$config = \OC::$server->getConfig();
698
-		$expiration = self::getExpiration();
699
-
700
-		if($config->getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' && $expiration->isEnabled()) {
701
-
702
-			if (!Filesystem::file_exists($filename)) {
703
-				return false;
704
-			}
705
-
706
-			list($uid, $filename) = self::getUidAndFilename($filename);
707
-			if (empty($filename)) {
708
-				// file maybe renamed or deleted
709
-				return false;
710
-			}
711
-			$versionsFileview = new View('/'.$uid.'/files_versions');
712
-
713
-			// get available disk space for user
714
-			$user = \OC::$server->getUserManager()->get($uid);
715
-			$softQuota = true;
716
-			$quota = $user->getQuota();
717
-			if ( $quota === null || $quota === 'none' ) {
718
-				$quota = Filesystem::free_space('/');
719
-				$softQuota = false;
720
-			} else {
721
-				$quota = \OCP\Util::computerFileSize($quota);
722
-			}
723
-
724
-			// make sure that we have the current size of the version history
725
-			$versionsSize = self::getVersionsSize($uid);
726
-
727
-			// calculate available space for version history
728
-			// subtract size of files and current versions size from quota
729
-			if ($quota >= 0) {
730
-				if ($softQuota) {
731
-					$files_view = new View('/' . $uid . '/files');
732
-					$rootInfo = $files_view->getFileInfo('/', false);
733
-					$free = $quota - $rootInfo['size']; // remaining free space for user
734
-					if ($free > 0) {
735
-						$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
736
-					} else {
737
-						$availableSpace = $free - $versionsSize;
738
-					}
739
-				} else {
740
-					$availableSpace = $quota;
741
-				}
742
-			} else {
743
-				$availableSpace = PHP_INT_MAX;
744
-			}
745
-
746
-			$allVersions = Storage::getVersions($uid, $filename);
747
-
748
-			$time = time();
749
-			list($toDelete, $sizeOfDeletedVersions) = self::getExpireList($time, $allVersions, $availableSpace <= 0);
750
-
751
-			$availableSpace = $availableSpace + $sizeOfDeletedVersions;
752
-			$versionsSize = $versionsSize - $sizeOfDeletedVersions;
753
-
754
-			// if still not enough free space we rearrange the versions from all files
755
-			if ($availableSpace <= 0) {
756
-				$result = Storage::getAllVersions($uid);
757
-				$allVersions = $result['all'];
758
-
759
-				foreach ($result['by_file'] as $versions) {
760
-					list($toDeleteNew, $size) = self::getExpireList($time, $versions, $availableSpace <= 0);
761
-					$toDelete = array_merge($toDelete, $toDeleteNew);
762
-					$sizeOfDeletedVersions += $size;
763
-				}
764
-				$availableSpace = $availableSpace + $sizeOfDeletedVersions;
765
-				$versionsSize = $versionsSize - $sizeOfDeletedVersions;
766
-			}
767
-
768
-			foreach($toDelete as $key => $path) {
769
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
770
-				self::deleteVersion($versionsFileview, $path);
771
-				\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
772
-				unset($allVersions[$key]); // update array with the versions we keep
773
-				\OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::INFO);
774
-			}
775
-
776
-			// Check if enough space is available after versions are rearranged.
777
-			// If not we delete the oldest versions until we meet the size limit for versions,
778
-			// but always keep the two latest versions
779
-			$numOfVersions = count($allVersions) -2 ;
780
-			$i = 0;
781
-			// sort oldest first and make sure that we start at the first element
782
-			ksort($allVersions);
783
-			reset($allVersions);
784
-			while ($availableSpace < 0 && $i < $numOfVersions) {
785
-				$version = current($allVersions);
786
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
787
-				self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
788
-				\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
789
-				\OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::INFO);
790
-				$versionsSize -= $version['size'];
791
-				$availableSpace += $version['size'];
792
-				next($allVersions);
793
-				$i++;
794
-			}
795
-
796
-			return $versionsSize; // finally return the new size of the version history
797
-		}
798
-
799
-		return false;
800
-	}
801
-
802
-	/**
803
-	 * Create recursively missing directories inside of files_versions
804
-	 * that match the given path to a file.
805
-	 *
806
-	 * @param string $filename $path to a file, relative to the user's
807
-	 * "files" folder
808
-	 * @param View $view view on data/user/
809
-	 */
810
-	private static function createMissingDirectories($filename, $view) {
811
-		$dirname = Filesystem::normalizePath(dirname($filename));
812
-		$dirParts = explode('/', $dirname);
813
-		$dir = "/files_versions";
814
-		foreach ($dirParts as $part) {
815
-			$dir = $dir . '/' . $part;
816
-			if (!$view->file_exists($dir)) {
817
-				$view->mkdir($dir);
818
-			}
819
-		}
820
-	}
821
-
822
-	/**
823
-	 * Static workaround
824
-	 * @return Expiration
825
-	 */
826
-	protected static function getExpiration(){
827
-		if (is_null(self::$application)) {
828
-			self::$application = new Application();
829
-		}
830
-		return self::$application->getContainer()->query('Expiration');
831
-	}
56
+    const DEFAULTENABLED=true;
57
+    const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
58
+    const VERSIONS_ROOT = 'files_versions/';
59
+
60
+    const DELETE_TRIGGER_MASTER_REMOVED = 0;
61
+    const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1;
62
+    const DELETE_TRIGGER_QUOTA_EXCEEDED = 2;
63
+
64
+    // files for which we can remove the versions after the delete operation was successful
65
+    private static $deletedFiles = array();
66
+
67
+    private static $sourcePathAndUser = array();
68
+
69
+    private static $max_versions_per_interval = array(
70
+        //first 10sec, one version every 2sec
71
+        1 => array('intervalEndsAfter' => 10,      'step' => 2),
72
+        //next minute, one version every 10sec
73
+        2 => array('intervalEndsAfter' => 60,      'step' => 10),
74
+        //next hour, one version every minute
75
+        3 => array('intervalEndsAfter' => 3600,    'step' => 60),
76
+        //next 24h, one version every hour
77
+        4 => array('intervalEndsAfter' => 86400,   'step' => 3600),
78
+        //next 30days, one version per day
79
+        5 => array('intervalEndsAfter' => 2592000, 'step' => 86400),
80
+        //until the end one version per week
81
+        6 => array('intervalEndsAfter' => -1,      'step' => 604800),
82
+    );
83
+
84
+    /** @var \OCA\Files_Versions\AppInfo\Application */
85
+    private static $application;
86
+
87
+    /**
88
+     * get the UID of the owner of the file and the path to the file relative to
89
+     * owners files folder
90
+     *
91
+     * @param string $filename
92
+     * @return array
93
+     * @throws \OC\User\NoUserException
94
+     */
95
+    public static function getUidAndFilename($filename) {
96
+        $uid = Filesystem::getOwner($filename);
97
+        $userManager = \OC::$server->getUserManager();
98
+        // if the user with the UID doesn't exists, e.g. because the UID points
99
+        // to a remote user with a federated cloud ID we use the current logged-in
100
+        // user. We need a valid local user to create the versions
101
+        if (!$userManager->userExists($uid)) {
102
+            $uid = User::getUser();
103
+        }
104
+        Filesystem::initMountPoints($uid);
105
+        if ( $uid != User::getUser() ) {
106
+            $info = Filesystem::getFileInfo($filename);
107
+            $ownerView = new View('/'.$uid.'/files');
108
+            try {
109
+                $filename = $ownerView->getPath($info['fileid']);
110
+                // make sure that the file name doesn't end with a trailing slash
111
+                // can for example happen single files shared across servers
112
+                $filename = rtrim($filename, '/');
113
+            } catch (NotFoundException $e) {
114
+                $filename = null;
115
+            }
116
+        }
117
+        return [$uid, $filename];
118
+    }
119
+
120
+    /**
121
+     * Remember the owner and the owner path of the source file
122
+     *
123
+     * @param string $source source path
124
+     */
125
+    public static function setSourcePathAndUser($source) {
126
+        list($uid, $path) = self::getUidAndFilename($source);
127
+        self::$sourcePathAndUser[$source] = array('uid' => $uid, 'path' => $path);
128
+    }
129
+
130
+    /**
131
+     * Gets the owner and the owner path from the source path
132
+     *
133
+     * @param string $source source path
134
+     * @return array with user id and path
135
+     */
136
+    public static function getSourcePathAndUser($source) {
137
+
138
+        if (isset(self::$sourcePathAndUser[$source])) {
139
+            $uid = self::$sourcePathAndUser[$source]['uid'];
140
+            $path = self::$sourcePathAndUser[$source]['path'];
141
+            unset(self::$sourcePathAndUser[$source]);
142
+        } else {
143
+            $uid = $path = false;
144
+        }
145
+        return array($uid, $path);
146
+    }
147
+
148
+    /**
149
+     * get current size of all versions from a given user
150
+     *
151
+     * @param string $user user who owns the versions
152
+     * @return int versions size
153
+     */
154
+    private static function getVersionsSize($user) {
155
+        $view = new View('/' . $user);
156
+        $fileInfo = $view->getFileInfo('/files_versions');
157
+        return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
158
+    }
159
+
160
+    /**
161
+     * store a new version of a file.
162
+     */
163
+    public static function store($filename) {
164
+        if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
165
+
166
+            // if the file gets streamed we need to remove the .part extension
167
+            // to get the right target
168
+            $ext = pathinfo($filename, PATHINFO_EXTENSION);
169
+            if ($ext === 'part') {
170
+                $filename = substr($filename, 0, strlen($filename) - 5);
171
+            }
172
+
173
+            // we only handle existing files
174
+            if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
175
+                return false;
176
+            }
177
+
178
+            list($uid, $filename) = self::getUidAndFilename($filename);
179
+
180
+            $files_view = new View('/'.$uid .'/files');
181
+            $users_view = new View('/'.$uid);
182
+
183
+            // no use making versions for empty files
184
+            if ($files_view->filesize($filename) === 0) {
185
+                return false;
186
+            }
187
+
188
+            // create all parent folders
189
+            self::createMissingDirectories($filename, $users_view);
190
+
191
+            self::scheduleExpire($uid, $filename);
192
+
193
+            // store a new version of a file
194
+            $mtime = $users_view->filemtime('files/' . $filename);
195
+            $users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime);
196
+            // call getFileInfo to enforce a file cache entry for the new version
197
+            $users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
198
+        }
199
+    }
200
+
201
+
202
+    /**
203
+     * mark file as deleted so that we can remove the versions if the file is gone
204
+     * @param string $path
205
+     */
206
+    public static function markDeletedFile($path) {
207
+        list($uid, $filename) = self::getUidAndFilename($path);
208
+        self::$deletedFiles[$path] = array(
209
+            'uid' => $uid,
210
+            'filename' => $filename);
211
+    }
212
+
213
+    /**
214
+     * delete the version from the storage and cache
215
+     *
216
+     * @param View $view
217
+     * @param string $path
218
+     */
219
+    protected static function deleteVersion($view, $path) {
220
+        $view->unlink($path);
221
+        /**
222
+         * @var \OC\Files\Storage\Storage $storage
223
+         * @var string $internalPath
224
+         */
225
+        list($storage, $internalPath) = $view->resolvePath($path);
226
+        $cache = $storage->getCache($internalPath);
227
+        $cache->remove($internalPath);
228
+    }
229
+
230
+    /**
231
+     * Delete versions of a file
232
+     */
233
+    public static function delete($path) {
234
+
235
+        $deletedFile = self::$deletedFiles[$path];
236
+        $uid = $deletedFile['uid'];
237
+        $filename = $deletedFile['filename'];
238
+
239
+        if (!Filesystem::file_exists($path)) {
240
+
241
+            $view = new View('/' . $uid . '/files_versions');
242
+
243
+            $versions = self::getVersions($uid, $filename);
244
+            if (!empty($versions)) {
245
+                foreach ($versions as $v) {
246
+                    \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED));
247
+                    self::deleteVersion($view, $filename . '.v' . $v['version']);
248
+                    \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED));
249
+                }
250
+            }
251
+        }
252
+        unset(self::$deletedFiles[$path]);
253
+    }
254
+
255
+    /**
256
+     * Rename or copy versions of a file of the given paths
257
+     *
258
+     * @param string $sourcePath source path of the file to move, relative to
259
+     * the currently logged in user's "files" folder
260
+     * @param string $targetPath target path of the file to move, relative to
261
+     * the currently logged in user's "files" folder
262
+     * @param string $operation can be 'copy' or 'rename'
263
+     */
264
+    public static function renameOrCopy($sourcePath, $targetPath, $operation) {
265
+        list($sourceOwner, $sourcePath) = self::getSourcePathAndUser($sourcePath);
266
+
267
+        // it was a upload of a existing file if no old path exists
268
+        // in this case the pre-hook already called the store method and we can
269
+        // stop here
270
+        if ($sourcePath === false) {
271
+            return true;
272
+        }
273
+
274
+        list($targetOwner, $targetPath) = self::getUidAndFilename($targetPath);
275
+
276
+        $sourcePath = ltrim($sourcePath, '/');
277
+        $targetPath = ltrim($targetPath, '/');
278
+
279
+        $rootView = new View('');
280
+
281
+        // did we move a directory ?
282
+        if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
283
+            // does the directory exists for versions too ?
284
+            if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) {
285
+                // create missing dirs if necessary
286
+                self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
287
+
288
+                // move the directory containing the versions
289
+                $rootView->$operation(
290
+                    '/' . $sourceOwner . '/files_versions/' . $sourcePath,
291
+                    '/' . $targetOwner . '/files_versions/' . $targetPath
292
+                );
293
+            }
294
+        } else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) {
295
+            // create missing dirs if necessary
296
+            self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
297
+
298
+            foreach ($versions as $v) {
299
+                // move each version one by one to the target directory
300
+                $rootView->$operation(
301
+                    '/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
302
+                    '/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
303
+                );
304
+            }
305
+        }
306
+
307
+        // if we moved versions directly for a file, schedule expiration check for that file
308
+        if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
309
+            self::scheduleExpire($targetOwner, $targetPath);
310
+        }
311
+
312
+    }
313
+
314
+    /**
315
+     * Rollback to an old version of a file.
316
+     *
317
+     * @param string $file file name
318
+     * @param int $revision revision timestamp
319
+     * @return bool
320
+     */
321
+    public static function rollback($file, $revision) {
322
+
323
+        if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
324
+            // add expected leading slash
325
+            $file = '/' . ltrim($file, '/');
326
+            list($uid, $filename) = self::getUidAndFilename($file);
327
+            if ($uid === null || trim($filename, '/') === '') {
328
+                return false;
329
+            }
330
+
331
+            $users_view = new View('/'.$uid);
332
+            $files_view = new View('/'. User::getUser().'/files');
333
+
334
+            $versionCreated = false;
335
+
336
+            $fileInfo = $files_view->getFileInfo($file);
337
+
338
+            // check if user has the permissions to revert a version
339
+            if (!$fileInfo->isUpdateable()) {
340
+                return false;
341
+            }
342
+
343
+            //first create a new version
344
+            $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
345
+            if (!$users_view->file_exists($version)) {
346
+                $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
347
+                $versionCreated = true;
348
+            }
349
+
350
+            $fileToRestore =  'files_versions' . $filename . '.v' . $revision;
351
+
352
+            // Restore encrypted version of the old file for the newly restored file
353
+            // This has to happen manually here since the file is manually copied below
354
+            $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
355
+            $oldFileInfo = $users_view->getFileInfo($fileToRestore);
356
+            $cache = $fileInfo->getStorage()->getCache();
357
+            $cache->update(
358
+                $fileInfo->getId(), [
359
+                    'encrypted' => $oldVersion,
360
+                    'encryptedVersion' => $oldVersion,
361
+                    'size' => $oldFileInfo->getSize()
362
+                ]
363
+            );
364
+
365
+            // rollback
366
+            if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
367
+                $files_view->touch($file, $revision);
368
+                Storage::scheduleExpire($uid, $file);
369
+                \OC_Hook::emit('\OCP\Versions', 'rollback', array(
370
+                    'path' => $filename,
371
+                    'revision' => $revision,
372
+                ));
373
+                return true;
374
+            } else if ($versionCreated) {
375
+                self::deleteVersion($users_view, $version);
376
+            }
377
+        }
378
+        return false;
379
+
380
+    }
381
+
382
+    /**
383
+     * Stream copy file contents from $path1 to $path2
384
+     *
385
+     * @param View $view view to use for copying
386
+     * @param string $path1 source file to copy
387
+     * @param string $path2 target file
388
+     *
389
+     * @return bool true for success, false otherwise
390
+     */
391
+    private static function copyFileContents($view, $path1, $path2) {
392
+        /** @var \OC\Files\Storage\Storage $storage1 */
393
+        list($storage1, $internalPath1) = $view->resolvePath($path1);
394
+        /** @var \OC\Files\Storage\Storage $storage2 */
395
+        list($storage2, $internalPath2) = $view->resolvePath($path2);
396
+
397
+        $view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
398
+        $view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
399
+
400
+        // TODO add a proper way of overwriting a file while maintaining file ids
401
+        if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
402
+            $source = $storage1->fopen($internalPath1, 'r');
403
+            $target = $storage2->fopen($internalPath2, 'w');
404
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
405
+            fclose($source);
406
+            fclose($target);
407
+
408
+            if ($result !== false) {
409
+                $storage1->unlink($internalPath1);
410
+            }
411
+        } else {
412
+            $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
413
+        }
414
+
415
+        $view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
416
+        $view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
417
+
418
+        return ($result !== false);
419
+    }
420
+
421
+    /**
422
+     * get a list of all available versions of a file in descending chronological order
423
+     * @param string $uid user id from the owner of the file
424
+     * @param string $filename file to find versions of, relative to the user files dir
425
+     * @param string $userFullPath
426
+     * @return array versions newest version first
427
+     */
428
+    public static function getVersions($uid, $filename, $userFullPath = '') {
429
+        $versions = array();
430
+        if (empty($filename)) {
431
+            return $versions;
432
+        }
433
+        // fetch for old versions
434
+        $view = new View('/' . $uid . '/');
435
+
436
+        $pathinfo = pathinfo($filename);
437
+        $versionedFile = $pathinfo['basename'];
438
+
439
+        $dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']);
440
+
441
+        $dirContent = false;
442
+        if ($view->is_dir($dir)) {
443
+            $dirContent = $view->opendir($dir);
444
+        }
445
+
446
+        if ($dirContent === false) {
447
+            return $versions;
448
+        }
449
+
450
+        if (is_resource($dirContent)) {
451
+            while (($entryName = readdir($dirContent)) !== false) {
452
+                if (!Filesystem::isIgnoredDir($entryName)) {
453
+                    $pathparts = pathinfo($entryName);
454
+                    $filename = $pathparts['filename'];
455
+                    if ($filename === $versionedFile) {
456
+                        $pathparts = pathinfo($entryName);
457
+                        $timestamp = substr($pathparts['extension'], 1);
458
+                        $filename = $pathparts['filename'];
459
+                        $key = $timestamp . '#' . $filename;
460
+                        $versions[$key]['version'] = $timestamp;
461
+                        $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
462
+                        if (empty($userFullPath)) {
463
+                            $versions[$key]['preview'] = '';
464
+                        } else {
465
+                            $versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
466
+                        }
467
+                        $versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
468
+                        $versions[$key]['name'] = $versionedFile;
469
+                        $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
470
+                        $versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile);
471
+                    }
472
+                }
473
+            }
474
+            closedir($dirContent);
475
+        }
476
+
477
+        // sort with newest version first
478
+        krsort($versions);
479
+
480
+        return $versions;
481
+    }
482
+
483
+    /**
484
+     * Expire versions that older than max version retention time
485
+     * @param string $uid
486
+     */
487
+    public static function expireOlderThanMaxForUser($uid){
488
+        $expiration = self::getExpiration();
489
+        $threshold = $expiration->getMaxAgeAsTimestamp();
490
+        $versions = self::getAllVersions($uid);
491
+        if (!$threshold || !array_key_exists('all', $versions)) {
492
+            return;
493
+        }
494
+
495
+        $toDelete = [];
496
+        foreach (array_reverse($versions['all']) as $key => $version) {
497
+            if (intval($version['version'])<$threshold) {
498
+                $toDelete[$key] = $version;
499
+            } else {
500
+                //Versions are sorted by time - nothing mo to iterate.
501
+                break;
502
+            }
503
+        }
504
+
505
+        $view = new View('/' . $uid . '/files_versions');
506
+        if (!empty($toDelete)) {
507
+            foreach ($toDelete as $version) {
508
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT));
509
+                self::deleteVersion($view, $version['path'] . '.v' . $version['version']);
510
+                \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT));
511
+            }
512
+        }
513
+    }
514
+
515
+    /**
516
+     * translate a timestamp into a string like "5 days ago"
517
+     * @param int $timestamp
518
+     * @return string for example "5 days ago"
519
+     */
520
+    private static function getHumanReadableTimestamp($timestamp) {
521
+
522
+        $diff = time() - $timestamp;
523
+
524
+        if ($diff < 60) { // first minute
525
+            return  $diff . " seconds ago";
526
+        } elseif ($diff < 3600) { //first hour
527
+            return round($diff / 60) . " minutes ago";
528
+        } elseif ($diff < 86400) { // first day
529
+            return round($diff / 3600) . " hours ago";
530
+        } elseif ($diff < 604800) { //first week
531
+            return round($diff / 86400) . " days ago";
532
+        } elseif ($diff < 2419200) { //first month
533
+            return round($diff / 604800) . " weeks ago";
534
+        } elseif ($diff < 29030400) { // first year
535
+            return round($diff / 2419200) . " months ago";
536
+        } else {
537
+            return round($diff / 29030400) . " years ago";
538
+        }
539
+
540
+    }
541
+
542
+    /**
543
+     * returns all stored file versions from a given user
544
+     * @param string $uid id of the user
545
+     * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
546
+     */
547
+    private static function getAllVersions($uid) {
548
+        $view = new View('/' . $uid . '/');
549
+        $dirs = array(self::VERSIONS_ROOT);
550
+        $versions = array();
551
+
552
+        while (!empty($dirs)) {
553
+            $dir = array_pop($dirs);
554
+            $files = $view->getDirectoryContent($dir);
555
+
556
+            foreach ($files as $file) {
557
+                $fileData = $file->getData();
558
+                $filePath = $dir . '/' . $fileData['name'];
559
+                if ($file['type'] === 'dir') {
560
+                    array_push($dirs, $filePath);
561
+                } else {
562
+                    $versionsBegin = strrpos($filePath, '.v');
563
+                    $relPathStart = strlen(self::VERSIONS_ROOT);
564
+                    $version = substr($filePath, $versionsBegin + 2);
565
+                    $relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart);
566
+                    $key = $version . '#' . $relpath;
567
+                    $versions[$key] = array('path' => $relpath, 'timestamp' => $version);
568
+                }
569
+            }
570
+        }
571
+
572
+        // newest version first
573
+        krsort($versions);
574
+
575
+        $result = array();
576
+
577
+        foreach ($versions as $key => $value) {
578
+            $size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']);
579
+            $filename = $value['path'];
580
+
581
+            $result['all'][$key]['version'] = $value['timestamp'];
582
+            $result['all'][$key]['path'] = $filename;
583
+            $result['all'][$key]['size'] = $size;
584
+
585
+            $result['by_file'][$filename][$key]['version'] = $value['timestamp'];
586
+            $result['by_file'][$filename][$key]['path'] = $filename;
587
+            $result['by_file'][$filename][$key]['size'] = $size;
588
+        }
589
+
590
+        return $result;
591
+    }
592
+
593
+    /**
594
+     * get list of files we want to expire
595
+     * @param array $versions list of versions
596
+     * @param integer $time
597
+     * @param bool $quotaExceeded is versions storage limit reached
598
+     * @return array containing the list of to deleted versions and the size of them
599
+     */
600
+    protected static function getExpireList($time, $versions, $quotaExceeded = false) {
601
+        $expiration = self::getExpiration();
602
+
603
+        if ($expiration->shouldAutoExpire()) {
604
+            list($toDelete, $size) = self::getAutoExpireList($time, $versions);
605
+        } else {
606
+            $size = 0;
607
+            $toDelete = [];  // versions we want to delete
608
+        }
609
+
610
+        foreach ($versions as $key => $version) {
611
+            if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) {
612
+                $size += $version['size'];
613
+                $toDelete[$key] = $version['path'] . '.v' . $version['version'];
614
+            }
615
+        }
616
+
617
+        return [$toDelete, $size];
618
+    }
619
+
620
+    /**
621
+     * get list of files we want to expire
622
+     * @param array $versions list of versions
623
+     * @param integer $time
624
+     * @return array containing the list of to deleted versions and the size of them
625
+     */
626
+    protected static function getAutoExpireList($time, $versions) {
627
+        $size = 0;
628
+        $toDelete = array();  // versions we want to delete
629
+
630
+        $interval = 1;
631
+        $step = Storage::$max_versions_per_interval[$interval]['step'];
632
+        if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) {
633
+            $nextInterval = -1;
634
+        } else {
635
+            $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
636
+        }
637
+
638
+        $firstVersion = reset($versions);
639
+        $firstKey = key($versions);
640
+        $prevTimestamp = $firstVersion['version'];
641
+        $nextVersion = $firstVersion['version'] - $step;
642
+        unset($versions[$firstKey]);
643
+
644
+        foreach ($versions as $key => $version) {
645
+            $newInterval = true;
646
+            while ($newInterval) {
647
+                if ($nextInterval == -1 || $prevTimestamp > $nextInterval) {
648
+                    if ($version['version'] > $nextVersion) {
649
+                        //distance between two version too small, mark to delete
650
+                        $toDelete[$key] = $version['path'] . '.v' . $version['version'];
651
+                        $size += $version['size'];
652
+                        \OCP\Util::writeLog('files_versions', 'Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, \OCP\Util::INFO);
653
+                    } else {
654
+                        $nextVersion = $version['version'] - $step;
655
+                        $prevTimestamp = $version['version'];
656
+                    }
657
+                    $newInterval = false; // version checked so we can move to the next one
658
+                } else { // time to move on to the next interval
659
+                    $interval++;
660
+                    $step = Storage::$max_versions_per_interval[$interval]['step'];
661
+                    $nextVersion = $prevTimestamp - $step;
662
+                    if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) {
663
+                        $nextInterval = -1;
664
+                    } else {
665
+                        $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
666
+                    }
667
+                    $newInterval = true; // we changed the interval -> check same version with new interval
668
+                }
669
+            }
670
+        }
671
+
672
+        return array($toDelete, $size);
673
+    }
674
+
675
+    /**
676
+     * Schedule versions expiration for the given file
677
+     *
678
+     * @param string $uid owner of the file
679
+     * @param string $fileName file/folder for which to schedule expiration
680
+     */
681
+    private static function scheduleExpire($uid, $fileName) {
682
+        // let the admin disable auto expire
683
+        $expiration = self::getExpiration();
684
+        if ($expiration->isEnabled()) {
685
+            $command = new Expire($uid, $fileName);
686
+            \OC::$server->getCommandBus()->push($command);
687
+        }
688
+    }
689
+
690
+    /**
691
+     * Expire versions which exceed the quota
692
+     *
693
+     * @param string $filename
694
+     * @return bool|int|null
695
+     */
696
+    public static function expire($filename) {
697
+        $config = \OC::$server->getConfig();
698
+        $expiration = self::getExpiration();
699
+
700
+        if($config->getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' && $expiration->isEnabled()) {
701
+
702
+            if (!Filesystem::file_exists($filename)) {
703
+                return false;
704
+            }
705
+
706
+            list($uid, $filename) = self::getUidAndFilename($filename);
707
+            if (empty($filename)) {
708
+                // file maybe renamed or deleted
709
+                return false;
710
+            }
711
+            $versionsFileview = new View('/'.$uid.'/files_versions');
712
+
713
+            // get available disk space for user
714
+            $user = \OC::$server->getUserManager()->get($uid);
715
+            $softQuota = true;
716
+            $quota = $user->getQuota();
717
+            if ( $quota === null || $quota === 'none' ) {
718
+                $quota = Filesystem::free_space('/');
719
+                $softQuota = false;
720
+            } else {
721
+                $quota = \OCP\Util::computerFileSize($quota);
722
+            }
723
+
724
+            // make sure that we have the current size of the version history
725
+            $versionsSize = self::getVersionsSize($uid);
726
+
727
+            // calculate available space for version history
728
+            // subtract size of files and current versions size from quota
729
+            if ($quota >= 0) {
730
+                if ($softQuota) {
731
+                    $files_view = new View('/' . $uid . '/files');
732
+                    $rootInfo = $files_view->getFileInfo('/', false);
733
+                    $free = $quota - $rootInfo['size']; // remaining free space for user
734
+                    if ($free > 0) {
735
+                        $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
736
+                    } else {
737
+                        $availableSpace = $free - $versionsSize;
738
+                    }
739
+                } else {
740
+                    $availableSpace = $quota;
741
+                }
742
+            } else {
743
+                $availableSpace = PHP_INT_MAX;
744
+            }
745
+
746
+            $allVersions = Storage::getVersions($uid, $filename);
747
+
748
+            $time = time();
749
+            list($toDelete, $sizeOfDeletedVersions) = self::getExpireList($time, $allVersions, $availableSpace <= 0);
750
+
751
+            $availableSpace = $availableSpace + $sizeOfDeletedVersions;
752
+            $versionsSize = $versionsSize - $sizeOfDeletedVersions;
753
+
754
+            // if still not enough free space we rearrange the versions from all files
755
+            if ($availableSpace <= 0) {
756
+                $result = Storage::getAllVersions($uid);
757
+                $allVersions = $result['all'];
758
+
759
+                foreach ($result['by_file'] as $versions) {
760
+                    list($toDeleteNew, $size) = self::getExpireList($time, $versions, $availableSpace <= 0);
761
+                    $toDelete = array_merge($toDelete, $toDeleteNew);
762
+                    $sizeOfDeletedVersions += $size;
763
+                }
764
+                $availableSpace = $availableSpace + $sizeOfDeletedVersions;
765
+                $versionsSize = $versionsSize - $sizeOfDeletedVersions;
766
+            }
767
+
768
+            foreach($toDelete as $key => $path) {
769
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
770
+                self::deleteVersion($versionsFileview, $path);
771
+                \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
772
+                unset($allVersions[$key]); // update array with the versions we keep
773
+                \OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::INFO);
774
+            }
775
+
776
+            // Check if enough space is available after versions are rearranged.
777
+            // If not we delete the oldest versions until we meet the size limit for versions,
778
+            // but always keep the two latest versions
779
+            $numOfVersions = count($allVersions) -2 ;
780
+            $i = 0;
781
+            // sort oldest first and make sure that we start at the first element
782
+            ksort($allVersions);
783
+            reset($allVersions);
784
+            while ($availableSpace < 0 && $i < $numOfVersions) {
785
+                $version = current($allVersions);
786
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
787
+                self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
788
+                \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
789
+                \OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::INFO);
790
+                $versionsSize -= $version['size'];
791
+                $availableSpace += $version['size'];
792
+                next($allVersions);
793
+                $i++;
794
+            }
795
+
796
+            return $versionsSize; // finally return the new size of the version history
797
+        }
798
+
799
+        return false;
800
+    }
801
+
802
+    /**
803
+     * Create recursively missing directories inside of files_versions
804
+     * that match the given path to a file.
805
+     *
806
+     * @param string $filename $path to a file, relative to the user's
807
+     * "files" folder
808
+     * @param View $view view on data/user/
809
+     */
810
+    private static function createMissingDirectories($filename, $view) {
811
+        $dirname = Filesystem::normalizePath(dirname($filename));
812
+        $dirParts = explode('/', $dirname);
813
+        $dir = "/files_versions";
814
+        foreach ($dirParts as $part) {
815
+            $dir = $dir . '/' . $part;
816
+            if (!$view->file_exists($dir)) {
817
+                $view->mkdir($dir);
818
+            }
819
+        }
820
+    }
821
+
822
+    /**
823
+     * Static workaround
824
+     * @return Expiration
825
+     */
826
+    protected static function getExpiration(){
827
+        if (is_null(self::$application)) {
828
+            self::$application = new Application();
829
+        }
830
+        return self::$application->getContainer()->query('Expiration');
831
+    }
832 832
 
833 833
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Controller/PreviewController.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -36,64 +36,64 @@
 block discarded – undo
36 36
 
37 37
 class PreviewController extends Controller {
38 38
 
39
-	/** @var IRootFolder */
40
-	private $rootFolder;
39
+    /** @var IRootFolder */
40
+    private $rootFolder;
41 41
 
42
-	/** @var string */
43
-	private $userId;
42
+    /** @var string */
43
+    private $userId;
44 44
 
45
-	/** @var IMimeTypeDetector */
46
-	private $mimeTypeDetector;
45
+    /** @var IMimeTypeDetector */
46
+    private $mimeTypeDetector;
47 47
 
48
-	/** @var IPreview */
49
-	private $previewManager;
48
+    /** @var IPreview */
49
+    private $previewManager;
50 50
 
51
-	public function __construct($appName,
52
-								IRequest $request,
53
-								IRootFolder $rootFolder,
54
-								$userId,
55
-								IMimeTypeDetector $mimeTypeDetector,
56
-								IPreview $previewManager) {
57
-		parent::__construct($appName, $request);
51
+    public function __construct($appName,
52
+                                IRequest $request,
53
+                                IRootFolder $rootFolder,
54
+                                $userId,
55
+                                IMimeTypeDetector $mimeTypeDetector,
56
+                                IPreview $previewManager) {
57
+        parent::__construct($appName, $request);
58 58
 
59
-		$this->rootFolder = $rootFolder;
60
-		$this->userId = $userId;
61
-		$this->mimeTypeDetector = $mimeTypeDetector;
62
-		$this->previewManager = $previewManager;
63
-	}
59
+        $this->rootFolder = $rootFolder;
60
+        $this->userId = $userId;
61
+        $this->mimeTypeDetector = $mimeTypeDetector;
62
+        $this->previewManager = $previewManager;
63
+    }
64 64
 
65
-	/**
66
-	 * @NoAdminRequired
67
-	 * @NoCSRFRequired
68
-	 *
69
-	 * @param string $file
70
-	 * @param int $x
71
-	 * @param int $y
72
-	 * @param string $version
73
-	 * @return DataResponse|FileDisplayResponse
74
-	 */
75
-	public function getPreview(
76
-		$file = '',
77
-		$x = 44,
78
-		$y = 44,
79
-		$version = ''
80
-	) {
81
-		if($file === '' || $version === '' || $x === 0 || $y === 0) {
82
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
83
-		}
65
+    /**
66
+     * @NoAdminRequired
67
+     * @NoCSRFRequired
68
+     *
69
+     * @param string $file
70
+     * @param int $x
71
+     * @param int $y
72
+     * @param string $version
73
+     * @return DataResponse|FileDisplayResponse
74
+     */
75
+    public function getPreview(
76
+        $file = '',
77
+        $x = 44,
78
+        $y = 44,
79
+        $version = ''
80
+    ) {
81
+        if($file === '' || $version === '' || $x === 0 || $y === 0) {
82
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
83
+        }
84 84
 
85
-		try {
86
-			$userFolder = $this->rootFolder->getUserFolder($this->userId);
87
-			/** @var Folder $versionFolder */
88
-			$versionFolder = $userFolder->getParent()->get('files_versions');
89
-			$mimeType = $this->mimeTypeDetector->detectPath($file);
90
-			$file = $versionFolder->get($file.'.v'.$version);
85
+        try {
86
+            $userFolder = $this->rootFolder->getUserFolder($this->userId);
87
+            /** @var Folder $versionFolder */
88
+            $versionFolder = $userFolder->getParent()->get('files_versions');
89
+            $mimeType = $this->mimeTypeDetector->detectPath($file);
90
+            $file = $versionFolder->get($file.'.v'.$version);
91 91
 
92
-			/** @var File $file */
93
-			$f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
94
-			return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
95
-		} catch (NotFoundException $e) {
96
-			return new DataResponse([], Http::STATUS_NOT_FOUND);
97
-		}
98
-	}
92
+            /** @var File $file */
93
+            $f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
94
+            return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
95
+        } catch (NotFoundException $e) {
96
+            return new DataResponse([], Http::STATUS_NOT_FOUND);
97
+        }
98
+    }
99 99
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/AppInfo/Application.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -27,24 +27,24 @@
 block discarded – undo
27 27
 use OCA\Files_Versions\Expiration;
28 28
 
29 29
 class Application extends App {
30
-	public function __construct(array $urlParams = array()) {
31
-		parent::__construct('files_versions', $urlParams);
30
+    public function __construct(array $urlParams = array()) {
31
+        parent::__construct('files_versions', $urlParams);
32 32
 
33
-		$container = $this->getContainer();
33
+        $container = $this->getContainer();
34 34
 
35
-		/*
35
+        /*
36 36
 		 * Register capabilities
37 37
 		 */
38
-		$container->registerCapability('OCA\Files_Versions\Capabilities');
38
+        $container->registerCapability('OCA\Files_Versions\Capabilities');
39 39
 
40
-		/*
40
+        /*
41 41
 		 * Register expiration
42 42
 		 */
43
-		$container->registerService('Expiration', function($c) {
44
-			return  new Expiration(
45
-				$c->query('ServerContainer')->getConfig(),
46
-				$c->query('OCP\AppFramework\Utility\ITimeFactory')
47
-			);
48
-		});
49
-	}
43
+        $container->registerService('Expiration', function($c) {
44
+            return  new Expiration(
45
+                $c->query('ServerContainer')->getConfig(),
46
+                $c->query('OCP\AppFramework\Utility\ITimeFactory')
47
+            );
48
+        });
49
+    }
50 50
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Command/Expire.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -29,37 +29,37 @@
 block discarded – undo
29 29
 use OCP\Command\ICommand;
30 30
 
31 31
 class Expire implements ICommand {
32
-	use FileAccess;
32
+    use FileAccess;
33 33
 
34
-	/**
35
-	 * @var string
36
-	 */
37
-	private $fileName;
34
+    /**
35
+     * @var string
36
+     */
37
+    private $fileName;
38 38
 
39
-	/**
40
-	 * @var string
41
-	 */
42
-	private $user;
39
+    /**
40
+     * @var string
41
+     */
42
+    private $user;
43 43
 
44
-	/**
45
-	 * @param string $user
46
-	 * @param string $fileName
47
-	 */
48
-	function __construct($user, $fileName) {
49
-		$this->user = $user;
50
-		$this->fileName = $fileName;
51
-	}
44
+    /**
45
+     * @param string $user
46
+     * @param string $fileName
47
+     */
48
+    function __construct($user, $fileName) {
49
+        $this->user = $user;
50
+        $this->fileName = $fileName;
51
+    }
52 52
 
53 53
 
54
-	public function handle() {
55
-		$userManager = \OC::$server->getUserManager();
56
-		if (!$userManager->userExists($this->user)) {
57
-			// User has been deleted already
58
-			return;
59
-		}
54
+    public function handle() {
55
+        $userManager = \OC::$server->getUserManager();
56
+        if (!$userManager->userExists($this->user)) {
57
+            // User has been deleted already
58
+            return;
59
+        }
60 60
 
61
-		\OC_Util::setupFS($this->user);
62
-		Storage::expire($this->fileName);
63
-		\OC_Util::tearDownFS();
64
-	}
61
+        \OC_Util::setupFS($this->user);
62
+        Storage::expire($this->fileName);
63
+        \OC_Util::tearDownFS();
64
+    }
65 65
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Command/ExpireVersions.php 1 patch
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -33,93 +33,93 @@
 block discarded – undo
33 33
 
34 34
 class ExpireVersions extends Command {
35 35
 
36
-	/**
37
-	 * @var Expiration
38
-	 */
39
-	private $expiration;
36
+    /**
37
+     * @var Expiration
38
+     */
39
+    private $expiration;
40 40
 	
41
-	/**
42
-	 * @var IUserManager
43
-	 */
44
-	private $userManager;
41
+    /**
42
+     * @var IUserManager
43
+     */
44
+    private $userManager;
45 45
 
46
-	/**
47
-	 * @param IUserManager|null $userManager
48
-	 * @param Expiration|null $expiration
49
-	 */
50
-	public function __construct(IUserManager $userManager = null,
51
-								Expiration $expiration = null) {
52
-		parent::__construct();
46
+    /**
47
+     * @param IUserManager|null $userManager
48
+     * @param Expiration|null $expiration
49
+     */
50
+    public function __construct(IUserManager $userManager = null,
51
+                                Expiration $expiration = null) {
52
+        parent::__construct();
53 53
 
54
-		$this->userManager = $userManager;
55
-		$this->expiration = $expiration;
56
-	}
54
+        $this->userManager = $userManager;
55
+        $this->expiration = $expiration;
56
+    }
57 57
 
58
-	protected function configure() {
59
-		$this
60
-			->setName('versions:expire')
61
-			->setDescription('Expires the users file versions')
62
-			->addArgument(
63
-				'user_id',
64
-				InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
65
-				'expire file versions of the given user(s), if no user is given file versions for all users will be expired.'
66
-			);
67
-	}
58
+    protected function configure() {
59
+        $this
60
+            ->setName('versions:expire')
61
+            ->setDescription('Expires the users file versions')
62
+            ->addArgument(
63
+                'user_id',
64
+                InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
65
+                'expire file versions of the given user(s), if no user is given file versions for all users will be expired.'
66
+            );
67
+    }
68 68
 
69
-	protected function execute(InputInterface $input, OutputInterface $output) {
69
+    protected function execute(InputInterface $input, OutputInterface $output) {
70 70
 
71
-		$maxAge = $this->expiration->getMaxAgeAsTimestamp();
72
-		if (!$maxAge) {
73
-			$output->writeln("No expiry configured.");
74
-			return;
75
-		}
71
+        $maxAge = $this->expiration->getMaxAgeAsTimestamp();
72
+        if (!$maxAge) {
73
+            $output->writeln("No expiry configured.");
74
+            return;
75
+        }
76 76
 
77
-		$users = $input->getArgument('user_id');
78
-		if (!empty($users)) {
79
-			foreach ($users as $user) {
80
-				if ($this->userManager->userExists($user)) {
81
-					$output->writeln("Remove deleted files of   <info>$user</info>");
82
-					$userObject = $this->userManager->get($user);
83
-					$this->expireVersionsForUser($userObject);
84
-				} else {
85
-					$output->writeln("<error>Unknown user $user</error>");
86
-				}
87
-			}
88
-		} else {
89
-			$p = new ProgressBar($output);
90
-			$p->start();
91
-			$this->userManager->callForSeenUsers(function(IUser $user) use ($p) {
92
-				$p->advance();
93
-				$this->expireVersionsForUser($user);
94
-			});
95
-			$p->finish();
96
-			$output->writeln('');
97
-		}
98
-	}
77
+        $users = $input->getArgument('user_id');
78
+        if (!empty($users)) {
79
+            foreach ($users as $user) {
80
+                if ($this->userManager->userExists($user)) {
81
+                    $output->writeln("Remove deleted files of   <info>$user</info>");
82
+                    $userObject = $this->userManager->get($user);
83
+                    $this->expireVersionsForUser($userObject);
84
+                } else {
85
+                    $output->writeln("<error>Unknown user $user</error>");
86
+                }
87
+            }
88
+        } else {
89
+            $p = new ProgressBar($output);
90
+            $p->start();
91
+            $this->userManager->callForSeenUsers(function(IUser $user) use ($p) {
92
+                $p->advance();
93
+                $this->expireVersionsForUser($user);
94
+            });
95
+            $p->finish();
96
+            $output->writeln('');
97
+        }
98
+    }
99 99
 
100
-	function expireVersionsForUser(IUser $user) {
101
-		$uid = $user->getUID();
102
-		if (!$this->setupFS($uid)) {
103
-			return;
104
-		}
105
-		Storage::expireOlderThanMaxForUser($uid);
106
-	}
100
+    function expireVersionsForUser(IUser $user) {
101
+        $uid = $user->getUID();
102
+        if (!$this->setupFS($uid)) {
103
+            return;
104
+        }
105
+        Storage::expireOlderThanMaxForUser($uid);
106
+    }
107 107
 
108
-	/**
109
-	 * Act on behalf on versions item owner
110
-	 * @param string $user
111
-	 * @return boolean
112
-	 */
113
-	protected function setupFS($user) {
114
-		\OC_Util::tearDownFS();
115
-		\OC_Util::setupFS($user);
108
+    /**
109
+     * Act on behalf on versions item owner
110
+     * @param string $user
111
+     * @return boolean
112
+     */
113
+    protected function setupFS($user) {
114
+        \OC_Util::tearDownFS();
115
+        \OC_Util::setupFS($user);
116 116
 
117
-		// Check if this user has a version directory
118
-		$view = new \OC\Files\View('/' . $user);
119
-		if (!$view->is_dir('/files_versions')) {
120
-			return false;
121
-		}
117
+        // Check if this user has a version directory
118
+        $view = new \OC\Files\View('/' . $user);
119
+        if (!$view->is_dir('/files_versions')) {
120
+            return false;
121
+        }
122 122
 
123
-		return true;
124
-	}
123
+        return true;
124
+    }
125 125
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Command/CleanUp.php 1 patch
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -33,83 +33,83 @@
 block discarded – undo
33 33
 
34 34
 class CleanUp extends Command {
35 35
 
36
-	/** @var IUserManager */
37
-	protected $userManager;
38
-
39
-	/** @var IRootFolder */
40
-	protected $rootFolder;
41
-
42
-	/**
43
-	 * @param IRootFolder $rootFolder
44
-	 * @param IUserManager $userManager
45
-	 */
46
-	function __construct(IRootFolder $rootFolder, IUserManager $userManager) {
47
-		parent::__construct();
48
-		$this->userManager = $userManager;
49
-		$this->rootFolder = $rootFolder;
50
-	}
51
-
52
-	protected function configure() {
53
-		$this
54
-			->setName('versions:cleanup')
55
-			->setDescription('Delete versions')
56
-			->addArgument(
57
-				'user_id',
58
-				InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
59
-				'delete versions of the given user(s), if no user is given all versions will be deleted'
60
-			);
61
-	}
62
-
63
-
64
-	protected function execute(InputInterface $input, OutputInterface $output) {
65
-
66
-		$users = $input->getArgument('user_id');
67
-		if (!empty($users)) {
68
-			foreach ($users as $user) {
69
-				if ($this->userManager->userExists($user)) {
70
-					$output->writeln("Delete versions of   <info>$user</info>");
71
-					$this->deleteVersions($user);
72
-				} else {
73
-					$output->writeln("<error>Unknown user $user</error>");
74
-				}
75
-			}
76
-		} else {
77
-			$output->writeln('Delete all versions');
78
-			foreach ($this->userManager->getBackends() as $backend) {
79
-				$name = get_class($backend);
80
-
81
-				if ($backend instanceof IUserBackend) {
82
-					$name = $backend->getBackendName();
83
-				}
84
-
85
-				$output->writeln("Delete versions for users on backend <info>$name</info>");
86
-
87
-				$limit = 500;
88
-				$offset = 0;
89
-				do {
90
-					$users = $backend->getUsers('', $limit, $offset);
91
-					foreach ($users as $user) {
92
-						$output->writeln("   <info>$user</info>");
93
-						$this->deleteVersions($user);
94
-					}
95
-					$offset += $limit;
96
-				} while (count($users) >= $limit);
97
-			}
98
-		}
99
-	}
100
-
101
-
102
-	/**
103
-	 * delete versions for the given user
104
-	 *
105
-	 * @param string $user
106
-	 */
107
-	protected function deleteVersions($user) {
108
-		\OC_Util::tearDownFS();
109
-		\OC_Util::setupFS($user);
110
-		if ($this->rootFolder->nodeExists('/' . $user . '/files_versions')) {
111
-			$this->rootFolder->get('/' . $user . '/files_versions')->delete();
112
-		}
113
-	}
36
+    /** @var IUserManager */
37
+    protected $userManager;
38
+
39
+    /** @var IRootFolder */
40
+    protected $rootFolder;
41
+
42
+    /**
43
+     * @param IRootFolder $rootFolder
44
+     * @param IUserManager $userManager
45
+     */
46
+    function __construct(IRootFolder $rootFolder, IUserManager $userManager) {
47
+        parent::__construct();
48
+        $this->userManager = $userManager;
49
+        $this->rootFolder = $rootFolder;
50
+    }
51
+
52
+    protected function configure() {
53
+        $this
54
+            ->setName('versions:cleanup')
55
+            ->setDescription('Delete versions')
56
+            ->addArgument(
57
+                'user_id',
58
+                InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
59
+                'delete versions of the given user(s), if no user is given all versions will be deleted'
60
+            );
61
+    }
62
+
63
+
64
+    protected function execute(InputInterface $input, OutputInterface $output) {
65
+
66
+        $users = $input->getArgument('user_id');
67
+        if (!empty($users)) {
68
+            foreach ($users as $user) {
69
+                if ($this->userManager->userExists($user)) {
70
+                    $output->writeln("Delete versions of   <info>$user</info>");
71
+                    $this->deleteVersions($user);
72
+                } else {
73
+                    $output->writeln("<error>Unknown user $user</error>");
74
+                }
75
+            }
76
+        } else {
77
+            $output->writeln('Delete all versions');
78
+            foreach ($this->userManager->getBackends() as $backend) {
79
+                $name = get_class($backend);
80
+
81
+                if ($backend instanceof IUserBackend) {
82
+                    $name = $backend->getBackendName();
83
+                }
84
+
85
+                $output->writeln("Delete versions for users on backend <info>$name</info>");
86
+
87
+                $limit = 500;
88
+                $offset = 0;
89
+                do {
90
+                    $users = $backend->getUsers('', $limit, $offset);
91
+                    foreach ($users as $user) {
92
+                        $output->writeln("   <info>$user</info>");
93
+                        $this->deleteVersions($user);
94
+                    }
95
+                    $offset += $limit;
96
+                } while (count($users) >= $limit);
97
+            }
98
+        }
99
+    }
100
+
101
+
102
+    /**
103
+     * delete versions for the given user
104
+     *
105
+     * @param string $user
106
+     */
107
+    protected function deleteVersions($user) {
108
+        \OC_Util::tearDownFS();
109
+        \OC_Util::setupFS($user);
110
+        if ($this->rootFolder->nodeExists('/' . $user . '/files_versions')) {
111
+            $this->rootFolder->get('/' . $user . '/files_versions')->delete();
112
+        }
113
+    }
114 114
 
115 115
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Capabilities.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -29,16 +29,16 @@
 block discarded – undo
29 29
 
30 30
 class Capabilities implements ICapability {
31 31
 	
32
-	/**
33
-	 * Return this classes capabilities
34
-	 *
35
-	 * @return array
36
-	 */
37
-	public function getCapabilities() {
38
-		return [
39
-			'files' => [
40
-				'versioning' => true
41
-			]
42
-		];
43
-	}
32
+    /**
33
+     * Return this classes capabilities
34
+     *
35
+     * @return array
36
+     */
37
+    public function getCapabilities() {
38
+        return [
39
+            'files' => [
40
+                'versioning' => true
41
+            ]
42
+        ];
43
+    }
44 44
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/BackgroundJob/ExpireVersions.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -31,66 +31,66 @@
 block discarded – undo
31 31
 
32 32
 class ExpireVersions extends \OC\BackgroundJob\TimedJob {
33 33
 
34
-	const ITEMS_PER_SESSION = 1000;
34
+    const ITEMS_PER_SESSION = 1000;
35 35
 
36
-	/**
37
-	 * @var Expiration
38
-	 */
39
-	private $expiration;
36
+    /**
37
+     * @var Expiration
38
+     */
39
+    private $expiration;
40 40
 	
41
-	/**
42
-	 * @var IUserManager
43
-	 */
44
-	private $userManager;
41
+    /**
42
+     * @var IUserManager
43
+     */
44
+    private $userManager;
45 45
 
46
-	public function __construct(IUserManager $userManager = null, Expiration $expiration = null) {
47
-		// Run once per 30 minutes
48
-		$this->setInterval(60 * 30);
46
+    public function __construct(IUserManager $userManager = null, Expiration $expiration = null) {
47
+        // Run once per 30 minutes
48
+        $this->setInterval(60 * 30);
49 49
 
50
-		if (is_null($expiration) || is_null($userManager)) {
51
-			$this->fixDIForJobs();
52
-		} else {
53
-			$this->expiration = $expiration;
54
-			$this->userManager = $userManager;
55
-		}
56
-	}
50
+        if (is_null($expiration) || is_null($userManager)) {
51
+            $this->fixDIForJobs();
52
+        } else {
53
+            $this->expiration = $expiration;
54
+            $this->userManager = $userManager;
55
+        }
56
+    }
57 57
 
58
-	protected function fixDIForJobs() {
59
-		$application = new Application();
60
-		$this->expiration = $application->getContainer()->query('Expiration');
61
-		$this->userManager = \OC::$server->getUserManager();
62
-	}
58
+    protected function fixDIForJobs() {
59
+        $application = new Application();
60
+        $this->expiration = $application->getContainer()->query('Expiration');
61
+        $this->userManager = \OC::$server->getUserManager();
62
+    }
63 63
 
64
-	protected function run($argument) {
65
-		$maxAge = $this->expiration->getMaxAgeAsTimestamp();
66
-		if (!$maxAge) {
67
-			return;
68
-		}
64
+    protected function run($argument) {
65
+        $maxAge = $this->expiration->getMaxAgeAsTimestamp();
66
+        if (!$maxAge) {
67
+            return;
68
+        }
69 69
 
70
-		$this->userManager->callForSeenUsers(function(IUser $user) {
71
-			$uid = $user->getUID();
72
-			if (!$this->setupFS($uid)) {
73
-				return;
74
-			}
75
-			Storage::expireOlderThanMaxForUser($uid);
76
-		});
77
-	}
70
+        $this->userManager->callForSeenUsers(function(IUser $user) {
71
+            $uid = $user->getUID();
72
+            if (!$this->setupFS($uid)) {
73
+                return;
74
+            }
75
+            Storage::expireOlderThanMaxForUser($uid);
76
+        });
77
+    }
78 78
 
79
-	/**
80
-	 * Act on behalf on trash item owner
81
-	 * @param string $user
82
-	 * @return boolean
83
-	 */
84
-	protected function setupFS($user) {
85
-		\OC_Util::tearDownFS();
86
-		\OC_Util::setupFS($user);
79
+    /**
80
+     * Act on behalf on trash item owner
81
+     * @param string $user
82
+     * @return boolean
83
+     */
84
+    protected function setupFS($user) {
85
+        \OC_Util::tearDownFS();
86
+        \OC_Util::setupFS($user);
87 87
 
88
-		// Check if this user has a versions directory
89
-		$view = new \OC\Files\View('/' . $user);
90
-		if (!$view->is_dir('/files_versions')) {
91
-			return false;
92
-		}
88
+        // Check if this user has a versions directory
89
+        $view = new \OC\Files\View('/' . $user);
90
+        if (!$view->is_dir('/files_versions')) {
91
+            return false;
92
+        }
93 93
 
94
-		return true;
95
-	}
94
+        return true;
95
+    }
96 96
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Hooks.php 1 patch
Indentation   +133 added lines, -133 removed lines patch added patch discarded remove patch
@@ -35,137 +35,137 @@
 block discarded – undo
35 35
 
36 36
 class Hooks {
37 37
 
38
-	public static function connectHooks() {
39
-		// Listen to write signals
40
-		\OCP\Util::connectHook('OC_Filesystem', 'write', 'OCA\Files_Versions\Hooks', 'write_hook');
41
-		// Listen to delete and rename signals
42
-		\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Files_Versions\Hooks', 'remove_hook');
43
-		\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Versions\Hooks', 'pre_remove_hook');
44
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Versions\Hooks', 'rename_hook');
45
-		\OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Files_Versions\Hooks', 'copy_hook');
46
-		\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Files_Versions\Hooks', 'pre_renameOrCopy_hook');
47
-		\OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Files_Versions\Hooks', 'pre_renameOrCopy_hook');
48
-
49
-		$eventDispatcher = \OC::$server->getEventDispatcher();
50
-		$eventDispatcher->addListener('OCA\Files::loadAdditionalScripts', ['OCA\Files_Versions\Hooks', 'onLoadFilesAppScripts']);
51
-	}
52
-
53
-	/**
54
-	 * listen to write event.
55
-	 */
56
-	public static function write_hook( $params ) {
57
-
58
-		if (\OCP\App::isEnabled('files_versions')) {
59
-			$path = $params[\OC\Files\Filesystem::signal_param_path];
60
-			if($path<>'') {
61
-				Storage::store($path);
62
-			}
63
-		}
64
-	}
65
-
66
-
67
-	/**
68
-	 * Erase versions of deleted file
69
-	 * @param array $params
70
-	 *
71
-	 * This function is connected to the delete signal of OC_Filesystem
72
-	 * cleanup the versions directory if the actual file gets deleted
73
-	 */
74
-	public static function remove_hook($params) {
75
-
76
-		if (\OCP\App::isEnabled('files_versions')) {
77
-			$path = $params[\OC\Files\Filesystem::signal_param_path];
78
-			if($path<>'') {
79
-				Storage::delete($path);
80
-			}
81
-		}
82
-	}
83
-
84
-	/**
85
-	 * mark file as "deleted" so that we can clean up the versions if the file is gone
86
-	 * @param array $params
87
-	 */
88
-	public static function pre_remove_hook($params) {
89
-		$path = $params[\OC\Files\Filesystem::signal_param_path];
90
-			if($path<>'') {
91
-				Storage::markDeletedFile($path);
92
-			}
93
-	}
94
-
95
-	/**
96
-	 * rename/move versions of renamed/moved files
97
-	 * @param array $params array with oldpath and newpath
98
-	 *
99
-	 * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
100
-	 * of the stored versions along the actual file
101
-	 */
102
-	public static function rename_hook($params) {
103
-
104
-		if (\OCP\App::isEnabled('files_versions')) {
105
-			$oldpath = $params['oldpath'];
106
-			$newpath = $params['newpath'];
107
-			if($oldpath<>'' && $newpath<>'') {
108
-				Storage::renameOrCopy($oldpath, $newpath, 'rename');
109
-			}
110
-		}
111
-	}
112
-
113
-	/**
114
-	 * copy versions of copied files
115
-	 * @param array $params array with oldpath and newpath
116
-	 *
117
-	 * This function is connected to the copy signal of OC_Filesystem and copies the
118
-	 * the stored versions to the new location
119
-	 */
120
-	public static function copy_hook($params) {
121
-
122
-		if (\OCP\App::isEnabled('files_versions')) {
123
-			$oldpath = $params['oldpath'];
124
-			$newpath = $params['newpath'];
125
-			if($oldpath<>'' && $newpath<>'') {
126
-				Storage::renameOrCopy($oldpath, $newpath, 'copy');
127
-			}
128
-		}
129
-	}
130
-
131
-	/**
132
-	 * Remember owner and the owner path of the source file.
133
-	 * If the file already exists, then it was a upload of a existing file
134
-	 * over the web interface and we call Storage::store() directly
135
-	 *
136
-	 * @param array $params array with oldpath and newpath
137
-	 *
138
-	 */
139
-	public static function pre_renameOrCopy_hook($params) {
140
-		if (\OCP\App::isEnabled('files_versions')) {
141
-
142
-			// if we rename a movable mount point, then the versions don't have
143
-			// to be renamed
144
-			$absOldPath = \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files' . $params['oldpath']);
145
-			$manager = \OC\Files\Filesystem::getMountManager();
146
-			$mount = $manager->find($absOldPath);
147
-			$internalPath = $mount->getInternalPath($absOldPath);
148
-			if ($internalPath === '' and $mount instanceof \OC\Files\Mount\MoveableMount) {
149
-				return;
150
-			}
151
-
152
-			$view = new \OC\Files\View(\OCP\User::getUser() . '/files');
153
-			if ($view->file_exists($params['newpath'])) {
154
-				Storage::store($params['newpath']);
155
-			} else {
156
-				Storage::setSourcePathAndUser($params['oldpath']);
157
-			}
158
-
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * Load additional scripts when the files app is visible
164
-	 */
165
-	public static function onLoadFilesAppScripts() {
166
-		\OCP\Util::addScript('files_versions', 'versionmodel');
167
-		\OCP\Util::addScript('files_versions', 'versioncollection');
168
-		\OCP\Util::addScript('files_versions', 'versionstabview');
169
-		\OCP\Util::addScript('files_versions', 'filesplugin');
170
-	}
38
+    public static function connectHooks() {
39
+        // Listen to write signals
40
+        \OCP\Util::connectHook('OC_Filesystem', 'write', 'OCA\Files_Versions\Hooks', 'write_hook');
41
+        // Listen to delete and rename signals
42
+        \OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Files_Versions\Hooks', 'remove_hook');
43
+        \OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Versions\Hooks', 'pre_remove_hook');
44
+        \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Versions\Hooks', 'rename_hook');
45
+        \OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Files_Versions\Hooks', 'copy_hook');
46
+        \OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Files_Versions\Hooks', 'pre_renameOrCopy_hook');
47
+        \OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Files_Versions\Hooks', 'pre_renameOrCopy_hook');
48
+
49
+        $eventDispatcher = \OC::$server->getEventDispatcher();
50
+        $eventDispatcher->addListener('OCA\Files::loadAdditionalScripts', ['OCA\Files_Versions\Hooks', 'onLoadFilesAppScripts']);
51
+    }
52
+
53
+    /**
54
+     * listen to write event.
55
+     */
56
+    public static function write_hook( $params ) {
57
+
58
+        if (\OCP\App::isEnabled('files_versions')) {
59
+            $path = $params[\OC\Files\Filesystem::signal_param_path];
60
+            if($path<>'') {
61
+                Storage::store($path);
62
+            }
63
+        }
64
+    }
65
+
66
+
67
+    /**
68
+     * Erase versions of deleted file
69
+     * @param array $params
70
+     *
71
+     * This function is connected to the delete signal of OC_Filesystem
72
+     * cleanup the versions directory if the actual file gets deleted
73
+     */
74
+    public static function remove_hook($params) {
75
+
76
+        if (\OCP\App::isEnabled('files_versions')) {
77
+            $path = $params[\OC\Files\Filesystem::signal_param_path];
78
+            if($path<>'') {
79
+                Storage::delete($path);
80
+            }
81
+        }
82
+    }
83
+
84
+    /**
85
+     * mark file as "deleted" so that we can clean up the versions if the file is gone
86
+     * @param array $params
87
+     */
88
+    public static function pre_remove_hook($params) {
89
+        $path = $params[\OC\Files\Filesystem::signal_param_path];
90
+            if($path<>'') {
91
+                Storage::markDeletedFile($path);
92
+            }
93
+    }
94
+
95
+    /**
96
+     * rename/move versions of renamed/moved files
97
+     * @param array $params array with oldpath and newpath
98
+     *
99
+     * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
100
+     * of the stored versions along the actual file
101
+     */
102
+    public static function rename_hook($params) {
103
+
104
+        if (\OCP\App::isEnabled('files_versions')) {
105
+            $oldpath = $params['oldpath'];
106
+            $newpath = $params['newpath'];
107
+            if($oldpath<>'' && $newpath<>'') {
108
+                Storage::renameOrCopy($oldpath, $newpath, 'rename');
109
+            }
110
+        }
111
+    }
112
+
113
+    /**
114
+     * copy versions of copied files
115
+     * @param array $params array with oldpath and newpath
116
+     *
117
+     * This function is connected to the copy signal of OC_Filesystem and copies the
118
+     * the stored versions to the new location
119
+     */
120
+    public static function copy_hook($params) {
121
+
122
+        if (\OCP\App::isEnabled('files_versions')) {
123
+            $oldpath = $params['oldpath'];
124
+            $newpath = $params['newpath'];
125
+            if($oldpath<>'' && $newpath<>'') {
126
+                Storage::renameOrCopy($oldpath, $newpath, 'copy');
127
+            }
128
+        }
129
+    }
130
+
131
+    /**
132
+     * Remember owner and the owner path of the source file.
133
+     * If the file already exists, then it was a upload of a existing file
134
+     * over the web interface and we call Storage::store() directly
135
+     *
136
+     * @param array $params array with oldpath and newpath
137
+     *
138
+     */
139
+    public static function pre_renameOrCopy_hook($params) {
140
+        if (\OCP\App::isEnabled('files_versions')) {
141
+
142
+            // if we rename a movable mount point, then the versions don't have
143
+            // to be renamed
144
+            $absOldPath = \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files' . $params['oldpath']);
145
+            $manager = \OC\Files\Filesystem::getMountManager();
146
+            $mount = $manager->find($absOldPath);
147
+            $internalPath = $mount->getInternalPath($absOldPath);
148
+            if ($internalPath === '' and $mount instanceof \OC\Files\Mount\MoveableMount) {
149
+                return;
150
+            }
151
+
152
+            $view = new \OC\Files\View(\OCP\User::getUser() . '/files');
153
+            if ($view->file_exists($params['newpath'])) {
154
+                Storage::store($params['newpath']);
155
+            } else {
156
+                Storage::setSourcePathAndUser($params['oldpath']);
157
+            }
158
+
159
+        }
160
+    }
161
+
162
+    /**
163
+     * Load additional scripts when the files app is visible
164
+     */
165
+    public static function onLoadFilesAppScripts() {
166
+        \OCP\Util::addScript('files_versions', 'versionmodel');
167
+        \OCP\Util::addScript('files_versions', 'versioncollection');
168
+        \OCP\Util::addScript('files_versions', 'versionstabview');
169
+        \OCP\Util::addScript('files_versions', 'filesplugin');
170
+    }
171 171
 }
Please login to merge, or discard this patch.