Passed
Push — master ( 645109...008e6d )
by Christoph
12:14 queued 12s
created
apps/theming/lib/ThemingDefaults.php 2 patches
Indentation   +375 added lines, -375 removed lines patch added patch discarded remove patch
@@ -50,379 +50,379 @@
 block discarded – undo
50 50
 
51 51
 class ThemingDefaults extends \OC_Defaults {
52 52
 
53
-	/** @var IConfig */
54
-	private $config;
55
-	/** @var IL10N */
56
-	private $l;
57
-	/** @var ImageManager */
58
-	private $imageManager;
59
-	/** @var IURLGenerator */
60
-	private $urlGenerator;
61
-	/** @var ICacheFactory */
62
-	private $cacheFactory;
63
-	/** @var Util */
64
-	private $util;
65
-	/** @var IAppManager */
66
-	private $appManager;
67
-	/** @var INavigationManager */
68
-	private $navigationManager;
69
-
70
-	/** @var string */
71
-	private $name;
72
-	/** @var string */
73
-	private $title;
74
-	/** @var string */
75
-	private $entity;
76
-	/** @var string */
77
-	private $url;
78
-	/** @var string */
79
-	private $color;
80
-
81
-	/** @var string */
82
-	private $iTunesAppId;
83
-	/** @var string */
84
-	private $iOSClientUrl;
85
-	/** @var string */
86
-	private $AndroidClientUrl;
87
-
88
-	/**
89
-	 * ThemingDefaults constructor.
90
-	 *
91
-	 * @param IConfig $config
92
-	 * @param IL10N $l
93
-	 * @param ImageManager $imageManager
94
-	 * @param IURLGenerator $urlGenerator
95
-	 * @param ICacheFactory $cacheFactory
96
-	 * @param Util $util
97
-	 * @param IAppManager $appManager
98
-	 */
99
-	public function __construct(IConfig $config,
100
-								IL10N $l,
101
-								IURLGenerator $urlGenerator,
102
-								ICacheFactory $cacheFactory,
103
-								Util $util,
104
-								ImageManager $imageManager,
105
-								IAppManager $appManager,
106
-								INavigationManager $navigationManager
107
-	) {
108
-		parent::__construct();
109
-		$this->config = $config;
110
-		$this->l = $l;
111
-		$this->imageManager = $imageManager;
112
-		$this->urlGenerator = $urlGenerator;
113
-		$this->cacheFactory = $cacheFactory;
114
-		$this->util = $util;
115
-		$this->appManager = $appManager;
116
-		$this->navigationManager = $navigationManager;
117
-
118
-		$this->name = parent::getName();
119
-		$this->title = parent::getTitle();
120
-		$this->entity = parent::getEntity();
121
-		$this->url = parent::getBaseUrl();
122
-		$this->color = parent::getColorPrimary();
123
-		$this->iTunesAppId = parent::getiTunesAppId();
124
-		$this->iOSClientUrl = parent::getiOSClientUrl();
125
-		$this->AndroidClientUrl = parent::getAndroidClientUrl();
126
-	}
127
-
128
-	public function getName() {
129
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
130
-	}
131
-
132
-	public function getHTMLName() {
133
-		return $this->config->getAppValue('theming', 'name', $this->name);
134
-	}
135
-
136
-	public function getTitle() {
137
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
138
-	}
139
-
140
-	public function getEntity() {
141
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
142
-	}
143
-
144
-	public function getBaseUrl() {
145
-		return $this->config->getAppValue('theming', 'url', $this->url);
146
-	}
147
-
148
-	public function getSlogan() {
149
-		return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan()));
150
-	}
151
-
152
-	public function getImprintUrl() {
153
-		return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
154
-	}
155
-
156
-	public function getPrivacyUrl() {
157
-		return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
158
-	}
159
-
160
-	public function getShortFooter() {
161
-		$slogan = $this->getSlogan();
162
-		$baseUrl = $this->getBaseUrl();
163
-		if ($baseUrl !== '') {
164
-			$footer = '<a href="' . $baseUrl . '" target="_blank"' .
165
-				' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
166
-		} else {
167
-			$footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
168
-		}
169
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
170
-
171
-		$links = [
172
-			[
173
-				'text' => $this->l->t('Legal notice'),
174
-				'url' => (string)$this->getImprintUrl()
175
-			],
176
-			[
177
-				'text' => $this->l->t('Privacy policy'),
178
-				'url' => (string)$this->getPrivacyUrl()
179
-			],
180
-		];
181
-
182
-		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
183
-		$guestNavigation = array_map(function ($nav) {
184
-			return [
185
-				'text' => $nav['name'],
186
-				'url' => $nav['href']
187
-			];
188
-		}, $navigation);
189
-		$links = array_merge($links, $guestNavigation);
190
-
191
-		$legalLinks = ''; $divider = '';
192
-		foreach($links as $link) {
193
-			if($link['url'] !== ''
194
-				&& filter_var($link['url'], FILTER_VALIDATE_URL)
195
-			) {
196
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
197
-					' rel="noreferrer noopener">' . $link['text'] . '</a>';
198
-				$divider = ' · ';
199
-			}
200
-		}
201
-		if($legalLinks !== '') {
202
-			$footer .= '<br/>' . $legalLinks;
203
-		}
204
-
205
-		return $footer;
206
-	}
207
-
208
-	/**
209
-	 * Color that is used for the header as well as for mail headers
210
-	 *
211
-	 * @return string
212
-	 */
213
-	public function getColorPrimary() {
214
-		return $this->config->getAppValue('theming', 'color', $this->color);
215
-	}
216
-
217
-	/**
218
-	 * Themed logo url
219
-	 *
220
-	 * @param bool $useSvg Whether to point to the SVG image or a fallback
221
-	 * @return string
222
-	 */
223
-	public function getLogo($useSvg = true): string {
224
-		$logo = $this->config->getAppValue('theming', 'logoMime', false);
225
-
226
-		$logoExists = true;
227
-		try {
228
-			$this->imageManager->getImage('logo', $useSvg);
229
-		} catch (\Exception $e) {
230
-			$logoExists = false;
231
-		}
232
-
233
-		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
234
-
235
-		if(!$logo || !$logoExists) {
236
-			if($useSvg) {
237
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
238
-			} else {
239
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
240
-			}
241
-			return $logo . '?v=' . $cacheBusterCounter;
242
-		}
243
-
244
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
245
-	}
246
-
247
-	/**
248
-	 * Themed background image url
249
-	 *
250
-	 * @return string
251
-	 */
252
-	public function getBackground(): string {
253
-		return $this->imageManager->getImageUrl('background');
254
-	}
255
-
256
-	/**
257
-	 * @return string
258
-	 */
259
-	public function getiTunesAppId() {
260
-		return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
261
-	}
262
-
263
-	/**
264
-	 * @return string
265
-	 */
266
-	public function getiOSClientUrl() {
267
-		return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
268
-	}
269
-
270
-	/**
271
-	 * @return string
272
-	 */
273
-	public function getAndroidClientUrl() {
274
-		return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
275
-	}
276
-
277
-
278
-	/**
279
-	 * @return array scss variables to overwrite
280
-	 */
281
-	public function getScssVariables() {
282
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
283
-		if ($value = $cache->get('getScssVariables')) {
284
-			return $value;
285
-		}
286
-
287
-		$variables = [
288
-			'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'",
289
-			'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
290
-			'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
291
-			'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
292
-			'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
293
-		];
294
-
295
-		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
296
-		$variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
297
-		$variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
298
-		$variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
299
-		$variables['image-login-plain'] = 'false';
300
-
301
-		if ($this->config->getAppValue('theming', 'color', null) !== null) {
302
-			$variables['color-primary'] = $this->getColorPrimary();
303
-			$variables['color-primary-text'] = $this->getTextColorPrimary();
304
-			$variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
305
-		}
306
-
307
-		if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') {
308
-			$variables['image-login-plain'] = 'true';
309
-		}
310
-
311
-		$variables['has-legal-links'] = 'false';
312
-		if($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
313
-			$variables['has-legal-links'] = 'true';
314
-		}
315
-
316
-		$cache->set('getScssVariables', $variables);
317
-		return $variables;
318
-	}
319
-
320
-	/**
321
-	 * Check if the image should be replaced by the theming app
322
-	 * and return the new image location then
323
-	 *
324
-	 * @param string $app name of the app
325
-	 * @param string $image filename of the image
326
-	 * @return bool|string false if image should not replaced, otherwise the location of the image
327
-	 */
328
-	public function replaceImagePath($app, $image) {
329
-		if ($app === '' || $app === 'files_sharing') {
330
-			$app = 'core';
331
-		}
332
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
333
-
334
-		try {
335
-			$customFavicon = $this->imageManager->getImage('favicon');
336
-		} catch (NotFoundException $e) {
337
-			$customFavicon = null;
338
-		}
339
-
340
-		$route = false;
341
-		if ($image === 'favicon.ico' && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
342
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
343
-		}
344
-		if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
345
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
346
-		}
347
-		if ($image === 'manifest.json') {
348
-			try {
349
-				$appPath = $this->appManager->getAppPath($app);
350
-				if (file_exists($appPath . '/img/manifest.json')) {
351
-					return false;
352
-				}
353
-			} catch (AppPathNotFoundException $e) {}
354
-			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest');
355
-		}
356
-		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
357
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
358
-		}
359
-
360
-		if ($route) {
361
-			return $route . '?v=' . $cacheBusterValue;
362
-		}
363
-
364
-		return false;
365
-	}
366
-
367
-	/**
368
-	 * Increases the cache buster key
369
-	 */
370
-	private function increaseCacheBuster() {
371
-		$cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0');
372
-		$this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1);
373
-		$this->cacheFactory->createDistributed('theming-')->clear();
374
-		$this->cacheFactory->createDistributed('imagePath')->clear();
375
-
376
-	}
377
-
378
-	/**
379
-	 * Update setting in the database
380
-	 *
381
-	 * @param string $setting
382
-	 * @param string $value
383
-	 */
384
-	public function set($setting, $value) {
385
-		$this->config->setAppValue('theming', $setting, $value);
386
-		$this->increaseCacheBuster();
387
-	}
388
-
389
-	/**
390
-	 * Revert settings to the default value
391
-	 *
392
-	 * @param string $setting setting which should be reverted
393
-	 * @return string default value
394
-	 */
395
-	public function undo($setting) {
396
-		$this->config->deleteAppValue('theming', $setting);
397
-		$this->increaseCacheBuster();
398
-
399
-		switch ($setting) {
400
-			case 'name':
401
-				$returnValue = $this->getEntity();
402
-				break;
403
-			case 'url':
404
-				$returnValue = $this->getBaseUrl();
405
-				break;
406
-			case 'slogan':
407
-				$returnValue = $this->getSlogan();
408
-				break;
409
-			case 'color':
410
-				$returnValue = $this->getColorPrimary();
411
-				break;
412
-			default:
413
-				$returnValue = '';
414
-				break;
415
-		}
416
-
417
-		return $returnValue;
418
-	}
419
-
420
-	/**
421
-	 * Color of text in the header and primary buttons
422
-	 *
423
-	 * @return string
424
-	 */
425
-	public function getTextColorPrimary() {
426
-		return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
427
-	}
53
+    /** @var IConfig */
54
+    private $config;
55
+    /** @var IL10N */
56
+    private $l;
57
+    /** @var ImageManager */
58
+    private $imageManager;
59
+    /** @var IURLGenerator */
60
+    private $urlGenerator;
61
+    /** @var ICacheFactory */
62
+    private $cacheFactory;
63
+    /** @var Util */
64
+    private $util;
65
+    /** @var IAppManager */
66
+    private $appManager;
67
+    /** @var INavigationManager */
68
+    private $navigationManager;
69
+
70
+    /** @var string */
71
+    private $name;
72
+    /** @var string */
73
+    private $title;
74
+    /** @var string */
75
+    private $entity;
76
+    /** @var string */
77
+    private $url;
78
+    /** @var string */
79
+    private $color;
80
+
81
+    /** @var string */
82
+    private $iTunesAppId;
83
+    /** @var string */
84
+    private $iOSClientUrl;
85
+    /** @var string */
86
+    private $AndroidClientUrl;
87
+
88
+    /**
89
+     * ThemingDefaults constructor.
90
+     *
91
+     * @param IConfig $config
92
+     * @param IL10N $l
93
+     * @param ImageManager $imageManager
94
+     * @param IURLGenerator $urlGenerator
95
+     * @param ICacheFactory $cacheFactory
96
+     * @param Util $util
97
+     * @param IAppManager $appManager
98
+     */
99
+    public function __construct(IConfig $config,
100
+                                IL10N $l,
101
+                                IURLGenerator $urlGenerator,
102
+                                ICacheFactory $cacheFactory,
103
+                                Util $util,
104
+                                ImageManager $imageManager,
105
+                                IAppManager $appManager,
106
+                                INavigationManager $navigationManager
107
+    ) {
108
+        parent::__construct();
109
+        $this->config = $config;
110
+        $this->l = $l;
111
+        $this->imageManager = $imageManager;
112
+        $this->urlGenerator = $urlGenerator;
113
+        $this->cacheFactory = $cacheFactory;
114
+        $this->util = $util;
115
+        $this->appManager = $appManager;
116
+        $this->navigationManager = $navigationManager;
117
+
118
+        $this->name = parent::getName();
119
+        $this->title = parent::getTitle();
120
+        $this->entity = parent::getEntity();
121
+        $this->url = parent::getBaseUrl();
122
+        $this->color = parent::getColorPrimary();
123
+        $this->iTunesAppId = parent::getiTunesAppId();
124
+        $this->iOSClientUrl = parent::getiOSClientUrl();
125
+        $this->AndroidClientUrl = parent::getAndroidClientUrl();
126
+    }
127
+
128
+    public function getName() {
129
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
130
+    }
131
+
132
+    public function getHTMLName() {
133
+        return $this->config->getAppValue('theming', 'name', $this->name);
134
+    }
135
+
136
+    public function getTitle() {
137
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
138
+    }
139
+
140
+    public function getEntity() {
141
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
142
+    }
143
+
144
+    public function getBaseUrl() {
145
+        return $this->config->getAppValue('theming', 'url', $this->url);
146
+    }
147
+
148
+    public function getSlogan() {
149
+        return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan()));
150
+    }
151
+
152
+    public function getImprintUrl() {
153
+        return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
154
+    }
155
+
156
+    public function getPrivacyUrl() {
157
+        return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
158
+    }
159
+
160
+    public function getShortFooter() {
161
+        $slogan = $this->getSlogan();
162
+        $baseUrl = $this->getBaseUrl();
163
+        if ($baseUrl !== '') {
164
+            $footer = '<a href="' . $baseUrl . '" target="_blank"' .
165
+                ' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
166
+        } else {
167
+            $footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
168
+        }
169
+        $footer .= ($slogan !== '' ? ' – ' . $slogan : '');
170
+
171
+        $links = [
172
+            [
173
+                'text' => $this->l->t('Legal notice'),
174
+                'url' => (string)$this->getImprintUrl()
175
+            ],
176
+            [
177
+                'text' => $this->l->t('Privacy policy'),
178
+                'url' => (string)$this->getPrivacyUrl()
179
+            ],
180
+        ];
181
+
182
+        $navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
183
+        $guestNavigation = array_map(function ($nav) {
184
+            return [
185
+                'text' => $nav['name'],
186
+                'url' => $nav['href']
187
+            ];
188
+        }, $navigation);
189
+        $links = array_merge($links, $guestNavigation);
190
+
191
+        $legalLinks = ''; $divider = '';
192
+        foreach($links as $link) {
193
+            if($link['url'] !== ''
194
+                && filter_var($link['url'], FILTER_VALIDATE_URL)
195
+            ) {
196
+                $legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
197
+                    ' rel="noreferrer noopener">' . $link['text'] . '</a>';
198
+                $divider = ' · ';
199
+            }
200
+        }
201
+        if($legalLinks !== '') {
202
+            $footer .= '<br/>' . $legalLinks;
203
+        }
204
+
205
+        return $footer;
206
+    }
207
+
208
+    /**
209
+     * Color that is used for the header as well as for mail headers
210
+     *
211
+     * @return string
212
+     */
213
+    public function getColorPrimary() {
214
+        return $this->config->getAppValue('theming', 'color', $this->color);
215
+    }
216
+
217
+    /**
218
+     * Themed logo url
219
+     *
220
+     * @param bool $useSvg Whether to point to the SVG image or a fallback
221
+     * @return string
222
+     */
223
+    public function getLogo($useSvg = true): string {
224
+        $logo = $this->config->getAppValue('theming', 'logoMime', false);
225
+
226
+        $logoExists = true;
227
+        try {
228
+            $this->imageManager->getImage('logo', $useSvg);
229
+        } catch (\Exception $e) {
230
+            $logoExists = false;
231
+        }
232
+
233
+        $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
234
+
235
+        if(!$logo || !$logoExists) {
236
+            if($useSvg) {
237
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
238
+            } else {
239
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
240
+            }
241
+            return $logo . '?v=' . $cacheBusterCounter;
242
+        }
243
+
244
+        return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
245
+    }
246
+
247
+    /**
248
+     * Themed background image url
249
+     *
250
+     * @return string
251
+     */
252
+    public function getBackground(): string {
253
+        return $this->imageManager->getImageUrl('background');
254
+    }
255
+
256
+    /**
257
+     * @return string
258
+     */
259
+    public function getiTunesAppId() {
260
+        return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
261
+    }
262
+
263
+    /**
264
+     * @return string
265
+     */
266
+    public function getiOSClientUrl() {
267
+        return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
268
+    }
269
+
270
+    /**
271
+     * @return string
272
+     */
273
+    public function getAndroidClientUrl() {
274
+        return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
275
+    }
276
+
277
+
278
+    /**
279
+     * @return array scss variables to overwrite
280
+     */
281
+    public function getScssVariables() {
282
+        $cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
283
+        if ($value = $cache->get('getScssVariables')) {
284
+            return $value;
285
+        }
286
+
287
+        $variables = [
288
+            'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'",
289
+            'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
290
+            'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
291
+            'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
292
+            'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
293
+        ];
294
+
295
+        $variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
296
+        $variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
297
+        $variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
298
+        $variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
299
+        $variables['image-login-plain'] = 'false';
300
+
301
+        if ($this->config->getAppValue('theming', 'color', null) !== null) {
302
+            $variables['color-primary'] = $this->getColorPrimary();
303
+            $variables['color-primary-text'] = $this->getTextColorPrimary();
304
+            $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
305
+        }
306
+
307
+        if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') {
308
+            $variables['image-login-plain'] = 'true';
309
+        }
310
+
311
+        $variables['has-legal-links'] = 'false';
312
+        if($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
313
+            $variables['has-legal-links'] = 'true';
314
+        }
315
+
316
+        $cache->set('getScssVariables', $variables);
317
+        return $variables;
318
+    }
319
+
320
+    /**
321
+     * Check if the image should be replaced by the theming app
322
+     * and return the new image location then
323
+     *
324
+     * @param string $app name of the app
325
+     * @param string $image filename of the image
326
+     * @return bool|string false if image should not replaced, otherwise the location of the image
327
+     */
328
+    public function replaceImagePath($app, $image) {
329
+        if ($app === '' || $app === 'files_sharing') {
330
+            $app = 'core';
331
+        }
332
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
333
+
334
+        try {
335
+            $customFavicon = $this->imageManager->getImage('favicon');
336
+        } catch (NotFoundException $e) {
337
+            $customFavicon = null;
338
+        }
339
+
340
+        $route = false;
341
+        if ($image === 'favicon.ico' && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
342
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
343
+        }
344
+        if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($customFavicon !== null || $this->imageManager->shouldReplaceIcons())) {
345
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
346
+        }
347
+        if ($image === 'manifest.json') {
348
+            try {
349
+                $appPath = $this->appManager->getAppPath($app);
350
+                if (file_exists($appPath . '/img/manifest.json')) {
351
+                    return false;
352
+                }
353
+            } catch (AppPathNotFoundException $e) {}
354
+            $route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest');
355
+        }
356
+        if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
357
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
358
+        }
359
+
360
+        if ($route) {
361
+            return $route . '?v=' . $cacheBusterValue;
362
+        }
363
+
364
+        return false;
365
+    }
366
+
367
+    /**
368
+     * Increases the cache buster key
369
+     */
370
+    private function increaseCacheBuster() {
371
+        $cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0');
372
+        $this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1);
373
+        $this->cacheFactory->createDistributed('theming-')->clear();
374
+        $this->cacheFactory->createDistributed('imagePath')->clear();
375
+
376
+    }
377
+
378
+    /**
379
+     * Update setting in the database
380
+     *
381
+     * @param string $setting
382
+     * @param string $value
383
+     */
384
+    public function set($setting, $value) {
385
+        $this->config->setAppValue('theming', $setting, $value);
386
+        $this->increaseCacheBuster();
387
+    }
388
+
389
+    /**
390
+     * Revert settings to the default value
391
+     *
392
+     * @param string $setting setting which should be reverted
393
+     * @return string default value
394
+     */
395
+    public function undo($setting) {
396
+        $this->config->deleteAppValue('theming', $setting);
397
+        $this->increaseCacheBuster();
398
+
399
+        switch ($setting) {
400
+            case 'name':
401
+                $returnValue = $this->getEntity();
402
+                break;
403
+            case 'url':
404
+                $returnValue = $this->getBaseUrl();
405
+                break;
406
+            case 'slogan':
407
+                $returnValue = $this->getSlogan();
408
+                break;
409
+            case 'color':
410
+                $returnValue = $this->getColorPrimary();
411
+                break;
412
+            default:
413
+                $returnValue = '';
414
+                break;
415
+        }
416
+
417
+        return $returnValue;
418
+    }
419
+
420
+    /**
421
+     * Color of text in the header and primary buttons
422
+     *
423
+     * @return string
424
+     */
425
+    public function getTextColorPrimary() {
426
+        return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
427
+    }
428 428
 }
Please login to merge, or discard this patch.
Spacing   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -150,37 +150,37 @@  discard block
 block discarded – undo
150 150
 	}
151 151
 
152 152
 	public function getImprintUrl() {
153
-		return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
153
+		return (string) $this->config->getAppValue('theming', 'imprintUrl', '');
154 154
 	}
155 155
 
156 156
 	public function getPrivacyUrl() {
157
-		return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
157
+		return (string) $this->config->getAppValue('theming', 'privacyUrl', '');
158 158
 	}
159 159
 
160 160
 	public function getShortFooter() {
161 161
 		$slogan = $this->getSlogan();
162 162
 		$baseUrl = $this->getBaseUrl();
163 163
 		if ($baseUrl !== '') {
164
-			$footer = '<a href="' . $baseUrl . '" target="_blank"' .
165
-				' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
164
+			$footer = '<a href="'.$baseUrl.'" target="_blank"'.
165
+				' rel="noreferrer noopener" class="entity-name">'.$this->getEntity().'</a>';
166 166
 		} else {
167
-			$footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
167
+			$footer = '<span class="entity-name">'.$this->getEntity().'</span>';
168 168
 		}
169
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
169
+		$footer .= ($slogan !== '' ? ' – '.$slogan : '');
170 170
 
171 171
 		$links = [
172 172
 			[
173 173
 				'text' => $this->l->t('Legal notice'),
174
-				'url' => (string)$this->getImprintUrl()
174
+				'url' => (string) $this->getImprintUrl()
175 175
 			],
176 176
 			[
177 177
 				'text' => $this->l->t('Privacy policy'),
178
-				'url' => (string)$this->getPrivacyUrl()
178
+				'url' => (string) $this->getPrivacyUrl()
179 179
 			],
180 180
 		];
181 181
 
182 182
 		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
183
-		$guestNavigation = array_map(function ($nav) {
183
+		$guestNavigation = array_map(function($nav) {
184 184
 			return [
185 185
 				'text' => $nav['name'],
186 186
 				'url' => $nav['href']
@@ -189,17 +189,17 @@  discard block
 block discarded – undo
189 189
 		$links = array_merge($links, $guestNavigation);
190 190
 
191 191
 		$legalLinks = ''; $divider = '';
192
-		foreach($links as $link) {
193
-			if($link['url'] !== ''
192
+		foreach ($links as $link) {
193
+			if ($link['url'] !== ''
194 194
 				&& filter_var($link['url'], FILTER_VALIDATE_URL)
195 195
 			) {
196
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
197
-					' rel="noreferrer noopener">' . $link['text'] . '</a>';
196
+				$legalLinks .= $divider.'<a href="'.$link['url'].'" class="legal" target="_blank"'.
197
+					' rel="noreferrer noopener">'.$link['text'].'</a>';
198 198
 				$divider = ' · ';
199 199
 			}
200 200
 		}
201
-		if($legalLinks !== '') {
202
-			$footer .= '<br/>' . $legalLinks;
201
+		if ($legalLinks !== '') {
202
+			$footer .= '<br/>'.$legalLinks;
203 203
 		}
204 204
 
205 205
 		return $footer;
@@ -232,16 +232,16 @@  discard block
 block discarded – undo
232 232
 
233 233
 		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
234 234
 
235
-		if(!$logo || !$logoExists) {
236
-			if($useSvg) {
235
+		if (!$logo || !$logoExists) {
236
+			if ($useSvg) {
237 237
 				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
238 238
 			} else {
239 239
 				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
240 240
 			}
241
-			return $logo . '?v=' . $cacheBusterCounter;
241
+			return $logo.'?v='.$cacheBusterCounter;
242 242
 		}
243 243
 
244
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
244
+		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', ['key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter]);
245 245
 	}
246 246
 
247 247
 	/**
@@ -279,17 +279,17 @@  discard block
 block discarded – undo
279 279
 	 * @return array scss variables to overwrite
280 280
 	 */
281 281
 	public function getScssVariables() {
282
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
282
+		$cache = $this->cacheFactory->createDistributed('theming-'.$this->urlGenerator->getBaseUrl());
283 283
 		if ($value = $cache->get('getScssVariables')) {
284 284
 			return $value;
285 285
 		}
286 286
 
287 287
 		$variables = [
288
-			'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'",
289
-			'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
290
-			'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
291
-			'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
292
-			'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
288
+			'theming-cachebuster' => "'".$this->config->getAppValue('theming', 'cachebuster', '0')."'",
289
+			'theming-logo-mime' => "'".$this->config->getAppValue('theming', 'logoMime')."'",
290
+			'theming-background-mime' => "'".$this->config->getAppValue('theming', 'backgroundMime')."'",
291
+			'theming-logoheader-mime' => "'".$this->config->getAppValue('theming', 'logoheaderMime')."'",
292
+			'theming-favicon-mime' => "'".$this->config->getAppValue('theming', 'faviconMime')."'"
293 293
 		];
294 294
 
295 295
 		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
 		}
310 310
 
311 311
 		$variables['has-legal-links'] = 'false';
312
-		if($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
312
+		if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
313 313
 			$variables['has-legal-links'] = 'true';
314 314
 		}
315 315
 
@@ -347,18 +347,18 @@  discard block
 block discarded – undo
347 347
 		if ($image === 'manifest.json') {
348 348
 			try {
349 349
 				$appPath = $this->appManager->getAppPath($app);
350
-				if (file_exists($appPath . '/img/manifest.json')) {
350
+				if (file_exists($appPath.'/img/manifest.json')) {
351 351
 					return false;
352 352
 				}
353 353
 			} catch (AppPathNotFoundException $e) {}
354 354
 			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest');
355 355
 		}
356
-		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
356
+		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT.'/core/img/'.$image)) {
357 357
 			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
358 358
 		}
359 359
 
360 360
 		if ($route) {
361
-			return $route . '?v=' . $cacheBusterValue;
361
+			return $route.'?v='.$cacheBusterValue;
362 362
 		}
363 363
 
364 364
 		return false;
@@ -369,7 +369,7 @@  discard block
 block discarded – undo
369 369
 	 */
370 370
 	private function increaseCacheBuster() {
371 371
 		$cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0');
372
-		$this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1);
372
+		$this->config->setAppValue('theming', 'cachebuster', (int) $cacheBusterKey + 1);
373 373
 		$this->cacheFactory->createDistributed('theming-')->clear();
374 374
 		$this->cacheFactory->createDistributed('imagePath')->clear();
375 375
 
Please login to merge, or discard this patch.
apps/theming/appinfo/app.php 1 patch
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -32,29 +32,29 @@
 block discarded – undo
32 32
 $app->getContainer()->registerCapability(\OCA\Theming\Capabilities::class);
33 33
 
34 34
 $linkToCSS = \OC::$server->getURLGenerator()->linkToRoute(
35
-	'theming.Theming.getStylesheet',
36
-	[
37
-		'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'),
38
-	]
35
+    'theming.Theming.getStylesheet',
36
+    [
37
+        'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'),
38
+    ]
39 39
 );
40 40
 \OCP\Util::addHeader(
41
-	'link',
42
-	[
43
-		'rel' => 'stylesheet',
44
-		'href' => $linkToCSS,
45
-	]
41
+    'link',
42
+    [
43
+        'rel' => 'stylesheet',
44
+        'href' => $linkToCSS,
45
+    ]
46 46
 );
47 47
 
48 48
 $linkToJs = \OC::$server->getURLGenerator()->linkToRoute(
49
-	'theming.Theming.getJavascript',
50
-	[
51
-		'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'),
52
-	]
49
+    'theming.Theming.getJavascript',
50
+    [
51
+        'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'),
52
+    ]
53 53
 );
54 54
 \OCP\Util::addHeader(
55
-	'script',
56
-	[
57
-		'src' => $linkToJs,
58
-		'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce()
59
-	], ''
55
+    'script',
56
+    [
57
+        'src' => $linkToJs,
58
+        'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce()
59
+    ], ''
60 60
 );
Please login to merge, or discard this patch.
apps/files_versions/lib/Storage.php 2 patches
Indentation   +796 added lines, -796 removed lines patch added patch discarded remove patch
@@ -58,801 +58,801 @@
 block discarded – undo
58 58
 
59 59
 class Storage {
60 60
 
61
-	const DEFAULTENABLED=true;
62
-	const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
63
-	const VERSIONS_ROOT = 'files_versions/';
64
-
65
-	const DELETE_TRIGGER_MASTER_REMOVED = 0;
66
-	const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1;
67
-	const DELETE_TRIGGER_QUOTA_EXCEEDED = 2;
68
-
69
-	// files for which we can remove the versions after the delete operation was successful
70
-	private static $deletedFiles = [];
71
-
72
-	private static $sourcePathAndUser = [];
73
-
74
-	private static $max_versions_per_interval = [
75
-		//first 10sec, one version every 2sec
76
-		1 => ['intervalEndsAfter' => 10,      'step' => 2],
77
-		//next minute, one version every 10sec
78
-		2 => ['intervalEndsAfter' => 60,      'step' => 10],
79
-		//next hour, one version every minute
80
-		3 => ['intervalEndsAfter' => 3600,    'step' => 60],
81
-		//next 24h, one version every hour
82
-		4 => ['intervalEndsAfter' => 86400,   'step' => 3600],
83
-		//next 30days, one version per day
84
-		5 => ['intervalEndsAfter' => 2592000, 'step' => 86400],
85
-		//until the end one version per week
86
-		6 => ['intervalEndsAfter' => -1,      'step' => 604800],
87
-	];
88
-
89
-	/** @var \OCA\Files_Versions\AppInfo\Application */
90
-	private static $application;
91
-
92
-	/**
93
-	 * get the UID of the owner of the file and the path to the file relative to
94
-	 * owners files folder
95
-	 *
96
-	 * @param string $filename
97
-	 * @return array
98
-	 * @throws \OC\User\NoUserException
99
-	 */
100
-	public static function getUidAndFilename($filename) {
101
-		$uid = Filesystem::getOwner($filename);
102
-		$userManager = \OC::$server->getUserManager();
103
-		// if the user with the UID doesn't exists, e.g. because the UID points
104
-		// to a remote user with a federated cloud ID we use the current logged-in
105
-		// user. We need a valid local user to create the versions
106
-		if (!$userManager->userExists($uid)) {
107
-			$uid = User::getUser();
108
-		}
109
-		Filesystem::initMountPoints($uid);
110
-		if ($uid !== User::getUser()) {
111
-			$info = Filesystem::getFileInfo($filename);
112
-			$ownerView = new View('/'.$uid.'/files');
113
-			try {
114
-				$filename = $ownerView->getPath($info['fileid']);
115
-				// make sure that the file name doesn't end with a trailing slash
116
-				// can for example happen single files shared across servers
117
-				$filename = rtrim($filename, '/');
118
-			} catch (NotFoundException $e) {
119
-				$filename = null;
120
-			}
121
-		}
122
-		return [$uid, $filename];
123
-	}
124
-
125
-	/**
126
-	 * Remember the owner and the owner path of the source file
127
-	 *
128
-	 * @param string $source source path
129
-	 */
130
-	public static function setSourcePathAndUser($source) {
131
-		list($uid, $path) = self::getUidAndFilename($source);
132
-		self::$sourcePathAndUser[$source] = ['uid' => $uid, 'path' => $path];
133
-	}
134
-
135
-	/**
136
-	 * Gets the owner and the owner path from the source path
137
-	 *
138
-	 * @param string $source source path
139
-	 * @return array with user id and path
140
-	 */
141
-	public static function getSourcePathAndUser($source) {
142
-
143
-		if (isset(self::$sourcePathAndUser[$source])) {
144
-			$uid = self::$sourcePathAndUser[$source]['uid'];
145
-			$path = self::$sourcePathAndUser[$source]['path'];
146
-			unset(self::$sourcePathAndUser[$source]);
147
-		} else {
148
-			$uid = $path = false;
149
-		}
150
-		return [$uid, $path];
151
-	}
152
-
153
-	/**
154
-	 * get current size of all versions from a given user
155
-	 *
156
-	 * @param string $user user who owns the versions
157
-	 * @return int versions size
158
-	 */
159
-	private static function getVersionsSize($user) {
160
-		$view = new View('/' . $user);
161
-		$fileInfo = $view->getFileInfo('/files_versions');
162
-		return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
163
-	}
164
-
165
-	/**
166
-	 * store a new version of a file.
167
-	 */
168
-	public static function store($filename) {
169
-
170
-		// if the file gets streamed we need to remove the .part extension
171
-		// to get the right target
172
-		$ext = pathinfo($filename, PATHINFO_EXTENSION);
173
-		if ($ext === 'part') {
174
-			$filename = substr($filename, 0, -5);
175
-		}
176
-
177
-		// we only handle existing files
178
-		if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
179
-			return false;
180
-		}
181
-
182
-		list($uid, $filename) = self::getUidAndFilename($filename);
183
-
184
-		$files_view = new View('/'.$uid .'/files');
185
-
186
-		$eventDispatcher = \OC::$server->getEventDispatcher();
187
-		$fileInfo = $files_view->getFileInfo($filename);
188
-		$id = $fileInfo->getId();
189
-		$nodes = \OC::$server->getRootFolder()->getUserFolder($uid)->getById($id);
190
-		foreach ($nodes as $node) {
191
-			$event = new CreateVersionEvent($node);
192
-			$eventDispatcher->dispatch('OCA\Files_Versions::createVersion', $event);
193
-			if ($event->shouldCreateVersion() === false) {
194
-				return false;
195
-			}
196
-		}
197
-
198
-		// no use making versions for empty files
199
-		if ($fileInfo->getSize() === 0) {
200
-			return false;
201
-		}
202
-
203
-		/** @var IVersionManager $versionManager */
204
-		$versionManager = \OC::$server->query(IVersionManager::class);
205
-		$userManager = \OC::$server->getUserManager();
206
-		$user = $userManager->get($uid);
207
-
208
-		$versionManager->createVersion($user, $fileInfo);
209
-	}
210
-
211
-
212
-	/**
213
-	 * mark file as deleted so that we can remove the versions if the file is gone
214
-	 * @param string $path
215
-	 */
216
-	public static function markDeletedFile($path) {
217
-		list($uid, $filename) = self::getUidAndFilename($path);
218
-		self::$deletedFiles[$path] = [
219
-			'uid' => $uid,
220
-			'filename' => $filename];
221
-	}
222
-
223
-	/**
224
-	 * delete the version from the storage and cache
225
-	 *
226
-	 * @param View $view
227
-	 * @param string $path
228
-	 */
229
-	protected static function deleteVersion($view, $path) {
230
-		$view->unlink($path);
231
-		/**
232
-		 * @var \OC\Files\Storage\Storage $storage
233
-		 * @var string $internalPath
234
-		 */
235
-		list($storage, $internalPath) = $view->resolvePath($path);
236
-		$cache = $storage->getCache($internalPath);
237
-		$cache->remove($internalPath);
238
-	}
239
-
240
-	/**
241
-	 * Delete versions of a file
242
-	 */
243
-	public static function delete($path) {
244
-
245
-		$deletedFile = self::$deletedFiles[$path];
246
-		$uid = $deletedFile['uid'];
247
-		$filename = $deletedFile['filename'];
248
-
249
-		if (!Filesystem::file_exists($path)) {
250
-
251
-			$view = new View('/' . $uid . '/files_versions');
252
-
253
-			$versions = self::getVersions($uid, $filename);
254
-			if (!empty($versions)) {
255
-				foreach ($versions as $v) {
256
-					\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
257
-					self::deleteVersion($view, $filename . '.v' . $v['version']);
258
-					\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
259
-				}
260
-			}
261
-		}
262
-		unset(self::$deletedFiles[$path]);
263
-	}
264
-
265
-	/**
266
-	 * Rename or copy versions of a file of the given paths
267
-	 *
268
-	 * @param string $sourcePath source path of the file to move, relative to
269
-	 * the currently logged in user's "files" folder
270
-	 * @param string $targetPath target path of the file to move, relative to
271
-	 * the currently logged in user's "files" folder
272
-	 * @param string $operation can be 'copy' or 'rename'
273
-	 */
274
-	public static function renameOrCopy($sourcePath, $targetPath, $operation) {
275
-		list($sourceOwner, $sourcePath) = self::getSourcePathAndUser($sourcePath);
276
-
277
-		// it was a upload of a existing file if no old path exists
278
-		// in this case the pre-hook already called the store method and we can
279
-		// stop here
280
-		if ($sourcePath === false) {
281
-			return true;
282
-		}
283
-
284
-		list($targetOwner, $targetPath) = self::getUidAndFilename($targetPath);
285
-
286
-		$sourcePath = ltrim($sourcePath, '/');
287
-		$targetPath = ltrim($targetPath, '/');
288
-
289
-		$rootView = new View('');
290
-
291
-		// did we move a directory ?
292
-		if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
293
-			// does the directory exists for versions too ?
294
-			if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) {
295
-				// create missing dirs if necessary
296
-				self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
297
-
298
-				// move the directory containing the versions
299
-				$rootView->$operation(
300
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath,
301
-					'/' . $targetOwner . '/files_versions/' . $targetPath
302
-				);
303
-			}
304
-		} else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) {
305
-			// create missing dirs if necessary
306
-			self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
307
-
308
-			foreach ($versions as $v) {
309
-				// move each version one by one to the target directory
310
-				$rootView->$operation(
311
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
312
-					'/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
313
-				);
314
-			}
315
-		}
316
-
317
-		// if we moved versions directly for a file, schedule expiration check for that file
318
-		if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
319
-			self::scheduleExpire($targetOwner, $targetPath);
320
-		}
321
-
322
-	}
323
-
324
-	/**
325
-	 * Rollback to an old version of a file.
326
-	 *
327
-	 * @param string $file file name
328
-	 * @param int $revision revision timestamp
329
-	 * @return bool
330
-	 */
331
-	public static function rollback(string $file, int $revision, IUser $user) {
332
-
333
-		// add expected leading slash
334
-		$filename = '/' . ltrim($file, '/');
335
-
336
-		// Fetch the userfolder to trigger view hooks
337
-		$userFolder = \OC::$server->getUserFolder($user->getUID());
338
-
339
-		$users_view = new View('/'.$user->getUID());
340
-		$files_view = new View('/'. $user->getUID().'/files');
341
-
342
-		$versionCreated = false;
343
-
344
-		$fileInfo = $files_view->getFileInfo($file);
345
-
346
-		// check if user has the permissions to revert a version
347
-		if (!$fileInfo->isUpdateable()) {
348
-			return false;
349
-		}
350
-
351
-		//first create a new version
352
-		$version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
353
-		if (!$users_view->file_exists($version)) {
354
-			$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
355
-			$versionCreated = true;
356
-		}
357
-
358
-		$fileToRestore =  'files_versions' . $filename . '.v' . $revision;
359
-
360
-		// Restore encrypted version of the old file for the newly restored file
361
-		// This has to happen manually here since the file is manually copied below
362
-		$oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
363
-		$oldFileInfo = $users_view->getFileInfo($fileToRestore);
364
-		$cache = $fileInfo->getStorage()->getCache();
365
-		$cache->update(
366
-			$fileInfo->getId(), [
367
-				'encrypted' => $oldVersion,
368
-				'encryptedVersion' => $oldVersion,
369
-				'size' => $oldFileInfo->getSize()
370
-			]
371
-		);
372
-
373
-		// rollback
374
-		if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
375
-			$files_view->touch($file, $revision);
376
-			Storage::scheduleExpire($user->getUID(), $file);
377
-
378
-			$node = $userFolder->get($file);
379
-
380
-			// TODO: move away from those legacy hooks!
381
-			\OC_Hook::emit('\OCP\Versions', 'rollback', [
382
-				'path' => $filename,
383
-				'revision' => $revision,
384
-				'node' => $node,
385
-			]);
386
-			return true;
387
-		} else if ($versionCreated) {
388
-			self::deleteVersion($users_view, $version);
389
-		}
390
-
391
-		return false;
392
-
393
-	}
394
-
395
-	/**
396
-	 * Stream copy file contents from $path1 to $path2
397
-	 *
398
-	 * @param View $view view to use for copying
399
-	 * @param string $path1 source file to copy
400
-	 * @param string $path2 target file
401
-	 *
402
-	 * @return bool true for success, false otherwise
403
-	 */
404
-	private static function copyFileContents($view, $path1, $path2) {
405
-		/** @var \OC\Files\Storage\Storage $storage1 */
406
-		list($storage1, $internalPath1) = $view->resolvePath($path1);
407
-		/** @var \OC\Files\Storage\Storage $storage2 */
408
-		list($storage2, $internalPath2) = $view->resolvePath($path2);
409
-
410
-		$view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
411
-		$view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
412
-
413
-		// TODO add a proper way of overwriting a file while maintaining file ids
414
-		if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
415
-			$source = $storage1->fopen($internalPath1, 'r');
416
-			$target = $storage2->fopen($internalPath2, 'w');
417
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
418
-			fclose($source);
419
-			fclose($target);
420
-
421
-			if ($result !== false) {
422
-				$storage1->unlink($internalPath1);
423
-			}
424
-		} else {
425
-			$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
426
-		}
427
-
428
-		$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
429
-		$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
430
-
431
-		return ($result !== false);
432
-	}
433
-
434
-	/**
435
-	 * get a list of all available versions of a file in descending chronological order
436
-	 * @param string $uid user id from the owner of the file
437
-	 * @param string $filename file to find versions of, relative to the user files dir
438
-	 * @param string $userFullPath
439
-	 * @return array versions newest version first
440
-	 */
441
-	public static function getVersions($uid, $filename, $userFullPath = '') {
442
-		$versions = [];
443
-		if (empty($filename)) {
444
-			return $versions;
445
-		}
446
-		// fetch for old versions
447
-		$view = new View('/' . $uid . '/');
448
-
449
-		$pathinfo = pathinfo($filename);
450
-		$versionedFile = $pathinfo['basename'];
451
-
452
-		$dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']);
453
-
454
-		$dirContent = false;
455
-		if ($view->is_dir($dir)) {
456
-			$dirContent = $view->opendir($dir);
457
-		}
458
-
459
-		if ($dirContent === false) {
460
-			return $versions;
461
-		}
462
-
463
-		if (is_resource($dirContent)) {
464
-			while (($entryName = readdir($dirContent)) !== false) {
465
-				if (!Filesystem::isIgnoredDir($entryName)) {
466
-					$pathparts = pathinfo($entryName);
467
-					$filename = $pathparts['filename'];
468
-					if ($filename === $versionedFile) {
469
-						$pathparts = pathinfo($entryName);
470
-						$timestamp = substr($pathparts['extension'], 1);
471
-						$filename = $pathparts['filename'];
472
-						$key = $timestamp . '#' . $filename;
473
-						$versions[$key]['version'] = $timestamp;
474
-						$versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
475
-						if (empty($userFullPath)) {
476
-							$versions[$key]['preview'] = '';
477
-						} else {
478
-							$versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
479
-						}
480
-						$versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
481
-						$versions[$key]['name'] = $versionedFile;
482
-						$versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
483
-						$versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile);
484
-					}
485
-				}
486
-			}
487
-			closedir($dirContent);
488
-		}
489
-
490
-		// sort with newest version first
491
-		krsort($versions);
492
-
493
-		return $versions;
494
-	}
495
-
496
-	/**
497
-	 * Expire versions that older than max version retention time
498
-	 * @param string $uid
499
-	 */
500
-	public static function expireOlderThanMaxForUser($uid) {
501
-		$expiration = self::getExpiration();
502
-		$threshold = $expiration->getMaxAgeAsTimestamp();
503
-		$versions = self::getAllVersions($uid);
504
-		if (!$threshold || !array_key_exists('all', $versions)) {
505
-			return;
506
-		}
507
-
508
-		$toDelete = [];
509
-		foreach (array_reverse($versions['all']) as $key => $version) {
510
-			if ((int)$version['version'] <$threshold) {
511
-				$toDelete[$key] = $version;
512
-			} else {
513
-				//Versions are sorted by time - nothing mo to iterate.
514
-				break;
515
-			}
516
-		}
517
-
518
-		$view = new View('/' . $uid . '/files_versions');
519
-		if (!empty($toDelete)) {
520
-			foreach ($toDelete as $version) {
521
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
522
-				self::deleteVersion($view, $version['path'] . '.v' . $version['version']);
523
-				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
524
-			}
525
-		}
526
-	}
527
-
528
-	/**
529
-	 * translate a timestamp into a string like "5 days ago"
530
-	 * @param int $timestamp
531
-	 * @return string for example "5 days ago"
532
-	 */
533
-	private static function getHumanReadableTimestamp($timestamp) {
534
-
535
-		$diff = time() - $timestamp;
536
-
537
-		if ($diff < 60) { // first minute
538
-			return  $diff . " seconds ago";
539
-		} elseif ($diff < 3600) { //first hour
540
-			return round($diff / 60) . " minutes ago";
541
-		} elseif ($diff < 86400) { // first day
542
-			return round($diff / 3600) . " hours ago";
543
-		} elseif ($diff < 604800) { //first week
544
-			return round($diff / 86400) . " days ago";
545
-		} elseif ($diff < 2419200) { //first month
546
-			return round($diff / 604800) . " weeks ago";
547
-		} elseif ($diff < 29030400) { // first year
548
-			return round($diff / 2419200) . " months ago";
549
-		} else {
550
-			return round($diff / 29030400) . " years ago";
551
-		}
552
-
553
-	}
554
-
555
-	/**
556
-	 * returns all stored file versions from a given user
557
-	 * @param string $uid id of the user
558
-	 * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
559
-	 */
560
-	private static function getAllVersions($uid) {
561
-		$view = new View('/' . $uid . '/');
562
-		$dirs = [self::VERSIONS_ROOT];
563
-		$versions = [];
564
-
565
-		while (!empty($dirs)) {
566
-			$dir = array_pop($dirs);
567
-			$files = $view->getDirectoryContent($dir);
568
-
569
-			foreach ($files as $file) {
570
-				$fileData = $file->getData();
571
-				$filePath = $dir . '/' . $fileData['name'];
572
-				if ($file['type'] === 'dir') {
573
-					$dirs[] = $filePath;
574
-				} else {
575
-					$versionsBegin = strrpos($filePath, '.v');
576
-					$relPathStart = strlen(self::VERSIONS_ROOT);
577
-					$version = substr($filePath, $versionsBegin + 2);
578
-					$relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart);
579
-					$key = $version . '#' . $relpath;
580
-					$versions[$key] = ['path' => $relpath, 'timestamp' => $version];
581
-				}
582
-			}
583
-		}
584
-
585
-		// newest version first
586
-		krsort($versions);
587
-
588
-		$result = [];
589
-
590
-		foreach ($versions as $key => $value) {
591
-			$size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']);
592
-			$filename = $value['path'];
593
-
594
-			$result['all'][$key]['version'] = $value['timestamp'];
595
-			$result['all'][$key]['path'] = $filename;
596
-			$result['all'][$key]['size'] = $size;
597
-
598
-			$result['by_file'][$filename][$key]['version'] = $value['timestamp'];
599
-			$result['by_file'][$filename][$key]['path'] = $filename;
600
-			$result['by_file'][$filename][$key]['size'] = $size;
601
-		}
602
-
603
-		return $result;
604
-	}
605
-
606
-	/**
607
-	 * get list of files we want to expire
608
-	 * @param array $versions list of versions
609
-	 * @param integer $time
610
-	 * @param bool $quotaExceeded is versions storage limit reached
611
-	 * @return array containing the list of to deleted versions and the size of them
612
-	 */
613
-	protected static function getExpireList($time, $versions, $quotaExceeded = false) {
614
-		$expiration = self::getExpiration();
615
-
616
-		if ($expiration->shouldAutoExpire()) {
617
-			list($toDelete, $size) = self::getAutoExpireList($time, $versions);
618
-		} else {
619
-			$size = 0;
620
-			$toDelete = [];  // versions we want to delete
621
-		}
622
-
623
-		foreach ($versions as $key => $version) {
624
-			if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) {
625
-				$size += $version['size'];
626
-				$toDelete[$key] = $version['path'] . '.v' . $version['version'];
627
-			}
628
-		}
629
-
630
-		return [$toDelete, $size];
631
-	}
632
-
633
-	/**
634
-	 * get list of files we want to expire
635
-	 * @param array $versions list of versions
636
-	 * @param integer $time
637
-	 * @return array containing the list of to deleted versions and the size of them
638
-	 */
639
-	protected static function getAutoExpireList($time, $versions) {
640
-		$size = 0;
641
-		$toDelete = [];  // versions we want to delete
642
-
643
-		$interval = 1;
644
-		$step = Storage::$max_versions_per_interval[$interval]['step'];
645
-		if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
646
-			$nextInterval = -1;
647
-		} else {
648
-			$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
649
-		}
650
-
651
-		$firstVersion = reset($versions);
652
-		$firstKey = key($versions);
653
-		$prevTimestamp = $firstVersion['version'];
654
-		$nextVersion = $firstVersion['version'] - $step;
655
-		unset($versions[$firstKey]);
656
-
657
-		foreach ($versions as $key => $version) {
658
-			$newInterval = true;
659
-			while ($newInterval) {
660
-				if ($nextInterval === -1 || $prevTimestamp > $nextInterval) {
661
-					if ($version['version'] > $nextVersion) {
662
-						//distance between two version too small, mark to delete
663
-						$toDelete[$key] = $version['path'] . '.v' . $version['version'];
664
-						$size += $version['size'];
665
-						\OC::$server->getLogger()->info('Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, ['app' => 'files_versions']);
666
-					} else {
667
-						$nextVersion = $version['version'] - $step;
668
-						$prevTimestamp = $version['version'];
669
-					}
670
-					$newInterval = false; // version checked so we can move to the next one
671
-				} else { // time to move on to the next interval
672
-					$interval++;
673
-					$step = Storage::$max_versions_per_interval[$interval]['step'];
674
-					$nextVersion = $prevTimestamp - $step;
675
-					if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
676
-						$nextInterval = -1;
677
-					} else {
678
-						$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
679
-					}
680
-					$newInterval = true; // we changed the interval -> check same version with new interval
681
-				}
682
-			}
683
-		}
684
-
685
-		return [$toDelete, $size];
686
-	}
687
-
688
-	/**
689
-	 * Schedule versions expiration for the given file
690
-	 *
691
-	 * @param string $uid owner of the file
692
-	 * @param string $fileName file/folder for which to schedule expiration
693
-	 */
694
-	public static function scheduleExpire($uid, $fileName) {
695
-		// let the admin disable auto expire
696
-		$expiration = self::getExpiration();
697
-		if ($expiration->isEnabled()) {
698
-			$command = new Expire($uid, $fileName);
699
-			\OC::$server->getCommandBus()->push($command);
700
-		}
701
-	}
702
-
703
-	/**
704
-	 * Expire versions which exceed the quota.
705
-	 *
706
-	 * This will setup the filesystem for the given user but will not
707
-	 * tear it down afterwards.
708
-	 *
709
-	 * @param string $filename path to file to expire
710
-	 * @param string $uid user for which to expire the version
711
-	 * @return bool|int|null
712
-	 */
713
-	public static function expire($filename, $uid) {
714
-		$expiration = self::getExpiration();
715
-
716
-		if ($expiration->isEnabled()) {
717
-			// get available disk space for user
718
-			$user = \OC::$server->getUserManager()->get($uid);
719
-			if (is_null($user)) {
720
-				\OC::$server->getLogger()->error('Backends provided no user object for ' . $uid, ['app' => 'files_versions']);
721
-				throw new \OC\User\NoUserException('Backends provided no user object for ' . $uid);
722
-			}
723
-
724
-			\OC_Util::setupFS($uid);
725
-
726
-			if (!Filesystem::file_exists($filename)) {
727
-				return false;
728
-			}
729
-
730
-			if (empty($filename)) {
731
-				// file maybe renamed or deleted
732
-				return false;
733
-			}
734
-			$versionsFileview = new View('/'.$uid.'/files_versions');
735
-
736
-			$softQuota = true;
737
-			$quota = $user->getQuota();
738
-			if ($quota === null || $quota === 'none') {
739
-				$quota = Filesystem::free_space('/');
740
-				$softQuota = false;
741
-			} else {
742
-				$quota = \OCP\Util::computerFileSize($quota);
743
-			}
744
-
745
-			// make sure that we have the current size of the version history
746
-			$versionsSize = self::getVersionsSize($uid);
747
-
748
-			// calculate available space for version history
749
-			// subtract size of files and current versions size from quota
750
-			if ($quota >= 0) {
751
-				if ($softQuota) {
752
-					$userFolder = \OC::$server->getUserFolder($uid);
753
-					if(is_null($userFolder)) {
754
-						$availableSpace = 0;
755
-					} else {
756
-						$free = $quota - $userFolder->getSize(false); // remaining free space for user
757
-						if ($free > 0) {
758
-							$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
759
-						} else {
760
-							$availableSpace = $free - $versionsSize;
761
-						}
762
-					}
763
-				} else {
764
-					$availableSpace = $quota;
765
-				}
766
-			} else {
767
-				$availableSpace = PHP_INT_MAX;
768
-			}
769
-
770
-			$allVersions = Storage::getVersions($uid, $filename);
771
-
772
-			$time = time();
773
-			list($toDelete, $sizeOfDeletedVersions) = self::getExpireList($time, $allVersions, $availableSpace <= 0);
774
-
775
-			$availableSpace = $availableSpace + $sizeOfDeletedVersions;
776
-			$versionsSize = $versionsSize - $sizeOfDeletedVersions;
777
-
778
-			// if still not enough free space we rearrange the versions from all files
779
-			if ($availableSpace <= 0) {
780
-				$result = Storage::getAllVersions($uid);
781
-				$allVersions = $result['all'];
782
-
783
-				foreach ($result['by_file'] as $versions) {
784
-					list($toDeleteNew, $size) = self::getExpireList($time, $versions, $availableSpace <= 0);
785
-					$toDelete = array_merge($toDelete, $toDeleteNew);
786
-					$sizeOfDeletedVersions += $size;
787
-				}
788
-				$availableSpace = $availableSpace + $sizeOfDeletedVersions;
789
-				$versionsSize = $versionsSize - $sizeOfDeletedVersions;
790
-			}
791
-
792
-			$logger = \OC::$server->getLogger();
793
-			foreach($toDelete as $key => $path) {
794
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
795
-				self::deleteVersion($versionsFileview, $path);
796
-				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
797
-				unset($allVersions[$key]); // update array with the versions we keep
798
-				$logger->info('Expire: ' . $path, ['app' => 'files_versions']);
799
-			}
800
-
801
-			// Check if enough space is available after versions are rearranged.
802
-			// If not we delete the oldest versions until we meet the size limit for versions,
803
-			// but always keep the two latest versions
804
-			$numOfVersions = count($allVersions) -2 ;
805
-			$i = 0;
806
-			// sort oldest first and make sure that we start at the first element
807
-			ksort($allVersions);
808
-			reset($allVersions);
809
-			while ($availableSpace < 0 && $i < $numOfVersions) {
810
-				$version = current($allVersions);
811
-				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
812
-				self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
813
-				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
814
-				\OC::$server->getLogger()->info('running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'], ['app' => 'files_versions']);
815
-				$versionsSize -= $version['size'];
816
-				$availableSpace += $version['size'];
817
-				next($allVersions);
818
-				$i++;
819
-			}
820
-
821
-			return $versionsSize; // finally return the new size of the version history
822
-		}
823
-
824
-		return false;
825
-	}
826
-
827
-	/**
828
-	 * Create recursively missing directories inside of files_versions
829
-	 * that match the given path to a file.
830
-	 *
831
-	 * @param string $filename $path to a file, relative to the user's
832
-	 * "files" folder
833
-	 * @param View $view view on data/user/
834
-	 */
835
-	public static function createMissingDirectories($filename, $view) {
836
-		$dirname = Filesystem::normalizePath(dirname($filename));
837
-		$dirParts = explode('/', $dirname);
838
-		$dir = "/files_versions";
839
-		foreach ($dirParts as $part) {
840
-			$dir = $dir . '/' . $part;
841
-			if (!$view->file_exists($dir)) {
842
-				$view->mkdir($dir);
843
-			}
844
-		}
845
-	}
846
-
847
-	/**
848
-	 * Static workaround
849
-	 * @return Expiration
850
-	 */
851
-	protected static function getExpiration() {
852
-		if (self::$application === null) {
853
-			self::$application = \OC::$server->query(Application::class);
854
-		}
855
-		return self::$application->getContainer()->query(Expiration::class);
856
-	}
61
+    const DEFAULTENABLED=true;
62
+    const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
63
+    const VERSIONS_ROOT = 'files_versions/';
64
+
65
+    const DELETE_TRIGGER_MASTER_REMOVED = 0;
66
+    const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1;
67
+    const DELETE_TRIGGER_QUOTA_EXCEEDED = 2;
68
+
69
+    // files for which we can remove the versions after the delete operation was successful
70
+    private static $deletedFiles = [];
71
+
72
+    private static $sourcePathAndUser = [];
73
+
74
+    private static $max_versions_per_interval = [
75
+        //first 10sec, one version every 2sec
76
+        1 => ['intervalEndsAfter' => 10,      'step' => 2],
77
+        //next minute, one version every 10sec
78
+        2 => ['intervalEndsAfter' => 60,      'step' => 10],
79
+        //next hour, one version every minute
80
+        3 => ['intervalEndsAfter' => 3600,    'step' => 60],
81
+        //next 24h, one version every hour
82
+        4 => ['intervalEndsAfter' => 86400,   'step' => 3600],
83
+        //next 30days, one version per day
84
+        5 => ['intervalEndsAfter' => 2592000, 'step' => 86400],
85
+        //until the end one version per week
86
+        6 => ['intervalEndsAfter' => -1,      'step' => 604800],
87
+    ];
88
+
89
+    /** @var \OCA\Files_Versions\AppInfo\Application */
90
+    private static $application;
91
+
92
+    /**
93
+     * get the UID of the owner of the file and the path to the file relative to
94
+     * owners files folder
95
+     *
96
+     * @param string $filename
97
+     * @return array
98
+     * @throws \OC\User\NoUserException
99
+     */
100
+    public static function getUidAndFilename($filename) {
101
+        $uid = Filesystem::getOwner($filename);
102
+        $userManager = \OC::$server->getUserManager();
103
+        // if the user with the UID doesn't exists, e.g. because the UID points
104
+        // to a remote user with a federated cloud ID we use the current logged-in
105
+        // user. We need a valid local user to create the versions
106
+        if (!$userManager->userExists($uid)) {
107
+            $uid = User::getUser();
108
+        }
109
+        Filesystem::initMountPoints($uid);
110
+        if ($uid !== User::getUser()) {
111
+            $info = Filesystem::getFileInfo($filename);
112
+            $ownerView = new View('/'.$uid.'/files');
113
+            try {
114
+                $filename = $ownerView->getPath($info['fileid']);
115
+                // make sure that the file name doesn't end with a trailing slash
116
+                // can for example happen single files shared across servers
117
+                $filename = rtrim($filename, '/');
118
+            } catch (NotFoundException $e) {
119
+                $filename = null;
120
+            }
121
+        }
122
+        return [$uid, $filename];
123
+    }
124
+
125
+    /**
126
+     * Remember the owner and the owner path of the source file
127
+     *
128
+     * @param string $source source path
129
+     */
130
+    public static function setSourcePathAndUser($source) {
131
+        list($uid, $path) = self::getUidAndFilename($source);
132
+        self::$sourcePathAndUser[$source] = ['uid' => $uid, 'path' => $path];
133
+    }
134
+
135
+    /**
136
+     * Gets the owner and the owner path from the source path
137
+     *
138
+     * @param string $source source path
139
+     * @return array with user id and path
140
+     */
141
+    public static function getSourcePathAndUser($source) {
142
+
143
+        if (isset(self::$sourcePathAndUser[$source])) {
144
+            $uid = self::$sourcePathAndUser[$source]['uid'];
145
+            $path = self::$sourcePathAndUser[$source]['path'];
146
+            unset(self::$sourcePathAndUser[$source]);
147
+        } else {
148
+            $uid = $path = false;
149
+        }
150
+        return [$uid, $path];
151
+    }
152
+
153
+    /**
154
+     * get current size of all versions from a given user
155
+     *
156
+     * @param string $user user who owns the versions
157
+     * @return int versions size
158
+     */
159
+    private static function getVersionsSize($user) {
160
+        $view = new View('/' . $user);
161
+        $fileInfo = $view->getFileInfo('/files_versions');
162
+        return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
163
+    }
164
+
165
+    /**
166
+     * store a new version of a file.
167
+     */
168
+    public static function store($filename) {
169
+
170
+        // if the file gets streamed we need to remove the .part extension
171
+        // to get the right target
172
+        $ext = pathinfo($filename, PATHINFO_EXTENSION);
173
+        if ($ext === 'part') {
174
+            $filename = substr($filename, 0, -5);
175
+        }
176
+
177
+        // we only handle existing files
178
+        if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
179
+            return false;
180
+        }
181
+
182
+        list($uid, $filename) = self::getUidAndFilename($filename);
183
+
184
+        $files_view = new View('/'.$uid .'/files');
185
+
186
+        $eventDispatcher = \OC::$server->getEventDispatcher();
187
+        $fileInfo = $files_view->getFileInfo($filename);
188
+        $id = $fileInfo->getId();
189
+        $nodes = \OC::$server->getRootFolder()->getUserFolder($uid)->getById($id);
190
+        foreach ($nodes as $node) {
191
+            $event = new CreateVersionEvent($node);
192
+            $eventDispatcher->dispatch('OCA\Files_Versions::createVersion', $event);
193
+            if ($event->shouldCreateVersion() === false) {
194
+                return false;
195
+            }
196
+        }
197
+
198
+        // no use making versions for empty files
199
+        if ($fileInfo->getSize() === 0) {
200
+            return false;
201
+        }
202
+
203
+        /** @var IVersionManager $versionManager */
204
+        $versionManager = \OC::$server->query(IVersionManager::class);
205
+        $userManager = \OC::$server->getUserManager();
206
+        $user = $userManager->get($uid);
207
+
208
+        $versionManager->createVersion($user, $fileInfo);
209
+    }
210
+
211
+
212
+    /**
213
+     * mark file as deleted so that we can remove the versions if the file is gone
214
+     * @param string $path
215
+     */
216
+    public static function markDeletedFile($path) {
217
+        list($uid, $filename) = self::getUidAndFilename($path);
218
+        self::$deletedFiles[$path] = [
219
+            'uid' => $uid,
220
+            'filename' => $filename];
221
+    }
222
+
223
+    /**
224
+     * delete the version from the storage and cache
225
+     *
226
+     * @param View $view
227
+     * @param string $path
228
+     */
229
+    protected static function deleteVersion($view, $path) {
230
+        $view->unlink($path);
231
+        /**
232
+         * @var \OC\Files\Storage\Storage $storage
233
+         * @var string $internalPath
234
+         */
235
+        list($storage, $internalPath) = $view->resolvePath($path);
236
+        $cache = $storage->getCache($internalPath);
237
+        $cache->remove($internalPath);
238
+    }
239
+
240
+    /**
241
+     * Delete versions of a file
242
+     */
243
+    public static function delete($path) {
244
+
245
+        $deletedFile = self::$deletedFiles[$path];
246
+        $uid = $deletedFile['uid'];
247
+        $filename = $deletedFile['filename'];
248
+
249
+        if (!Filesystem::file_exists($path)) {
250
+
251
+            $view = new View('/' . $uid . '/files_versions');
252
+
253
+            $versions = self::getVersions($uid, $filename);
254
+            if (!empty($versions)) {
255
+                foreach ($versions as $v) {
256
+                    \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
257
+                    self::deleteVersion($view, $filename . '.v' . $v['version']);
258
+                    \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
259
+                }
260
+            }
261
+        }
262
+        unset(self::$deletedFiles[$path]);
263
+    }
264
+
265
+    /**
266
+     * Rename or copy versions of a file of the given paths
267
+     *
268
+     * @param string $sourcePath source path of the file to move, relative to
269
+     * the currently logged in user's "files" folder
270
+     * @param string $targetPath target path of the file to move, relative to
271
+     * the currently logged in user's "files" folder
272
+     * @param string $operation can be 'copy' or 'rename'
273
+     */
274
+    public static function renameOrCopy($sourcePath, $targetPath, $operation) {
275
+        list($sourceOwner, $sourcePath) = self::getSourcePathAndUser($sourcePath);
276
+
277
+        // it was a upload of a existing file if no old path exists
278
+        // in this case the pre-hook already called the store method and we can
279
+        // stop here
280
+        if ($sourcePath === false) {
281
+            return true;
282
+        }
283
+
284
+        list($targetOwner, $targetPath) = self::getUidAndFilename($targetPath);
285
+
286
+        $sourcePath = ltrim($sourcePath, '/');
287
+        $targetPath = ltrim($targetPath, '/');
288
+
289
+        $rootView = new View('');
290
+
291
+        // did we move a directory ?
292
+        if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
293
+            // does the directory exists for versions too ?
294
+            if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) {
295
+                // create missing dirs if necessary
296
+                self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
297
+
298
+                // move the directory containing the versions
299
+                $rootView->$operation(
300
+                    '/' . $sourceOwner . '/files_versions/' . $sourcePath,
301
+                    '/' . $targetOwner . '/files_versions/' . $targetPath
302
+                );
303
+            }
304
+        } else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) {
305
+            // create missing dirs if necessary
306
+            self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
307
+
308
+            foreach ($versions as $v) {
309
+                // move each version one by one to the target directory
310
+                $rootView->$operation(
311
+                    '/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
312
+                    '/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
313
+                );
314
+            }
315
+        }
316
+
317
+        // if we moved versions directly for a file, schedule expiration check for that file
318
+        if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
319
+            self::scheduleExpire($targetOwner, $targetPath);
320
+        }
321
+
322
+    }
323
+
324
+    /**
325
+     * Rollback to an old version of a file.
326
+     *
327
+     * @param string $file file name
328
+     * @param int $revision revision timestamp
329
+     * @return bool
330
+     */
331
+    public static function rollback(string $file, int $revision, IUser $user) {
332
+
333
+        // add expected leading slash
334
+        $filename = '/' . ltrim($file, '/');
335
+
336
+        // Fetch the userfolder to trigger view hooks
337
+        $userFolder = \OC::$server->getUserFolder($user->getUID());
338
+
339
+        $users_view = new View('/'.$user->getUID());
340
+        $files_view = new View('/'. $user->getUID().'/files');
341
+
342
+        $versionCreated = false;
343
+
344
+        $fileInfo = $files_view->getFileInfo($file);
345
+
346
+        // check if user has the permissions to revert a version
347
+        if (!$fileInfo->isUpdateable()) {
348
+            return false;
349
+        }
350
+
351
+        //first create a new version
352
+        $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
353
+        if (!$users_view->file_exists($version)) {
354
+            $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
355
+            $versionCreated = true;
356
+        }
357
+
358
+        $fileToRestore =  'files_versions' . $filename . '.v' . $revision;
359
+
360
+        // Restore encrypted version of the old file for the newly restored file
361
+        // This has to happen manually here since the file is manually copied below
362
+        $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
363
+        $oldFileInfo = $users_view->getFileInfo($fileToRestore);
364
+        $cache = $fileInfo->getStorage()->getCache();
365
+        $cache->update(
366
+            $fileInfo->getId(), [
367
+                'encrypted' => $oldVersion,
368
+                'encryptedVersion' => $oldVersion,
369
+                'size' => $oldFileInfo->getSize()
370
+            ]
371
+        );
372
+
373
+        // rollback
374
+        if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
375
+            $files_view->touch($file, $revision);
376
+            Storage::scheduleExpire($user->getUID(), $file);
377
+
378
+            $node = $userFolder->get($file);
379
+
380
+            // TODO: move away from those legacy hooks!
381
+            \OC_Hook::emit('\OCP\Versions', 'rollback', [
382
+                'path' => $filename,
383
+                'revision' => $revision,
384
+                'node' => $node,
385
+            ]);
386
+            return true;
387
+        } else if ($versionCreated) {
388
+            self::deleteVersion($users_view, $version);
389
+        }
390
+
391
+        return false;
392
+
393
+    }
394
+
395
+    /**
396
+     * Stream copy file contents from $path1 to $path2
397
+     *
398
+     * @param View $view view to use for copying
399
+     * @param string $path1 source file to copy
400
+     * @param string $path2 target file
401
+     *
402
+     * @return bool true for success, false otherwise
403
+     */
404
+    private static function copyFileContents($view, $path1, $path2) {
405
+        /** @var \OC\Files\Storage\Storage $storage1 */
406
+        list($storage1, $internalPath1) = $view->resolvePath($path1);
407
+        /** @var \OC\Files\Storage\Storage $storage2 */
408
+        list($storage2, $internalPath2) = $view->resolvePath($path2);
409
+
410
+        $view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
411
+        $view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
412
+
413
+        // TODO add a proper way of overwriting a file while maintaining file ids
414
+        if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
415
+            $source = $storage1->fopen($internalPath1, 'r');
416
+            $target = $storage2->fopen($internalPath2, 'w');
417
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
418
+            fclose($source);
419
+            fclose($target);
420
+
421
+            if ($result !== false) {
422
+                $storage1->unlink($internalPath1);
423
+            }
424
+        } else {
425
+            $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
426
+        }
427
+
428
+        $view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
429
+        $view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
430
+
431
+        return ($result !== false);
432
+    }
433
+
434
+    /**
435
+     * get a list of all available versions of a file in descending chronological order
436
+     * @param string $uid user id from the owner of the file
437
+     * @param string $filename file to find versions of, relative to the user files dir
438
+     * @param string $userFullPath
439
+     * @return array versions newest version first
440
+     */
441
+    public static function getVersions($uid, $filename, $userFullPath = '') {
442
+        $versions = [];
443
+        if (empty($filename)) {
444
+            return $versions;
445
+        }
446
+        // fetch for old versions
447
+        $view = new View('/' . $uid . '/');
448
+
449
+        $pathinfo = pathinfo($filename);
450
+        $versionedFile = $pathinfo['basename'];
451
+
452
+        $dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']);
453
+
454
+        $dirContent = false;
455
+        if ($view->is_dir($dir)) {
456
+            $dirContent = $view->opendir($dir);
457
+        }
458
+
459
+        if ($dirContent === false) {
460
+            return $versions;
461
+        }
462
+
463
+        if (is_resource($dirContent)) {
464
+            while (($entryName = readdir($dirContent)) !== false) {
465
+                if (!Filesystem::isIgnoredDir($entryName)) {
466
+                    $pathparts = pathinfo($entryName);
467
+                    $filename = $pathparts['filename'];
468
+                    if ($filename === $versionedFile) {
469
+                        $pathparts = pathinfo($entryName);
470
+                        $timestamp = substr($pathparts['extension'], 1);
471
+                        $filename = $pathparts['filename'];
472
+                        $key = $timestamp . '#' . $filename;
473
+                        $versions[$key]['version'] = $timestamp;
474
+                        $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
475
+                        if (empty($userFullPath)) {
476
+                            $versions[$key]['preview'] = '';
477
+                        } else {
478
+                            $versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
479
+                        }
480
+                        $versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
481
+                        $versions[$key]['name'] = $versionedFile;
482
+                        $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
483
+                        $versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile);
484
+                    }
485
+                }
486
+            }
487
+            closedir($dirContent);
488
+        }
489
+
490
+        // sort with newest version first
491
+        krsort($versions);
492
+
493
+        return $versions;
494
+    }
495
+
496
+    /**
497
+     * Expire versions that older than max version retention time
498
+     * @param string $uid
499
+     */
500
+    public static function expireOlderThanMaxForUser($uid) {
501
+        $expiration = self::getExpiration();
502
+        $threshold = $expiration->getMaxAgeAsTimestamp();
503
+        $versions = self::getAllVersions($uid);
504
+        if (!$threshold || !array_key_exists('all', $versions)) {
505
+            return;
506
+        }
507
+
508
+        $toDelete = [];
509
+        foreach (array_reverse($versions['all']) as $key => $version) {
510
+            if ((int)$version['version'] <$threshold) {
511
+                $toDelete[$key] = $version;
512
+            } else {
513
+                //Versions are sorted by time - nothing mo to iterate.
514
+                break;
515
+            }
516
+        }
517
+
518
+        $view = new View('/' . $uid . '/files_versions');
519
+        if (!empty($toDelete)) {
520
+            foreach ($toDelete as $version) {
521
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
522
+                self::deleteVersion($view, $version['path'] . '.v' . $version['version']);
523
+                \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
524
+            }
525
+        }
526
+    }
527
+
528
+    /**
529
+     * translate a timestamp into a string like "5 days ago"
530
+     * @param int $timestamp
531
+     * @return string for example "5 days ago"
532
+     */
533
+    private static function getHumanReadableTimestamp($timestamp) {
534
+
535
+        $diff = time() - $timestamp;
536
+
537
+        if ($diff < 60) { // first minute
538
+            return  $diff . " seconds ago";
539
+        } elseif ($diff < 3600) { //first hour
540
+            return round($diff / 60) . " minutes ago";
541
+        } elseif ($diff < 86400) { // first day
542
+            return round($diff / 3600) . " hours ago";
543
+        } elseif ($diff < 604800) { //first week
544
+            return round($diff / 86400) . " days ago";
545
+        } elseif ($diff < 2419200) { //first month
546
+            return round($diff / 604800) . " weeks ago";
547
+        } elseif ($diff < 29030400) { // first year
548
+            return round($diff / 2419200) . " months ago";
549
+        } else {
550
+            return round($diff / 29030400) . " years ago";
551
+        }
552
+
553
+    }
554
+
555
+    /**
556
+     * returns all stored file versions from a given user
557
+     * @param string $uid id of the user
558
+     * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
559
+     */
560
+    private static function getAllVersions($uid) {
561
+        $view = new View('/' . $uid . '/');
562
+        $dirs = [self::VERSIONS_ROOT];
563
+        $versions = [];
564
+
565
+        while (!empty($dirs)) {
566
+            $dir = array_pop($dirs);
567
+            $files = $view->getDirectoryContent($dir);
568
+
569
+            foreach ($files as $file) {
570
+                $fileData = $file->getData();
571
+                $filePath = $dir . '/' . $fileData['name'];
572
+                if ($file['type'] === 'dir') {
573
+                    $dirs[] = $filePath;
574
+                } else {
575
+                    $versionsBegin = strrpos($filePath, '.v');
576
+                    $relPathStart = strlen(self::VERSIONS_ROOT);
577
+                    $version = substr($filePath, $versionsBegin + 2);
578
+                    $relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart);
579
+                    $key = $version . '#' . $relpath;
580
+                    $versions[$key] = ['path' => $relpath, 'timestamp' => $version];
581
+                }
582
+            }
583
+        }
584
+
585
+        // newest version first
586
+        krsort($versions);
587
+
588
+        $result = [];
589
+
590
+        foreach ($versions as $key => $value) {
591
+            $size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']);
592
+            $filename = $value['path'];
593
+
594
+            $result['all'][$key]['version'] = $value['timestamp'];
595
+            $result['all'][$key]['path'] = $filename;
596
+            $result['all'][$key]['size'] = $size;
597
+
598
+            $result['by_file'][$filename][$key]['version'] = $value['timestamp'];
599
+            $result['by_file'][$filename][$key]['path'] = $filename;
600
+            $result['by_file'][$filename][$key]['size'] = $size;
601
+        }
602
+
603
+        return $result;
604
+    }
605
+
606
+    /**
607
+     * get list of files we want to expire
608
+     * @param array $versions list of versions
609
+     * @param integer $time
610
+     * @param bool $quotaExceeded is versions storage limit reached
611
+     * @return array containing the list of to deleted versions and the size of them
612
+     */
613
+    protected static function getExpireList($time, $versions, $quotaExceeded = false) {
614
+        $expiration = self::getExpiration();
615
+
616
+        if ($expiration->shouldAutoExpire()) {
617
+            list($toDelete, $size) = self::getAutoExpireList($time, $versions);
618
+        } else {
619
+            $size = 0;
620
+            $toDelete = [];  // versions we want to delete
621
+        }
622
+
623
+        foreach ($versions as $key => $version) {
624
+            if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) {
625
+                $size += $version['size'];
626
+                $toDelete[$key] = $version['path'] . '.v' . $version['version'];
627
+            }
628
+        }
629
+
630
+        return [$toDelete, $size];
631
+    }
632
+
633
+    /**
634
+     * get list of files we want to expire
635
+     * @param array $versions list of versions
636
+     * @param integer $time
637
+     * @return array containing the list of to deleted versions and the size of them
638
+     */
639
+    protected static function getAutoExpireList($time, $versions) {
640
+        $size = 0;
641
+        $toDelete = [];  // versions we want to delete
642
+
643
+        $interval = 1;
644
+        $step = Storage::$max_versions_per_interval[$interval]['step'];
645
+        if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
646
+            $nextInterval = -1;
647
+        } else {
648
+            $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
649
+        }
650
+
651
+        $firstVersion = reset($versions);
652
+        $firstKey = key($versions);
653
+        $prevTimestamp = $firstVersion['version'];
654
+        $nextVersion = $firstVersion['version'] - $step;
655
+        unset($versions[$firstKey]);
656
+
657
+        foreach ($versions as $key => $version) {
658
+            $newInterval = true;
659
+            while ($newInterval) {
660
+                if ($nextInterval === -1 || $prevTimestamp > $nextInterval) {
661
+                    if ($version['version'] > $nextVersion) {
662
+                        //distance between two version too small, mark to delete
663
+                        $toDelete[$key] = $version['path'] . '.v' . $version['version'];
664
+                        $size += $version['size'];
665
+                        \OC::$server->getLogger()->info('Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, ['app' => 'files_versions']);
666
+                    } else {
667
+                        $nextVersion = $version['version'] - $step;
668
+                        $prevTimestamp = $version['version'];
669
+                    }
670
+                    $newInterval = false; // version checked so we can move to the next one
671
+                } else { // time to move on to the next interval
672
+                    $interval++;
673
+                    $step = Storage::$max_versions_per_interval[$interval]['step'];
674
+                    $nextVersion = $prevTimestamp - $step;
675
+                    if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
676
+                        $nextInterval = -1;
677
+                    } else {
678
+                        $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
679
+                    }
680
+                    $newInterval = true; // we changed the interval -> check same version with new interval
681
+                }
682
+            }
683
+        }
684
+
685
+        return [$toDelete, $size];
686
+    }
687
+
688
+    /**
689
+     * Schedule versions expiration for the given file
690
+     *
691
+     * @param string $uid owner of the file
692
+     * @param string $fileName file/folder for which to schedule expiration
693
+     */
694
+    public static function scheduleExpire($uid, $fileName) {
695
+        // let the admin disable auto expire
696
+        $expiration = self::getExpiration();
697
+        if ($expiration->isEnabled()) {
698
+            $command = new Expire($uid, $fileName);
699
+            \OC::$server->getCommandBus()->push($command);
700
+        }
701
+    }
702
+
703
+    /**
704
+     * Expire versions which exceed the quota.
705
+     *
706
+     * This will setup the filesystem for the given user but will not
707
+     * tear it down afterwards.
708
+     *
709
+     * @param string $filename path to file to expire
710
+     * @param string $uid user for which to expire the version
711
+     * @return bool|int|null
712
+     */
713
+    public static function expire($filename, $uid) {
714
+        $expiration = self::getExpiration();
715
+
716
+        if ($expiration->isEnabled()) {
717
+            // get available disk space for user
718
+            $user = \OC::$server->getUserManager()->get($uid);
719
+            if (is_null($user)) {
720
+                \OC::$server->getLogger()->error('Backends provided no user object for ' . $uid, ['app' => 'files_versions']);
721
+                throw new \OC\User\NoUserException('Backends provided no user object for ' . $uid);
722
+            }
723
+
724
+            \OC_Util::setupFS($uid);
725
+
726
+            if (!Filesystem::file_exists($filename)) {
727
+                return false;
728
+            }
729
+
730
+            if (empty($filename)) {
731
+                // file maybe renamed or deleted
732
+                return false;
733
+            }
734
+            $versionsFileview = new View('/'.$uid.'/files_versions');
735
+
736
+            $softQuota = true;
737
+            $quota = $user->getQuota();
738
+            if ($quota === null || $quota === 'none') {
739
+                $quota = Filesystem::free_space('/');
740
+                $softQuota = false;
741
+            } else {
742
+                $quota = \OCP\Util::computerFileSize($quota);
743
+            }
744
+
745
+            // make sure that we have the current size of the version history
746
+            $versionsSize = self::getVersionsSize($uid);
747
+
748
+            // calculate available space for version history
749
+            // subtract size of files and current versions size from quota
750
+            if ($quota >= 0) {
751
+                if ($softQuota) {
752
+                    $userFolder = \OC::$server->getUserFolder($uid);
753
+                    if(is_null($userFolder)) {
754
+                        $availableSpace = 0;
755
+                    } else {
756
+                        $free = $quota - $userFolder->getSize(false); // remaining free space for user
757
+                        if ($free > 0) {
758
+                            $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
759
+                        } else {
760
+                            $availableSpace = $free - $versionsSize;
761
+                        }
762
+                    }
763
+                } else {
764
+                    $availableSpace = $quota;
765
+                }
766
+            } else {
767
+                $availableSpace = PHP_INT_MAX;
768
+            }
769
+
770
+            $allVersions = Storage::getVersions($uid, $filename);
771
+
772
+            $time = time();
773
+            list($toDelete, $sizeOfDeletedVersions) = self::getExpireList($time, $allVersions, $availableSpace <= 0);
774
+
775
+            $availableSpace = $availableSpace + $sizeOfDeletedVersions;
776
+            $versionsSize = $versionsSize - $sizeOfDeletedVersions;
777
+
778
+            // if still not enough free space we rearrange the versions from all files
779
+            if ($availableSpace <= 0) {
780
+                $result = Storage::getAllVersions($uid);
781
+                $allVersions = $result['all'];
782
+
783
+                foreach ($result['by_file'] as $versions) {
784
+                    list($toDeleteNew, $size) = self::getExpireList($time, $versions, $availableSpace <= 0);
785
+                    $toDelete = array_merge($toDelete, $toDeleteNew);
786
+                    $sizeOfDeletedVersions += $size;
787
+                }
788
+                $availableSpace = $availableSpace + $sizeOfDeletedVersions;
789
+                $versionsSize = $versionsSize - $sizeOfDeletedVersions;
790
+            }
791
+
792
+            $logger = \OC::$server->getLogger();
793
+            foreach($toDelete as $key => $path) {
794
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
795
+                self::deleteVersion($versionsFileview, $path);
796
+                \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
797
+                unset($allVersions[$key]); // update array with the versions we keep
798
+                $logger->info('Expire: ' . $path, ['app' => 'files_versions']);
799
+            }
800
+
801
+            // Check if enough space is available after versions are rearranged.
802
+            // If not we delete the oldest versions until we meet the size limit for versions,
803
+            // but always keep the two latest versions
804
+            $numOfVersions = count($allVersions) -2 ;
805
+            $i = 0;
806
+            // sort oldest first and make sure that we start at the first element
807
+            ksort($allVersions);
808
+            reset($allVersions);
809
+            while ($availableSpace < 0 && $i < $numOfVersions) {
810
+                $version = current($allVersions);
811
+                \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
812
+                self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
813
+                \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
814
+                \OC::$server->getLogger()->info('running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'], ['app' => 'files_versions']);
815
+                $versionsSize -= $version['size'];
816
+                $availableSpace += $version['size'];
817
+                next($allVersions);
818
+                $i++;
819
+            }
820
+
821
+            return $versionsSize; // finally return the new size of the version history
822
+        }
823
+
824
+        return false;
825
+    }
826
+
827
+    /**
828
+     * Create recursively missing directories inside of files_versions
829
+     * that match the given path to a file.
830
+     *
831
+     * @param string $filename $path to a file, relative to the user's
832
+     * "files" folder
833
+     * @param View $view view on data/user/
834
+     */
835
+    public static function createMissingDirectories($filename, $view) {
836
+        $dirname = Filesystem::normalizePath(dirname($filename));
837
+        $dirParts = explode('/', $dirname);
838
+        $dir = "/files_versions";
839
+        foreach ($dirParts as $part) {
840
+            $dir = $dir . '/' . $part;
841
+            if (!$view->file_exists($dir)) {
842
+                $view->mkdir($dir);
843
+            }
844
+        }
845
+    }
846
+
847
+    /**
848
+     * Static workaround
849
+     * @return Expiration
850
+     */
851
+    protected static function getExpiration() {
852
+        if (self::$application === null) {
853
+            self::$application = \OC::$server->query(Application::class);
854
+        }
855
+        return self::$application->getContainer()->query(Expiration::class);
856
+    }
857 857
 
858 858
 }
Please login to merge, or discard this patch.
Spacing   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -58,8 +58,8 @@  discard block
 block discarded – undo
58 58
 
59 59
 class Storage {
60 60
 
61
-	const DEFAULTENABLED=true;
62
-	const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
61
+	const DEFAULTENABLED = true;
62
+	const DEFAULTMAXSIZE = 50; // unit: percentage; 50% of available disk space/quota
63 63
 	const VERSIONS_ROOT = 'files_versions/';
64 64
 
65 65
 	const DELETE_TRIGGER_MASTER_REMOVED = 0;
@@ -73,17 +73,17 @@  discard block
 block discarded – undo
73 73
 
74 74
 	private static $max_versions_per_interval = [
75 75
 		//first 10sec, one version every 2sec
76
-		1 => ['intervalEndsAfter' => 10,      'step' => 2],
76
+		1 => ['intervalEndsAfter' => 10, 'step' => 2],
77 77
 		//next minute, one version every 10sec
78
-		2 => ['intervalEndsAfter' => 60,      'step' => 10],
78
+		2 => ['intervalEndsAfter' => 60, 'step' => 10],
79 79
 		//next hour, one version every minute
80
-		3 => ['intervalEndsAfter' => 3600,    'step' => 60],
80
+		3 => ['intervalEndsAfter' => 3600, 'step' => 60],
81 81
 		//next 24h, one version every hour
82
-		4 => ['intervalEndsAfter' => 86400,   'step' => 3600],
82
+		4 => ['intervalEndsAfter' => 86400, 'step' => 3600],
83 83
 		//next 30days, one version per day
84 84
 		5 => ['intervalEndsAfter' => 2592000, 'step' => 86400],
85 85
 		//until the end one version per week
86
-		6 => ['intervalEndsAfter' => -1,      'step' => 604800],
86
+		6 => ['intervalEndsAfter' => -1, 'step' => 604800],
87 87
 	];
88 88
 
89 89
 	/** @var \OCA\Files_Versions\AppInfo\Application */
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 	 * @return int versions size
158 158
 	 */
159 159
 	private static function getVersionsSize($user) {
160
-		$view = new View('/' . $user);
160
+		$view = new View('/'.$user);
161 161
 		$fileInfo = $view->getFileInfo('/files_versions');
162 162
 		return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
163 163
 	}
@@ -175,13 +175,13 @@  discard block
 block discarded – undo
175 175
 		}
176 176
 
177 177
 		// we only handle existing files
178
-		if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
178
+		if (!Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) {
179 179
 			return false;
180 180
 		}
181 181
 
182 182
 		list($uid, $filename) = self::getUidAndFilename($filename);
183 183
 
184
-		$files_view = new View('/'.$uid .'/files');
184
+		$files_view = new View('/'.$uid.'/files');
185 185
 
186 186
 		$eventDispatcher = \OC::$server->getEventDispatcher();
187 187
 		$fileInfo = $files_view->getFileInfo($filename);
@@ -248,14 +248,14 @@  discard block
 block discarded – undo
248 248
 
249 249
 		if (!Filesystem::file_exists($path)) {
250 250
 
251
-			$view = new View('/' . $uid . '/files_versions');
251
+			$view = new View('/'.$uid.'/files_versions');
252 252
 
253 253
 			$versions = self::getVersions($uid, $filename);
254 254
 			if (!empty($versions)) {
255 255
 				foreach ($versions as $v) {
256
-					\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
257
-					self::deleteVersion($view, $filename . '.v' . $v['version']);
258
-					\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
256
+					\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path.$v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
257
+					self::deleteVersion($view, $filename.'.v'.$v['version']);
258
+					\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path.$v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED]);
259 259
 				}
260 260
 			}
261 261
 		}
@@ -289,33 +289,33 @@  discard block
 block discarded – undo
289 289
 		$rootView = new View('');
290 290
 
291 291
 		// did we move a directory ?
292
-		if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
292
+		if ($rootView->is_dir('/'.$targetOwner.'/files/'.$targetPath)) {
293 293
 			// does the directory exists for versions too ?
294
-			if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) {
294
+			if ($rootView->is_dir('/'.$sourceOwner.'/files_versions/'.$sourcePath)) {
295 295
 				// create missing dirs if necessary
296
-				self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
296
+				self::createMissingDirectories($targetPath, new View('/'.$targetOwner));
297 297
 
298 298
 				// move the directory containing the versions
299 299
 				$rootView->$operation(
300
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath,
301
-					'/' . $targetOwner . '/files_versions/' . $targetPath
300
+					'/'.$sourceOwner.'/files_versions/'.$sourcePath,
301
+					'/'.$targetOwner.'/files_versions/'.$targetPath
302 302
 				);
303 303
 			}
304
-		} else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) {
304
+		} else if ($versions = Storage::getVersions($sourceOwner, '/'.$sourcePath)) {
305 305
 			// create missing dirs if necessary
306
-			self::createMissingDirectories($targetPath, new View('/'. $targetOwner));
306
+			self::createMissingDirectories($targetPath, new View('/'.$targetOwner));
307 307
 
308 308
 			foreach ($versions as $v) {
309 309
 				// move each version one by one to the target directory
310 310
 				$rootView->$operation(
311
-					'/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
312
-					'/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
311
+					'/'.$sourceOwner.'/files_versions/'.$sourcePath.'.v'.$v['version'],
312
+					'/'.$targetOwner.'/files_versions/'.$targetPath.'.v'.$v['version']
313 313
 				);
314 314
 			}
315 315
 		}
316 316
 
317 317
 		// if we moved versions directly for a file, schedule expiration check for that file
318
-		if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) {
318
+		if (!$rootView->is_dir('/'.$targetOwner.'/files/'.$targetPath)) {
319 319
 			self::scheduleExpire($targetOwner, $targetPath);
320 320
 		}
321 321
 
@@ -331,13 +331,13 @@  discard block
 block discarded – undo
331 331
 	public static function rollback(string $file, int $revision, IUser $user) {
332 332
 
333 333
 		// add expected leading slash
334
-		$filename = '/' . ltrim($file, '/');
334
+		$filename = '/'.ltrim($file, '/');
335 335
 
336 336
 		// Fetch the userfolder to trigger view hooks
337 337
 		$userFolder = \OC::$server->getUserFolder($user->getUID());
338 338
 
339 339
 		$users_view = new View('/'.$user->getUID());
340
-		$files_view = new View('/'. $user->getUID().'/files');
340
+		$files_view = new View('/'.$user->getUID().'/files');
341 341
 
342 342
 		$versionCreated = false;
343 343
 
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
 			$versionCreated = true;
356 356
 		}
357 357
 
358
-		$fileToRestore =  'files_versions' . $filename . '.v' . $revision;
358
+		$fileToRestore = 'files_versions'.$filename.'.v'.$revision;
359 359
 
360 360
 		// Restore encrypted version of the old file for the newly restored file
361 361
 		// This has to happen manually here since the file is manually copied below
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
 		);
372 372
 
373 373
 		// rollback
374
-		if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
374
+		if (self::copyFileContents($users_view, $fileToRestore, 'files'.$filename)) {
375 375
 			$files_view->touch($file, $revision);
376 376
 			Storage::scheduleExpire($user->getUID(), $file);
377 377
 
@@ -444,12 +444,12 @@  discard block
 block discarded – undo
444 444
 			return $versions;
445 445
 		}
446 446
 		// fetch for old versions
447
-		$view = new View('/' . $uid . '/');
447
+		$view = new View('/'.$uid.'/');
448 448
 
449 449
 		$pathinfo = pathinfo($filename);
450 450
 		$versionedFile = $pathinfo['basename'];
451 451
 
452
-		$dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']);
452
+		$dir = Filesystem::normalizePath(self::VERSIONS_ROOT.'/'.$pathinfo['dirname']);
453 453
 
454 454
 		$dirContent = false;
455 455
 		if ($view->is_dir($dir)) {
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
 						$pathparts = pathinfo($entryName);
470 470
 						$timestamp = substr($pathparts['extension'], 1);
471 471
 						$filename = $pathparts['filename'];
472
-						$key = $timestamp . '#' . $filename;
472
+						$key = $timestamp.'#'.$filename;
473 473
 						$versions[$key]['version'] = $timestamp;
474 474
 						$versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
475 475
 						if (empty($userFullPath)) {
@@ -477,9 +477,9 @@  discard block
 block discarded – undo
477 477
 						} else {
478 478
 							$versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]);
479 479
 						}
480
-						$versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename);
480
+						$versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'].'/'.$filename);
481 481
 						$versions[$key]['name'] = $versionedFile;
482
-						$versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
482
+						$versions[$key]['size'] = $view->filesize($dir.'/'.$entryName);
483 483
 						$versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile);
484 484
 					}
485 485
 				}
@@ -507,7 +507,7 @@  discard block
 block discarded – undo
507 507
 
508 508
 		$toDelete = [];
509 509
 		foreach (array_reverse($versions['all']) as $key => $version) {
510
-			if ((int)$version['version'] <$threshold) {
510
+			if ((int) $version['version'] < $threshold) {
511 511
 				$toDelete[$key] = $version;
512 512
 			} else {
513 513
 				//Versions are sorted by time - nothing mo to iterate.
@@ -515,11 +515,11 @@  discard block
 block discarded – undo
515 515
 			}
516 516
 		}
517 517
 
518
-		$view = new View('/' . $uid . '/files_versions');
518
+		$view = new View('/'.$uid.'/files_versions');
519 519
 		if (!empty($toDelete)) {
520 520
 			foreach ($toDelete as $version) {
521 521
 				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
522
-				self::deleteVersion($view, $version['path'] . '.v' . $version['version']);
522
+				self::deleteVersion($view, $version['path'].'.v'.$version['version']);
523 523
 				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]);
524 524
 			}
525 525
 		}
@@ -535,19 +535,19 @@  discard block
 block discarded – undo
535 535
 		$diff = time() - $timestamp;
536 536
 
537 537
 		if ($diff < 60) { // first minute
538
-			return  $diff . " seconds ago";
538
+			return  $diff." seconds ago";
539 539
 		} elseif ($diff < 3600) { //first hour
540
-			return round($diff / 60) . " minutes ago";
540
+			return round($diff / 60)." minutes ago";
541 541
 		} elseif ($diff < 86400) { // first day
542
-			return round($diff / 3600) . " hours ago";
542
+			return round($diff / 3600)." hours ago";
543 543
 		} elseif ($diff < 604800) { //first week
544
-			return round($diff / 86400) . " days ago";
544
+			return round($diff / 86400)." days ago";
545 545
 		} elseif ($diff < 2419200) { //first month
546
-			return round($diff / 604800) . " weeks ago";
546
+			return round($diff / 604800)." weeks ago";
547 547
 		} elseif ($diff < 29030400) { // first year
548
-			return round($diff / 2419200) . " months ago";
548
+			return round($diff / 2419200)." months ago";
549 549
 		} else {
550
-			return round($diff / 29030400) . " years ago";
550
+			return round($diff / 29030400)." years ago";
551 551
 		}
552 552
 
553 553
 	}
@@ -558,7 +558,7 @@  discard block
 block discarded – undo
558 558
 	 * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
559 559
 	 */
560 560
 	private static function getAllVersions($uid) {
561
-		$view = new View('/' . $uid . '/');
561
+		$view = new View('/'.$uid.'/');
562 562
 		$dirs = [self::VERSIONS_ROOT];
563 563
 		$versions = [];
564 564
 
@@ -568,7 +568,7 @@  discard block
 block discarded – undo
568 568
 
569 569
 			foreach ($files as $file) {
570 570
 				$fileData = $file->getData();
571
-				$filePath = $dir . '/' . $fileData['name'];
571
+				$filePath = $dir.'/'.$fileData['name'];
572 572
 				if ($file['type'] === 'dir') {
573 573
 					$dirs[] = $filePath;
574 574
 				} else {
@@ -576,7 +576,7 @@  discard block
 block discarded – undo
576 576
 					$relPathStart = strlen(self::VERSIONS_ROOT);
577 577
 					$version = substr($filePath, $versionsBegin + 2);
578 578
 					$relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart);
579
-					$key = $version . '#' . $relpath;
579
+					$key = $version.'#'.$relpath;
580 580
 					$versions[$key] = ['path' => $relpath, 'timestamp' => $version];
581 581
 				}
582 582
 			}
@@ -617,13 +617,13 @@  discard block
 block discarded – undo
617 617
 			list($toDelete, $size) = self::getAutoExpireList($time, $versions);
618 618
 		} else {
619 619
 			$size = 0;
620
-			$toDelete = [];  // versions we want to delete
620
+			$toDelete = []; // versions we want to delete
621 621
 		}
622 622
 
623 623
 		foreach ($versions as $key => $version) {
624 624
 			if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) {
625 625
 				$size += $version['size'];
626
-				$toDelete[$key] = $version['path'] . '.v' . $version['version'];
626
+				$toDelete[$key] = $version['path'].'.v'.$version['version'];
627 627
 			}
628 628
 		}
629 629
 
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
 	 */
639 639
 	protected static function getAutoExpireList($time, $versions) {
640 640
 		$size = 0;
641
-		$toDelete = [];  // versions we want to delete
641
+		$toDelete = []; // versions we want to delete
642 642
 
643 643
 		$interval = 1;
644 644
 		$step = Storage::$max_versions_per_interval[$interval]['step'];
@@ -660,9 +660,9 @@  discard block
 block discarded – undo
660 660
 				if ($nextInterval === -1 || $prevTimestamp > $nextInterval) {
661 661
 					if ($version['version'] > $nextVersion) {
662 662
 						//distance between two version too small, mark to delete
663
-						$toDelete[$key] = $version['path'] . '.v' . $version['version'];
663
+						$toDelete[$key] = $version['path'].'.v'.$version['version'];
664 664
 						$size += $version['size'];
665
-						\OC::$server->getLogger()->info('Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, ['app' => 'files_versions']);
665
+						\OC::$server->getLogger()->info('Mark to expire '.$version['path'].' next version should be '.$nextVersion." or smaller. (prevTimestamp: ".$prevTimestamp."; step: ".$step, ['app' => 'files_versions']);
666 666
 					} else {
667 667
 						$nextVersion = $version['version'] - $step;
668 668
 						$prevTimestamp = $version['version'];
@@ -717,8 +717,8 @@  discard block
 block discarded – undo
717 717
 			// get available disk space for user
718 718
 			$user = \OC::$server->getUserManager()->get($uid);
719 719
 			if (is_null($user)) {
720
-				\OC::$server->getLogger()->error('Backends provided no user object for ' . $uid, ['app' => 'files_versions']);
721
-				throw new \OC\User\NoUserException('Backends provided no user object for ' . $uid);
720
+				\OC::$server->getLogger()->error('Backends provided no user object for '.$uid, ['app' => 'files_versions']);
721
+				throw new \OC\User\NoUserException('Backends provided no user object for '.$uid);
722 722
 			}
723 723
 
724 724
 			\OC_Util::setupFS($uid);
@@ -750,7 +750,7 @@  discard block
 block discarded – undo
750 750
 			if ($quota >= 0) {
751 751
 				if ($softQuota) {
752 752
 					$userFolder = \OC::$server->getUserFolder($uid);
753
-					if(is_null($userFolder)) {
753
+					if (is_null($userFolder)) {
754 754
 						$availableSpace = 0;
755 755
 					} else {
756 756
 						$free = $quota - $userFolder->getSize(false); // remaining free space for user
@@ -790,18 +790,18 @@  discard block
 block discarded – undo
790 790
 			}
791 791
 
792 792
 			$logger = \OC::$server->getLogger();
793
-			foreach($toDelete as $key => $path) {
793
+			foreach ($toDelete as $key => $path) {
794 794
 				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
795 795
 				self::deleteVersion($versionsFileview, $path);
796 796
 				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
797 797
 				unset($allVersions[$key]); // update array with the versions we keep
798
-				$logger->info('Expire: ' . $path, ['app' => 'files_versions']);
798
+				$logger->info('Expire: '.$path, ['app' => 'files_versions']);
799 799
 			}
800 800
 
801 801
 			// Check if enough space is available after versions are rearranged.
802 802
 			// If not we delete the oldest versions until we meet the size limit for versions,
803 803
 			// but always keep the two latest versions
804
-			$numOfVersions = count($allVersions) -2 ;
804
+			$numOfVersions = count($allVersions) - 2;
805 805
 			$i = 0;
806 806
 			// sort oldest first and make sure that we start at the first element
807 807
 			ksort($allVersions);
@@ -809,9 +809,9 @@  discard block
 block discarded – undo
809 809
 			while ($availableSpace < 0 && $i < $numOfVersions) {
810 810
 				$version = current($allVersions);
811 811
 				\OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
812
-				self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
812
+				self::deleteVersion($versionsFileview, $version['path'].'.v'.$version['version']);
813 813
 				\OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED]);
814
-				\OC::$server->getLogger()->info('running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'], ['app' => 'files_versions']);
814
+				\OC::$server->getLogger()->info('running out of space! Delete oldest version: '.$version['path'].'.v'.$version['version'], ['app' => 'files_versions']);
815 815
 				$versionsSize -= $version['size'];
816 816
 				$availableSpace += $version['size'];
817 817
 				next($allVersions);
@@ -837,7 +837,7 @@  discard block
 block discarded – undo
837 837
 		$dirParts = explode('/', $dirname);
838 838
 		$dir = "/files_versions";
839 839
 		foreach ($dirParts as $part) {
840
-			$dir = $dir . '/' . $part;
840
+			$dir = $dir.'/'.$part;
841 841
 			if (!$view->file_exists($dir)) {
842 842
 				$view->mkdir($dir);
843 843
 			}
Please login to merge, or discard this patch.
apps/files_versions/lib/Expiration.php 1 patch
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -29,173 +29,173 @@
 block discarded – undo
29 29
 
30 30
 class Expiration {
31 31
 
32
-	// how long do we keep files a version if no other value is defined in the config file (unit: days)
33
-	const NO_OBLIGATION = -1;
34
-
35
-	/** @var ITimeFactory */
36
-	private $timeFactory;
37
-
38
-	/** @var string */
39
-	private $retentionObligation;
40
-
41
-	/** @var int */
42
-	private $minAge;
43
-
44
-	/** @var int */
45
-	private $maxAge;
46
-
47
-	/** @var bool */
48
-	private $canPurgeToSaveSpace;
49
-
50
-	public function __construct(IConfig $config,ITimeFactory $timeFactory) {
51
-		$this->timeFactory = $timeFactory;
52
-		$this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto');
53
-
54
-		if ($this->retentionObligation !== 'disabled') {
55
-			$this->parseRetentionObligation();
56
-		}
57
-	}
58
-
59
-	/**
60
-	 * Is versions expiration enabled
61
-	 * @return bool
62
-	 */
63
-	public function isEnabled() {
64
-		return $this->retentionObligation !== 'disabled';
65
-	}
66
-
67
-	/**
68
-	 * Is default expiration active
69
-	 */
70
-	public function shouldAutoExpire() {
71
-		return $this->minAge === self::NO_OBLIGATION
72
-				|| $this->maxAge === self::NO_OBLIGATION;
73
-	}
74
-
75
-	/**
76
-	 * Check if given timestamp in expiration range
77
-	 * @param int $timestamp
78
-	 * @param bool $quotaExceeded
79
-	 * @return bool
80
-	 */
81
-	public function isExpired($timestamp, $quotaExceeded = false) {
82
-		// No expiration if disabled
83
-		if (!$this->isEnabled()) {
84
-			return false;
85
-		}
86
-
87
-		// Purge to save space (if allowed)
88
-		if ($quotaExceeded && $this->canPurgeToSaveSpace) {
89
-			return true;
90
-		}
91
-
92
-		$time = $this->timeFactory->getTime();
93
-		// Never expire dates in future e.g. misconfiguration or negative time
94
-		// adjustment
95
-		if ($time<$timestamp) {
96
-			return false;
97
-		}
98
-
99
-		// Purge as too old
100
-		if ($this->maxAge !== self::NO_OBLIGATION) {
101
-			$maxTimestamp = $time - ($this->maxAge * 86400);
102
-			$isOlderThanMax = $timestamp < $maxTimestamp;
103
-		} else {
104
-			$isOlderThanMax = false;
105
-		}
106
-
107
-		if ($this->minAge !== self::NO_OBLIGATION) {
108
-			// older than Min obligation and we are running out of quota?
109
-			$minTimestamp = $time - ($this->minAge * 86400);
110
-			$isMinReached = ($timestamp < $minTimestamp) && $quotaExceeded;
111
-		} else {
112
-			$isMinReached = false;
113
-		}
114
-
115
-		return $isOlderThanMax || $isMinReached;
116
-	}
117
-
118
-	/**
119
-	 * Get maximal retention obligation as a timestamp
120
-	 * @return int
121
-	 */
122
-	public function getMaxAgeAsTimestamp() {
123
-		$maxAge = false;
124
-		if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) {
125
-			$time = $this->timeFactory->getTime();
126
-			$maxAge = $time - ($this->maxAge * 86400);
127
-		}
128
-		return $maxAge;
129
-	}
130
-
131
-	/**
132
-	 * Read versions_retention_obligation, validate it
133
-	 * and set private members accordingly
134
-	 */
135
-	private function parseRetentionObligation() {
136
-		$splitValues = explode(',', $this->retentionObligation);
137
-		if (!isset($splitValues[0])) {
138
-			$minValue = 'auto';
139
-		} else {
140
-			$minValue = trim($splitValues[0]);
141
-		}
142
-
143
-		if (!isset($splitValues[1])) {
144
-			$maxValue = 'auto';
145
-		} else {
146
-			$maxValue = trim($splitValues[1]);
147
-		}
148
-
149
-		$isValid = true;
150
-		// Validate
151
-		if (!ctype_digit($minValue) && $minValue !== 'auto') {
152
-			$isValid = false;
153
-			\OC::$server->getLogger()->warning(
154
-					$minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
155
-					['app'=>'files_versions']
156
-			);
157
-		}
158
-
159
-		if (!ctype_digit($maxValue) && $maxValue !== 'auto') {
160
-			$isValid = false;
161
-			\OC::$server->getLogger()->warning(
162
-					$maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
163
-					['app'=>'files_versions']
164
-			);
165
-		}
166
-
167
-		if (!$isValid){
168
-			$minValue = 'auto';
169
-			$maxValue = 'auto';
170
-		}
171
-
172
-
173
-		if ($minValue === 'auto' && $maxValue === 'auto') {
174
-			// Default: Delete anytime if space needed
175
-			$this->minAge = self::NO_OBLIGATION;
176
-			$this->maxAge = self::NO_OBLIGATION;
177
-			$this->canPurgeToSaveSpace = true;
178
-		} elseif ($minValue !== 'auto' && $maxValue === 'auto') {
179
-			// Keep for X days but delete anytime if space needed
180
-			$this->minAge = (int)$minValue;
181
-			$this->maxAge = self::NO_OBLIGATION;
182
-			$this->canPurgeToSaveSpace = true;
183
-		} elseif ($minValue === 'auto' && $maxValue !== 'auto') {
184
-			// Delete anytime if space needed, Delete all older than max automatically
185
-			$this->minAge = self::NO_OBLIGATION;
186
-			$this->maxAge = (int)$maxValue;
187
-			$this->canPurgeToSaveSpace = true;
188
-		} elseif ($minValue !== 'auto' && $maxValue !== 'auto') {
189
-			// Delete all older than max OR older than min if space needed
190
-
191
-			// Max < Min as per https://github.com/owncloud/core/issues/16301
192
-			if ($maxValue < $minValue) {
193
-				$maxValue = $minValue;
194
-			}
195
-
196
-			$this->minAge = (int)$minValue;
197
-			$this->maxAge = (int)$maxValue;
198
-			$this->canPurgeToSaveSpace = false;
199
-		}
200
-	}
32
+    // how long do we keep files a version if no other value is defined in the config file (unit: days)
33
+    const NO_OBLIGATION = -1;
34
+
35
+    /** @var ITimeFactory */
36
+    private $timeFactory;
37
+
38
+    /** @var string */
39
+    private $retentionObligation;
40
+
41
+    /** @var int */
42
+    private $minAge;
43
+
44
+    /** @var int */
45
+    private $maxAge;
46
+
47
+    /** @var bool */
48
+    private $canPurgeToSaveSpace;
49
+
50
+    public function __construct(IConfig $config,ITimeFactory $timeFactory) {
51
+        $this->timeFactory = $timeFactory;
52
+        $this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto');
53
+
54
+        if ($this->retentionObligation !== 'disabled') {
55
+            $this->parseRetentionObligation();
56
+        }
57
+    }
58
+
59
+    /**
60
+     * Is versions expiration enabled
61
+     * @return bool
62
+     */
63
+    public function isEnabled() {
64
+        return $this->retentionObligation !== 'disabled';
65
+    }
66
+
67
+    /**
68
+     * Is default expiration active
69
+     */
70
+    public function shouldAutoExpire() {
71
+        return $this->minAge === self::NO_OBLIGATION
72
+                || $this->maxAge === self::NO_OBLIGATION;
73
+    }
74
+
75
+    /**
76
+     * Check if given timestamp in expiration range
77
+     * @param int $timestamp
78
+     * @param bool $quotaExceeded
79
+     * @return bool
80
+     */
81
+    public function isExpired($timestamp, $quotaExceeded = false) {
82
+        // No expiration if disabled
83
+        if (!$this->isEnabled()) {
84
+            return false;
85
+        }
86
+
87
+        // Purge to save space (if allowed)
88
+        if ($quotaExceeded && $this->canPurgeToSaveSpace) {
89
+            return true;
90
+        }
91
+
92
+        $time = $this->timeFactory->getTime();
93
+        // Never expire dates in future e.g. misconfiguration or negative time
94
+        // adjustment
95
+        if ($time<$timestamp) {
96
+            return false;
97
+        }
98
+
99
+        // Purge as too old
100
+        if ($this->maxAge !== self::NO_OBLIGATION) {
101
+            $maxTimestamp = $time - ($this->maxAge * 86400);
102
+            $isOlderThanMax = $timestamp < $maxTimestamp;
103
+        } else {
104
+            $isOlderThanMax = false;
105
+        }
106
+
107
+        if ($this->minAge !== self::NO_OBLIGATION) {
108
+            // older than Min obligation and we are running out of quota?
109
+            $minTimestamp = $time - ($this->minAge * 86400);
110
+            $isMinReached = ($timestamp < $minTimestamp) && $quotaExceeded;
111
+        } else {
112
+            $isMinReached = false;
113
+        }
114
+
115
+        return $isOlderThanMax || $isMinReached;
116
+    }
117
+
118
+    /**
119
+     * Get maximal retention obligation as a timestamp
120
+     * @return int
121
+     */
122
+    public function getMaxAgeAsTimestamp() {
123
+        $maxAge = false;
124
+        if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) {
125
+            $time = $this->timeFactory->getTime();
126
+            $maxAge = $time - ($this->maxAge * 86400);
127
+        }
128
+        return $maxAge;
129
+    }
130
+
131
+    /**
132
+     * Read versions_retention_obligation, validate it
133
+     * and set private members accordingly
134
+     */
135
+    private function parseRetentionObligation() {
136
+        $splitValues = explode(',', $this->retentionObligation);
137
+        if (!isset($splitValues[0])) {
138
+            $minValue = 'auto';
139
+        } else {
140
+            $minValue = trim($splitValues[0]);
141
+        }
142
+
143
+        if (!isset($splitValues[1])) {
144
+            $maxValue = 'auto';
145
+        } else {
146
+            $maxValue = trim($splitValues[1]);
147
+        }
148
+
149
+        $isValid = true;
150
+        // Validate
151
+        if (!ctype_digit($minValue) && $minValue !== 'auto') {
152
+            $isValid = false;
153
+            \OC::$server->getLogger()->warning(
154
+                    $minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
155
+                    ['app'=>'files_versions']
156
+            );
157
+        }
158
+
159
+        if (!ctype_digit($maxValue) && $maxValue !== 'auto') {
160
+            $isValid = false;
161
+            \OC::$server->getLogger()->warning(
162
+                    $maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
163
+                    ['app'=>'files_versions']
164
+            );
165
+        }
166
+
167
+        if (!$isValid){
168
+            $minValue = 'auto';
169
+            $maxValue = 'auto';
170
+        }
171
+
172
+
173
+        if ($minValue === 'auto' && $maxValue === 'auto') {
174
+            // Default: Delete anytime if space needed
175
+            $this->minAge = self::NO_OBLIGATION;
176
+            $this->maxAge = self::NO_OBLIGATION;
177
+            $this->canPurgeToSaveSpace = true;
178
+        } elseif ($minValue !== 'auto' && $maxValue === 'auto') {
179
+            // Keep for X days but delete anytime if space needed
180
+            $this->minAge = (int)$minValue;
181
+            $this->maxAge = self::NO_OBLIGATION;
182
+            $this->canPurgeToSaveSpace = true;
183
+        } elseif ($minValue === 'auto' && $maxValue !== 'auto') {
184
+            // Delete anytime if space needed, Delete all older than max automatically
185
+            $this->minAge = self::NO_OBLIGATION;
186
+            $this->maxAge = (int)$maxValue;
187
+            $this->canPurgeToSaveSpace = true;
188
+        } elseif ($minValue !== 'auto' && $maxValue !== 'auto') {
189
+            // Delete all older than max OR older than min if space needed
190
+
191
+            // Max < Min as per https://github.com/owncloud/core/issues/16301
192
+            if ($maxValue < $minValue) {
193
+                $maxValue = $minValue;
194
+            }
195
+
196
+            $this->minAge = (int)$minValue;
197
+            $this->maxAge = (int)$maxValue;
198
+            $this->canPurgeToSaveSpace = false;
199
+        }
200
+    }
201 201
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Listener/LoadSidebarListener.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -34,14 +34,14 @@
 block discarded – undo
34 34
 use OCP\Util;
35 35
 
36 36
 class LoadSidebarListener implements IEventListener {
37
-	public function handle(Event $event): void {
38
-		if (!($event instanceof LoadSidebar)) {
39
-			return;
40
-		}
37
+    public function handle(Event $event): void {
38
+        if (!($event instanceof LoadSidebar)) {
39
+            return;
40
+        }
41 41
 
42
-		// TODO: make sure to only include the sidebar script when
43
-		// we properly split it between files list and sidebar
44
-		Util::addScript(Application::APP_ID, 'files_versions');
45
-	}
42
+        // TODO: make sure to only include the sidebar script when
43
+        // we properly split it between files list and sidebar
44
+        Util::addScript(Application::APP_ID, 'files_versions');
45
+    }
46 46
 
47 47
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Listener/LoadAdditionalListener.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -34,14 +34,14 @@
 block discarded – undo
34 34
 use OCP\Util;
35 35
 
36 36
 class LoadAdditionalListener implements IEventListener {
37
-	public function handle(Event $event): void {
38
-		if (!($event instanceof LoadAdditionalScriptsEvent)) {
39
-			return;
40
-		}
37
+    public function handle(Event $event): void {
38
+        if (!($event instanceof LoadAdditionalScriptsEvent)) {
39
+            return;
40
+        }
41 41
 
42
-		// TODO: make sure to only include the sidebar script when
43
-		// we properly split it between files list and sidebar
44
-		Util::addScript(Application::APP_ID, 'files_versions');
45
-	}
42
+        // TODO: make sure to only include the sidebar script when
43
+        // we properly split it between files list and sidebar
44
+        Util::addScript(Application::APP_ID, 'files_versions');
45
+    }
46 46
 
47 47
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 2 patches
Indentation   +662 added lines, -662 removed lines patch added patch discarded remove patch
@@ -83,670 +83,670 @@
 block discarded – undo
83 83
  */
84 84
 class ShareController extends AuthPublicShareController {
85 85
 
86
-	/** @var IConfig */
87
-	protected $config;
88
-	/** @var IUserManager */
89
-	protected $userManager;
90
-	/** @var ILogger */
91
-	protected $logger;
92
-	/** @var \OCP\Activity\IManager */
93
-	protected $activityManager;
94
-	/** @var IPreview */
95
-	protected $previewManager;
96
-	/** @var IRootFolder */
97
-	protected $rootFolder;
98
-	/** @var FederatedShareProvider */
99
-	protected $federatedShareProvider;
100
-	/** @var IAccountManager */
101
-	protected $accountManager;
102
-	/** @var EventDispatcherInterface */
103
-	protected $eventDispatcher;
104
-	/** @var IL10N */
105
-	protected $l10n;
106
-	/** @var Defaults */
107
-	protected $defaults;
108
-	/** @var ShareManager */
109
-	protected $shareManager;
110
-
111
-	/** @var Share\IShare */
112
-	protected $share;
113
-
114
-	/**
115
-	 * @param string $appName
116
-	 * @param IRequest $request
117
-	 * @param IConfig $config
118
-	 * @param IURLGenerator $urlGenerator
119
-	 * @param IUserManager $userManager
120
-	 * @param ILogger $logger
121
-	 * @param \OCP\Activity\IManager $activityManager
122
-	 * @param \OCP\Share\IManager $shareManager
123
-	 * @param ISession $session
124
-	 * @param IPreview $previewManager
125
-	 * @param IRootFolder $rootFolder
126
-	 * @param FederatedShareProvider $federatedShareProvider
127
-	 * @param IAccountManager $accountManager
128
-	 * @param EventDispatcherInterface $eventDispatcher
129
-	 * @param IL10N $l10n
130
-	 * @param Defaults $defaults
131
-	 */
132
-	public function __construct(string $appName,
133
-								IRequest $request,
134
-								IConfig $config,
135
-								IURLGenerator $urlGenerator,
136
-								IUserManager $userManager,
137
-								ILogger $logger,
138
-								\OCP\Activity\IManager $activityManager,
139
-								ShareManager $shareManager,
140
-								ISession $session,
141
-								IPreview $previewManager,
142
-								IRootFolder $rootFolder,
143
-								FederatedShareProvider $federatedShareProvider,
144
-								IAccountManager $accountManager,
145
-								EventDispatcherInterface $eventDispatcher,
146
-								IL10N $l10n,
147
-								Defaults $defaults) {
148
-		parent::__construct($appName, $request, $session, $urlGenerator);
149
-
150
-		$this->config = $config;
151
-		$this->userManager = $userManager;
152
-		$this->logger = $logger;
153
-		$this->activityManager = $activityManager;
154
-		$this->previewManager = $previewManager;
155
-		$this->rootFolder = $rootFolder;
156
-		$this->federatedShareProvider = $federatedShareProvider;
157
-		$this->accountManager = $accountManager;
158
-		$this->eventDispatcher = $eventDispatcher;
159
-		$this->l10n = $l10n;
160
-		$this->defaults = $defaults;
161
-		$this->shareManager = $shareManager;
162
-	}
163
-
164
-	/**
165
-	 * @PublicPage
166
-	 * @NoCSRFRequired
167
-	 *
168
-	 * Show the authentication page
169
-	 * The form has to submit to the authenticate method route
170
-	 */
171
-	public function showAuthenticate(): TemplateResponse {
172
-		$templateParameters = ['share' => $this->share];
173
-
174
-		$event = new GenericEvent(null, $templateParameters);
175
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
176
-
177
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
178
-		if ($this->share->getSendPasswordByTalk()) {
179
-			$csp = new ContentSecurityPolicy();
180
-			$csp->addAllowedConnectDomain('*');
181
-			$csp->addAllowedMediaDomain('blob:');
182
-			$response->setContentSecurityPolicy($csp);
183
-		}
184
-
185
-		return $response;
186
-	}
187
-
188
-	/**
189
-	 * The template to show when authentication failed
190
-	 */
191
-	protected function showAuthFailed(): TemplateResponse {
192
-		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
193
-
194
-		$event = new GenericEvent(null, $templateParameters);
195
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
196
-
197
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
-		if ($this->share->getSendPasswordByTalk()) {
199
-			$csp = new ContentSecurityPolicy();
200
-			$csp->addAllowedConnectDomain('*');
201
-			$csp->addAllowedMediaDomain('blob:');
202
-			$response->setContentSecurityPolicy($csp);
203
-		}
204
-
205
-		return $response;
206
-	}
207
-
208
-	protected function verifyPassword(string $password): bool {
209
-		return $this->shareManager->checkPassword($this->share, $password);
210
-	}
211
-
212
-	protected function getPasswordHash(): string {
213
-		return $this->share->getPassword();
214
-	}
215
-
216
-	public function isValidToken(): bool {
217
-		try {
218
-			$this->share = $this->shareManager->getShareByToken($this->getToken());
219
-		} catch (ShareNotFound $e) {
220
-			return false;
221
-		}
222
-
223
-		return true;
224
-	}
225
-
226
-	protected function isPasswordProtected(): bool {
227
-		return $this->share->getPassword() !== null;
228
-	}
229
-
230
-	protected function authSucceeded() {
231
-		// For share this was always set so it is still used in other apps
232
-		$this->session->set('public_link_authenticated', (string)$this->share->getId());
233
-	}
234
-
235
-	protected function authFailed() {
236
-		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
-	}
238
-
239
-	/**
240
-	 * throws hooks when a share is attempted to be accessed
241
-	 *
242
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
243
-	 * otherwise token
244
-	 * @param int $errorCode
245
-	 * @param string $errorMessage
246
-	 * @throws \OC\HintException
247
-	 * @throws \OC\ServerNotAvailableException
248
-	 */
249
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
-		$itemType = $itemSource = $uidOwner = '';
251
-		$token = $share;
252
-		$exception = null;
253
-		if($share instanceof \OCP\Share\IShare) {
254
-			try {
255
-				$token = $share->getToken();
256
-				$uidOwner = $share->getSharedBy();
257
-				$itemType = $share->getNodeType();
258
-				$itemSource = $share->getNodeId();
259
-			} catch (\Exception $e) {
260
-				// we log what we know and pass on the exception afterwards
261
-				$exception = $e;
262
-			}
263
-		}
264
-		\OC_Hook::emit(Share::class, 'share_link_access', [
265
-			'itemType' => $itemType,
266
-			'itemSource' => $itemSource,
267
-			'uidOwner' => $uidOwner,
268
-			'token' => $token,
269
-			'errorCode' => $errorCode,
270
-			'errorMessage' => $errorMessage,
271
-		]);
272
-		if(!is_null($exception)) {
273
-			throw $exception;
274
-		}
275
-	}
276
-
277
-	/**
278
-	 * Validate the permissions of the share
279
-	 *
280
-	 * @param Share\IShare $share
281
-	 * @return bool
282
-	 */
283
-	private function validateShare(\OCP\Share\IShare $share) {
284
-		// If the owner is disabled no access to the linke is granted
285
-		$owner = $this->userManager->get($share->getShareOwner());
286
-		if ($owner === null || !$owner->isEnabled()) {
287
-			return false;
288
-		}
289
-
290
-		// If the initiator of the share is disabled no access is granted
291
-		$initiator = $this->userManager->get($share->getSharedBy());
292
-		if ($initiator === null || !$initiator->isEnabled()) {
293
-			return false;
294
-		}
295
-
296
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
-	}
298
-
299
-	/**
300
-	 * @PublicPage
301
-	 * @NoCSRFRequired
302
-	 *
303
-	 *
304
-	 * @param string $path
305
-	 * @return TemplateResponse
306
-	 * @throws NotFoundException
307
-	 * @throws \Exception
308
-	 */
309
-	public function showShare($path = ''): TemplateResponse {
310
-		\OC_User::setIncognitoMode(true);
311
-
312
-		// Check whether share exists
313
-		try {
314
-			$share = $this->shareManager->getShareByToken($this->getToken());
315
-		} catch (ShareNotFound $e) {
316
-			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
-			throw new NotFoundException();
318
-		}
319
-
320
-		if (!$this->validateShare($share)) {
321
-			throw new NotFoundException();
322
-		}
323
-
324
-		$shareNode = $share->getNode();
325
-
326
-		// We can't get the path of a file share
327
-		try {
328
-			if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
-				$this->emitAccessShareHook($share, 404, 'Share not found');
330
-				throw new NotFoundException();
331
-			}
332
-		} catch (\Exception $e) {
333
-			$this->emitAccessShareHook($share, 404, 'Share not found');
334
-			throw $e;
335
-		}
336
-
337
-		$shareTmpl = [];
338
-		$shareTmpl['owner'] = '';
339
-		$shareTmpl['shareOwner'] = '';
340
-
341
-		$owner = $this->userManager->get($share->getShareOwner());
342
-		if ($owner instanceof IUser) {
343
-			$ownerAccount = $this->accountManager->getAccount($owner);
344
-
345
-			$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
-			if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
-				$shareTmpl['owner'] = $owner->getUID();
348
-				$shareTmpl['shareOwner'] = $owner->getDisplayName();
349
-			}
350
-		}
351
-
352
-		$shareTmpl['filename'] = $shareNode->getName();
353
-		$shareTmpl['directory_path'] = $share->getTarget();
354
-		$shareTmpl['note'] = $share->getNote();
355
-		$shareTmpl['mimetype'] = $shareNode->getMimetype();
356
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
-		$shareTmpl['dirToken'] = $this->getToken();
358
-		$shareTmpl['sharingToken'] = $this->getToken();
359
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
-		$shareTmpl['dir'] = '';
362
-		$shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
-		$shareTmpl['hideDownload'] = $share->getHideDownload();
365
-
366
-		$hideFileList = false;
367
-
368
-		if ($shareNode instanceof \OCP\Files\Folder) {
369
-
370
-			$shareIsFolder = true;
371
-
372
-			try {
373
-				$folderNode = $shareNode->get($path);
374
-			} catch (\OCP\Files\NotFoundException $e) {
375
-				$this->emitAccessShareHook($share, 404, 'Share not found');
376
-				throw new NotFoundException();
377
-			}
378
-
379
-			$shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
380
-
381
-			/*
86
+    /** @var IConfig */
87
+    protected $config;
88
+    /** @var IUserManager */
89
+    protected $userManager;
90
+    /** @var ILogger */
91
+    protected $logger;
92
+    /** @var \OCP\Activity\IManager */
93
+    protected $activityManager;
94
+    /** @var IPreview */
95
+    protected $previewManager;
96
+    /** @var IRootFolder */
97
+    protected $rootFolder;
98
+    /** @var FederatedShareProvider */
99
+    protected $federatedShareProvider;
100
+    /** @var IAccountManager */
101
+    protected $accountManager;
102
+    /** @var EventDispatcherInterface */
103
+    protected $eventDispatcher;
104
+    /** @var IL10N */
105
+    protected $l10n;
106
+    /** @var Defaults */
107
+    protected $defaults;
108
+    /** @var ShareManager */
109
+    protected $shareManager;
110
+
111
+    /** @var Share\IShare */
112
+    protected $share;
113
+
114
+    /**
115
+     * @param string $appName
116
+     * @param IRequest $request
117
+     * @param IConfig $config
118
+     * @param IURLGenerator $urlGenerator
119
+     * @param IUserManager $userManager
120
+     * @param ILogger $logger
121
+     * @param \OCP\Activity\IManager $activityManager
122
+     * @param \OCP\Share\IManager $shareManager
123
+     * @param ISession $session
124
+     * @param IPreview $previewManager
125
+     * @param IRootFolder $rootFolder
126
+     * @param FederatedShareProvider $federatedShareProvider
127
+     * @param IAccountManager $accountManager
128
+     * @param EventDispatcherInterface $eventDispatcher
129
+     * @param IL10N $l10n
130
+     * @param Defaults $defaults
131
+     */
132
+    public function __construct(string $appName,
133
+                                IRequest $request,
134
+                                IConfig $config,
135
+                                IURLGenerator $urlGenerator,
136
+                                IUserManager $userManager,
137
+                                ILogger $logger,
138
+                                \OCP\Activity\IManager $activityManager,
139
+                                ShareManager $shareManager,
140
+                                ISession $session,
141
+                                IPreview $previewManager,
142
+                                IRootFolder $rootFolder,
143
+                                FederatedShareProvider $federatedShareProvider,
144
+                                IAccountManager $accountManager,
145
+                                EventDispatcherInterface $eventDispatcher,
146
+                                IL10N $l10n,
147
+                                Defaults $defaults) {
148
+        parent::__construct($appName, $request, $session, $urlGenerator);
149
+
150
+        $this->config = $config;
151
+        $this->userManager = $userManager;
152
+        $this->logger = $logger;
153
+        $this->activityManager = $activityManager;
154
+        $this->previewManager = $previewManager;
155
+        $this->rootFolder = $rootFolder;
156
+        $this->federatedShareProvider = $federatedShareProvider;
157
+        $this->accountManager = $accountManager;
158
+        $this->eventDispatcher = $eventDispatcher;
159
+        $this->l10n = $l10n;
160
+        $this->defaults = $defaults;
161
+        $this->shareManager = $shareManager;
162
+    }
163
+
164
+    /**
165
+     * @PublicPage
166
+     * @NoCSRFRequired
167
+     *
168
+     * Show the authentication page
169
+     * The form has to submit to the authenticate method route
170
+     */
171
+    public function showAuthenticate(): TemplateResponse {
172
+        $templateParameters = ['share' => $this->share];
173
+
174
+        $event = new GenericEvent(null, $templateParameters);
175
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
176
+
177
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
178
+        if ($this->share->getSendPasswordByTalk()) {
179
+            $csp = new ContentSecurityPolicy();
180
+            $csp->addAllowedConnectDomain('*');
181
+            $csp->addAllowedMediaDomain('blob:');
182
+            $response->setContentSecurityPolicy($csp);
183
+        }
184
+
185
+        return $response;
186
+    }
187
+
188
+    /**
189
+     * The template to show when authentication failed
190
+     */
191
+    protected function showAuthFailed(): TemplateResponse {
192
+        $templateParameters = ['share' => $this->share, 'wrongpw' => true];
193
+
194
+        $event = new GenericEvent(null, $templateParameters);
195
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
196
+
197
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
+        if ($this->share->getSendPasswordByTalk()) {
199
+            $csp = new ContentSecurityPolicy();
200
+            $csp->addAllowedConnectDomain('*');
201
+            $csp->addAllowedMediaDomain('blob:');
202
+            $response->setContentSecurityPolicy($csp);
203
+        }
204
+
205
+        return $response;
206
+    }
207
+
208
+    protected function verifyPassword(string $password): bool {
209
+        return $this->shareManager->checkPassword($this->share, $password);
210
+    }
211
+
212
+    protected function getPasswordHash(): string {
213
+        return $this->share->getPassword();
214
+    }
215
+
216
+    public function isValidToken(): bool {
217
+        try {
218
+            $this->share = $this->shareManager->getShareByToken($this->getToken());
219
+        } catch (ShareNotFound $e) {
220
+            return false;
221
+        }
222
+
223
+        return true;
224
+    }
225
+
226
+    protected function isPasswordProtected(): bool {
227
+        return $this->share->getPassword() !== null;
228
+    }
229
+
230
+    protected function authSucceeded() {
231
+        // For share this was always set so it is still used in other apps
232
+        $this->session->set('public_link_authenticated', (string)$this->share->getId());
233
+    }
234
+
235
+    protected function authFailed() {
236
+        $this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
+    }
238
+
239
+    /**
240
+     * throws hooks when a share is attempted to be accessed
241
+     *
242
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
243
+     * otherwise token
244
+     * @param int $errorCode
245
+     * @param string $errorMessage
246
+     * @throws \OC\HintException
247
+     * @throws \OC\ServerNotAvailableException
248
+     */
249
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
+        $itemType = $itemSource = $uidOwner = '';
251
+        $token = $share;
252
+        $exception = null;
253
+        if($share instanceof \OCP\Share\IShare) {
254
+            try {
255
+                $token = $share->getToken();
256
+                $uidOwner = $share->getSharedBy();
257
+                $itemType = $share->getNodeType();
258
+                $itemSource = $share->getNodeId();
259
+            } catch (\Exception $e) {
260
+                // we log what we know and pass on the exception afterwards
261
+                $exception = $e;
262
+            }
263
+        }
264
+        \OC_Hook::emit(Share::class, 'share_link_access', [
265
+            'itemType' => $itemType,
266
+            'itemSource' => $itemSource,
267
+            'uidOwner' => $uidOwner,
268
+            'token' => $token,
269
+            'errorCode' => $errorCode,
270
+            'errorMessage' => $errorMessage,
271
+        ]);
272
+        if(!is_null($exception)) {
273
+            throw $exception;
274
+        }
275
+    }
276
+
277
+    /**
278
+     * Validate the permissions of the share
279
+     *
280
+     * @param Share\IShare $share
281
+     * @return bool
282
+     */
283
+    private function validateShare(\OCP\Share\IShare $share) {
284
+        // If the owner is disabled no access to the linke is granted
285
+        $owner = $this->userManager->get($share->getShareOwner());
286
+        if ($owner === null || !$owner->isEnabled()) {
287
+            return false;
288
+        }
289
+
290
+        // If the initiator of the share is disabled no access is granted
291
+        $initiator = $this->userManager->get($share->getSharedBy());
292
+        if ($initiator === null || !$initiator->isEnabled()) {
293
+            return false;
294
+        }
295
+
296
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
+    }
298
+
299
+    /**
300
+     * @PublicPage
301
+     * @NoCSRFRequired
302
+     *
303
+     *
304
+     * @param string $path
305
+     * @return TemplateResponse
306
+     * @throws NotFoundException
307
+     * @throws \Exception
308
+     */
309
+    public function showShare($path = ''): TemplateResponse {
310
+        \OC_User::setIncognitoMode(true);
311
+
312
+        // Check whether share exists
313
+        try {
314
+            $share = $this->shareManager->getShareByToken($this->getToken());
315
+        } catch (ShareNotFound $e) {
316
+            $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
+            throw new NotFoundException();
318
+        }
319
+
320
+        if (!$this->validateShare($share)) {
321
+            throw new NotFoundException();
322
+        }
323
+
324
+        $shareNode = $share->getNode();
325
+
326
+        // We can't get the path of a file share
327
+        try {
328
+            if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
+                $this->emitAccessShareHook($share, 404, 'Share not found');
330
+                throw new NotFoundException();
331
+            }
332
+        } catch (\Exception $e) {
333
+            $this->emitAccessShareHook($share, 404, 'Share not found');
334
+            throw $e;
335
+        }
336
+
337
+        $shareTmpl = [];
338
+        $shareTmpl['owner'] = '';
339
+        $shareTmpl['shareOwner'] = '';
340
+
341
+        $owner = $this->userManager->get($share->getShareOwner());
342
+        if ($owner instanceof IUser) {
343
+            $ownerAccount = $this->accountManager->getAccount($owner);
344
+
345
+            $ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
+            if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
+                $shareTmpl['owner'] = $owner->getUID();
348
+                $shareTmpl['shareOwner'] = $owner->getDisplayName();
349
+            }
350
+        }
351
+
352
+        $shareTmpl['filename'] = $shareNode->getName();
353
+        $shareTmpl['directory_path'] = $share->getTarget();
354
+        $shareTmpl['note'] = $share->getNote();
355
+        $shareTmpl['mimetype'] = $shareNode->getMimetype();
356
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
+        $shareTmpl['dirToken'] = $this->getToken();
358
+        $shareTmpl['sharingToken'] = $this->getToken();
359
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
+        $shareTmpl['dir'] = '';
362
+        $shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
+        $shareTmpl['hideDownload'] = $share->getHideDownload();
365
+
366
+        $hideFileList = false;
367
+
368
+        if ($shareNode instanceof \OCP\Files\Folder) {
369
+
370
+            $shareIsFolder = true;
371
+
372
+            try {
373
+                $folderNode = $shareNode->get($path);
374
+            } catch (\OCP\Files\NotFoundException $e) {
375
+                $this->emitAccessShareHook($share, 404, 'Share not found');
376
+                throw new NotFoundException();
377
+            }
378
+
379
+            $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
380
+
381
+            /*
382 382
 			 * The OC_Util methods require a view. This just uses the node API
383 383
 			 */
384
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
385
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
386
-				$freeSpace = max($freeSpace, 0);
387
-			} else {
388
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
389
-			}
390
-
391
-			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
392
-			$maxUploadFilesize = $freeSpace;
393
-
394
-			$folder = new Template('files', 'list', '');
395
-
396
-			$folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
397
-			$folder->assign('dirToken', $this->getToken());
398
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
399
-			$folder->assign('isPublic', true);
400
-			$folder->assign('hideFileList', $hideFileList);
401
-			$folder->assign('publicUploadEnabled', 'no');
402
-			// default to list view
403
-			$folder->assign('showgridview', false);
404
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
405
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
406
-			$folder->assign('freeSpace', $freeSpace);
407
-			$folder->assign('usedSpacePercent', 0);
408
-			$folder->assign('trash', false);
409
-			$shareTmpl['folder'] = $folder->fetchPage();
410
-		} else {
411
-			$shareIsFolder = false;
412
-		}
413
-
414
-		// default to list view
415
-		$shareTmpl['showgridview'] = false;
416
-
417
-		$shareTmpl['hideFileList'] = $hideFileList;
418
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
419
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
420
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
421
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
422
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
423
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
424
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
425
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
426
-
427
-		if ($shareTmpl['previewSupported']) {
428
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
429
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
430
-			$ogPreview = $shareTmpl['previewImage'];
431
-
432
-			// We just have direct previews for image files
433
-			if ($shareNode->getMimePart() === 'image') {
434
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
435
-
436
-				$ogPreview = $shareTmpl['previewURL'];
437
-
438
-				//Whatapp is kind of picky about their size requirements
439
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
440
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
441
-						'token' => $this->getToken(),
442
-						'x' => 256,
443
-						'y' => 256,
444
-						'a' => true,
445
-					]);
446
-				}
447
-			}
448
-		} else {
449
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
450
-			$ogPreview = $shareTmpl['previewImage'];
451
-		}
452
-
453
-		// Load files we need
454
-		\OCP\Util::addScript('files', 'semaphore');
455
-		\OCP\Util::addScript('files', 'file-upload');
456
-		\OCP\Util::addStyle('files_sharing', 'publicView');
457
-		\OCP\Util::addScript('files_sharing', 'public');
458
-		\OCP\Util::addScript('files_sharing', 'templates');
459
-		\OCP\Util::addScript('files', 'fileactions');
460
-		\OCP\Util::addScript('files', 'fileactionsmenu');
461
-		\OCP\Util::addScript('files', 'jquery.fileupload');
462
-		\OCP\Util::addScript('files_sharing', 'files_drop');
463
-
464
-		if (isset($shareTmpl['folder'])) {
465
-			// JS required for folders
466
-			\OCP\Util::addStyle('files', 'merged');
467
-			\OCP\Util::addScript('files', 'filesummary');
468
-			\OCP\Util::addScript('files', 'templates');
469
-			\OCP\Util::addScript('files', 'breadcrumb');
470
-			\OCP\Util::addScript('files', 'fileinfomodel');
471
-			\OCP\Util::addScript('files', 'newfilemenu');
472
-			\OCP\Util::addScript('files', 'files');
473
-			\OCP\Util::addScript('files', 'filemultiselectmenu');
474
-			\OCP\Util::addScript('files', 'filelist');
475
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
476
-			\OCP\Util::addScript('files', 'operationprogressbar');
477
-
478
-			// Load Viewer scripts
479
-			if (class_exists(LoadViewer::class)) {
480
-				$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
481
-			}
482
-		}
483
-
484
-		// OpenGraph Support: http://ogp.me/
485
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
486
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
487
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
488
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
489
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
490
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
491
-
492
-		$event = new GenericEvent(null, ['share' => $share]);
493
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts', $event);
494
-
495
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
496
-		$csp->addAllowedFrameDomain('\'self\'');
497
-
498
-		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
499
-		$response->setHeaderTitle($shareTmpl['filename']);
500
-		if ($shareTmpl['shareOwner'] !== '') {
501
-			$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
502
-		}
503
-
504
-		$isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
505
-
506
-		if ($isNoneFileDropFolder && !$share->getHideDownload()) {
507
-			\OCP\Util::addScript('files_sharing', 'public_note');
508
-
509
-			$downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
510
-			$downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
-			$download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
512
-			$downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
-			$directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
514
-			$externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
515
-
516
-			$responseComposer = [];
517
-
518
-			if ($shareIsFolder) {
519
-				$responseComposer[] = $downloadAllWhite;
520
-				$responseComposer[] = $downloadAll;
521
-			} else {
522
-				$responseComposer[] = $downloadWhite;
523
-				$responseComposer[] = $download;
524
-			}
525
-			$responseComposer[] = $directLink;
526
-			if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
527
-				$responseComposer[] = $externalShare;
528
-			}
529
-
530
-			$response->setHeaderActions($responseComposer);
531
-		}
532
-
533
-		$response->setContentSecurityPolicy($csp);
534
-
535
-		$this->emitAccessShareHook($share);
536
-
537
-		return $response;
538
-	}
539
-
540
-	/**
541
-	 * @PublicPage
542
-	 * @NoCSRFRequired
543
-	 *
544
-	 * @param string $token
545
-	 * @param string $files
546
-	 * @param string $path
547
-	 * @param string $downloadStartSecret
548
-	 * @return void|\OCP\AppFramework\Http\Response
549
-	 * @throws NotFoundException
550
-	 */
551
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
552
-		\OC_User::setIncognitoMode(true);
553
-
554
-		$share = $this->shareManager->getShareByToken($token);
555
-
556
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
557
-			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
558
-		}
559
-
560
-		$files_list = null;
561
-		if (!is_null($files)) { // download selected files
562
-			$files_list = json_decode($files);
563
-			// in case we get only a single file
564
-			if ($files_list === null) {
565
-				$files_list = [$files];
566
-			}
567
-			// Just in case $files is a single int like '1234'
568
-			if (!is_array($files_list)) {
569
-				$files_list = [$files_list];
570
-			}
571
-		}
572
-
573
-		if (!$this->validateShare($share)) {
574
-			throw new NotFoundException();
575
-		}
576
-
577
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
578
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
579
-
580
-
581
-		// Single file share
582
-		if ($share->getNode() instanceof \OCP\Files\File) {
583
-			// Single file download
584
-			$this->singleFileDownloaded($share, $share->getNode());
585
-		}
586
-		// Directory share
587
-		else {
588
-			/** @var \OCP\Files\Folder $node */
589
-			$node = $share->getNode();
590
-
591
-			// Try to get the path
592
-			if ($path !== '') {
593
-				try {
594
-					$node = $node->get($path);
595
-				} catch (NotFoundException $e) {
596
-					$this->emitAccessShareHook($share, 404, 'Share not found');
597
-					return new NotFoundResponse();
598
-				}
599
-			}
600
-
601
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
602
-
603
-			if ($node instanceof \OCP\Files\File) {
604
-				// Single file download
605
-				$this->singleFileDownloaded($share, $share->getNode());
606
-			} else {
607
-				try {
608
-					if (!empty($files_list)) {
609
-						$this->fileListDownloaded($share, $files_list, $node);
610
-					} else {
611
-						// The folder is downloaded
612
-						$this->singleFileDownloaded($share, $share->getNode());
613
-					}
614
-				} catch (NotFoundException $e) {
615
-					return new NotFoundResponse();
616
-				}
617
-			}
618
-		}
619
-
620
-		/* FIXME: We should do this all nicely in OCP */
621
-		OC_Util::tearDownFS();
622
-		OC_Util::setupFS($share->getShareOwner());
623
-
624
-		/**
625
-		 * this sets a cookie to be able to recognize the start of the download
626
-		 * the content must not be longer than 32 characters and must only contain
627
-		 * alphanumeric characters
628
-		 */
629
-		if (!empty($downloadStartSecret)
630
-			&& !isset($downloadStartSecret[32])
631
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
632
-
633
-			// FIXME: set on the response once we use an actual app framework response
634
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
635
-		}
636
-
637
-		$this->emitAccessShareHook($share);
638
-
639
-		$server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
640
-
641
-		/**
642
-		 * Http range requests support
643
-		 */
644
-		if (isset($_SERVER['HTTP_RANGE'])) {
645
-			$server_params['range'] = $this->request->getHeader('Range');
646
-		}
647
-
648
-		// download selected files
649
-		if (!is_null($files) && $files !== '') {
650
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
651
-			// after dispatching the request which results in a "Cannot modify header information" notice.
652
-			OC_Files::get($originalSharePath, $files_list, $server_params);
653
-			exit();
654
-		} else {
655
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
656
-			// after dispatching the request which results in a "Cannot modify header information" notice.
657
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
658
-			exit();
659
-		}
660
-	}
661
-
662
-	/**
663
-	 * create activity for every downloaded file
664
-	 *
665
-	 * @param Share\IShare $share
666
-	 * @param array $files_list
667
-	 * @param \OCP\Files\Folder $node
668
-	 * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
669
-	 */
670
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
671
-		if ($share->getHideDownload() && count($files_list) > 1) {
672
-			throw new NotFoundException('Downloading more than 1 file');
673
-		}
674
-
675
-		foreach ($files_list as $file) {
676
-			$subNode = $node->get($file);
677
-			$this->singleFileDownloaded($share, $subNode);
678
-		}
679
-
680
-	}
681
-
682
-	/**
683
-	 * create activity if a single file was downloaded from a link share
684
-	 *
685
-	 * @param Share\IShare $share
686
-	 * @throws NotFoundException when trying to download a folder of a "hide download" share
687
-	 */
688
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
689
-		if ($share->getHideDownload() && $node instanceof Folder) {
690
-			throw new NotFoundException('Downloading a folder');
691
-		}
692
-
693
-		$fileId = $node->getId();
694
-
695
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
696
-		$userNodeList = $userFolder->getById($fileId);
697
-		$userNode = $userNodeList[0];
698
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
699
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
700
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
701
-
702
-		$parameters = [$userPath];
703
-
704
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
705
-			if ($node instanceof \OCP\Files\File) {
706
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
707
-			} else {
708
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
709
-			}
710
-			$parameters[] = $share->getSharedWith();
711
-		} else {
712
-			if ($node instanceof \OCP\Files\File) {
713
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
714
-			} else {
715
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
716
-			}
717
-		}
718
-
719
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
720
-
721
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
722
-			$parameters[0] = $ownerPath;
723
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
724
-		}
725
-	}
726
-
727
-	/**
728
-	 * publish activity
729
-	 *
730
-	 * @param string $subject
731
-	 * @param array $parameters
732
-	 * @param string $affectedUser
733
-	 * @param int $fileId
734
-	 * @param string $filePath
735
-	 */
736
-	protected function publishActivity($subject,
737
-										array $parameters,
738
-										$affectedUser,
739
-										$fileId,
740
-										$filePath) {
741
-
742
-		$event = $this->activityManager->generateEvent();
743
-		$event->setApp('files_sharing')
744
-			->setType('public_links')
745
-			->setSubject($subject, $parameters)
746
-			->setAffectedUser($affectedUser)
747
-			->setObject('files', $fileId, $filePath);
748
-		$this->activityManager->publish($event);
749
-	}
384
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
385
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
386
+                $freeSpace = max($freeSpace, 0);
387
+            } else {
388
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
389
+            }
390
+
391
+            $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
392
+            $maxUploadFilesize = $freeSpace;
393
+
394
+            $folder = new Template('files', 'list', '');
395
+
396
+            $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
397
+            $folder->assign('dirToken', $this->getToken());
398
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
399
+            $folder->assign('isPublic', true);
400
+            $folder->assign('hideFileList', $hideFileList);
401
+            $folder->assign('publicUploadEnabled', 'no');
402
+            // default to list view
403
+            $folder->assign('showgridview', false);
404
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
405
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
406
+            $folder->assign('freeSpace', $freeSpace);
407
+            $folder->assign('usedSpacePercent', 0);
408
+            $folder->assign('trash', false);
409
+            $shareTmpl['folder'] = $folder->fetchPage();
410
+        } else {
411
+            $shareIsFolder = false;
412
+        }
413
+
414
+        // default to list view
415
+        $shareTmpl['showgridview'] = false;
416
+
417
+        $shareTmpl['hideFileList'] = $hideFileList;
418
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
419
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
420
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
421
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
422
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
423
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
424
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
425
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
426
+
427
+        if ($shareTmpl['previewSupported']) {
428
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
429
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
430
+            $ogPreview = $shareTmpl['previewImage'];
431
+
432
+            // We just have direct previews for image files
433
+            if ($shareNode->getMimePart() === 'image') {
434
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
435
+
436
+                $ogPreview = $shareTmpl['previewURL'];
437
+
438
+                //Whatapp is kind of picky about their size requirements
439
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
440
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
441
+                        'token' => $this->getToken(),
442
+                        'x' => 256,
443
+                        'y' => 256,
444
+                        'a' => true,
445
+                    ]);
446
+                }
447
+            }
448
+        } else {
449
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
450
+            $ogPreview = $shareTmpl['previewImage'];
451
+        }
452
+
453
+        // Load files we need
454
+        \OCP\Util::addScript('files', 'semaphore');
455
+        \OCP\Util::addScript('files', 'file-upload');
456
+        \OCP\Util::addStyle('files_sharing', 'publicView');
457
+        \OCP\Util::addScript('files_sharing', 'public');
458
+        \OCP\Util::addScript('files_sharing', 'templates');
459
+        \OCP\Util::addScript('files', 'fileactions');
460
+        \OCP\Util::addScript('files', 'fileactionsmenu');
461
+        \OCP\Util::addScript('files', 'jquery.fileupload');
462
+        \OCP\Util::addScript('files_sharing', 'files_drop');
463
+
464
+        if (isset($shareTmpl['folder'])) {
465
+            // JS required for folders
466
+            \OCP\Util::addStyle('files', 'merged');
467
+            \OCP\Util::addScript('files', 'filesummary');
468
+            \OCP\Util::addScript('files', 'templates');
469
+            \OCP\Util::addScript('files', 'breadcrumb');
470
+            \OCP\Util::addScript('files', 'fileinfomodel');
471
+            \OCP\Util::addScript('files', 'newfilemenu');
472
+            \OCP\Util::addScript('files', 'files');
473
+            \OCP\Util::addScript('files', 'filemultiselectmenu');
474
+            \OCP\Util::addScript('files', 'filelist');
475
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
476
+            \OCP\Util::addScript('files', 'operationprogressbar');
477
+
478
+            // Load Viewer scripts
479
+            if (class_exists(LoadViewer::class)) {
480
+                $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
481
+            }
482
+        }
483
+
484
+        // OpenGraph Support: http://ogp.me/
485
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
486
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
487
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
488
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
489
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
490
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
491
+
492
+        $event = new GenericEvent(null, ['share' => $share]);
493
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts', $event);
494
+
495
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
496
+        $csp->addAllowedFrameDomain('\'self\'');
497
+
498
+        $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
499
+        $response->setHeaderTitle($shareTmpl['filename']);
500
+        if ($shareTmpl['shareOwner'] !== '') {
501
+            $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
502
+        }
503
+
504
+        $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
505
+
506
+        if ($isNoneFileDropFolder && !$share->getHideDownload()) {
507
+            \OCP\Util::addScript('files_sharing', 'public_note');
508
+
509
+            $downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
510
+            $downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
+            $download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
512
+            $downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
+            $directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
514
+            $externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
515
+
516
+            $responseComposer = [];
517
+
518
+            if ($shareIsFolder) {
519
+                $responseComposer[] = $downloadAllWhite;
520
+                $responseComposer[] = $downloadAll;
521
+            } else {
522
+                $responseComposer[] = $downloadWhite;
523
+                $responseComposer[] = $download;
524
+            }
525
+            $responseComposer[] = $directLink;
526
+            if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
527
+                $responseComposer[] = $externalShare;
528
+            }
529
+
530
+            $response->setHeaderActions($responseComposer);
531
+        }
532
+
533
+        $response->setContentSecurityPolicy($csp);
534
+
535
+        $this->emitAccessShareHook($share);
536
+
537
+        return $response;
538
+    }
539
+
540
+    /**
541
+     * @PublicPage
542
+     * @NoCSRFRequired
543
+     *
544
+     * @param string $token
545
+     * @param string $files
546
+     * @param string $path
547
+     * @param string $downloadStartSecret
548
+     * @return void|\OCP\AppFramework\Http\Response
549
+     * @throws NotFoundException
550
+     */
551
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
552
+        \OC_User::setIncognitoMode(true);
553
+
554
+        $share = $this->shareManager->getShareByToken($token);
555
+
556
+        if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
557
+            return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
558
+        }
559
+
560
+        $files_list = null;
561
+        if (!is_null($files)) { // download selected files
562
+            $files_list = json_decode($files);
563
+            // in case we get only a single file
564
+            if ($files_list === null) {
565
+                $files_list = [$files];
566
+            }
567
+            // Just in case $files is a single int like '1234'
568
+            if (!is_array($files_list)) {
569
+                $files_list = [$files_list];
570
+            }
571
+        }
572
+
573
+        if (!$this->validateShare($share)) {
574
+            throw new NotFoundException();
575
+        }
576
+
577
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
578
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
579
+
580
+
581
+        // Single file share
582
+        if ($share->getNode() instanceof \OCP\Files\File) {
583
+            // Single file download
584
+            $this->singleFileDownloaded($share, $share->getNode());
585
+        }
586
+        // Directory share
587
+        else {
588
+            /** @var \OCP\Files\Folder $node */
589
+            $node = $share->getNode();
590
+
591
+            // Try to get the path
592
+            if ($path !== '') {
593
+                try {
594
+                    $node = $node->get($path);
595
+                } catch (NotFoundException $e) {
596
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
597
+                    return new NotFoundResponse();
598
+                }
599
+            }
600
+
601
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
602
+
603
+            if ($node instanceof \OCP\Files\File) {
604
+                // Single file download
605
+                $this->singleFileDownloaded($share, $share->getNode());
606
+            } else {
607
+                try {
608
+                    if (!empty($files_list)) {
609
+                        $this->fileListDownloaded($share, $files_list, $node);
610
+                    } else {
611
+                        // The folder is downloaded
612
+                        $this->singleFileDownloaded($share, $share->getNode());
613
+                    }
614
+                } catch (NotFoundException $e) {
615
+                    return new NotFoundResponse();
616
+                }
617
+            }
618
+        }
619
+
620
+        /* FIXME: We should do this all nicely in OCP */
621
+        OC_Util::tearDownFS();
622
+        OC_Util::setupFS($share->getShareOwner());
623
+
624
+        /**
625
+         * this sets a cookie to be able to recognize the start of the download
626
+         * the content must not be longer than 32 characters and must only contain
627
+         * alphanumeric characters
628
+         */
629
+        if (!empty($downloadStartSecret)
630
+            && !isset($downloadStartSecret[32])
631
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
632
+
633
+            // FIXME: set on the response once we use an actual app framework response
634
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
635
+        }
636
+
637
+        $this->emitAccessShareHook($share);
638
+
639
+        $server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
640
+
641
+        /**
642
+         * Http range requests support
643
+         */
644
+        if (isset($_SERVER['HTTP_RANGE'])) {
645
+            $server_params['range'] = $this->request->getHeader('Range');
646
+        }
647
+
648
+        // download selected files
649
+        if (!is_null($files) && $files !== '') {
650
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
651
+            // after dispatching the request which results in a "Cannot modify header information" notice.
652
+            OC_Files::get($originalSharePath, $files_list, $server_params);
653
+            exit();
654
+        } else {
655
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
656
+            // after dispatching the request which results in a "Cannot modify header information" notice.
657
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
658
+            exit();
659
+        }
660
+    }
661
+
662
+    /**
663
+     * create activity for every downloaded file
664
+     *
665
+     * @param Share\IShare $share
666
+     * @param array $files_list
667
+     * @param \OCP\Files\Folder $node
668
+     * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
669
+     */
670
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
671
+        if ($share->getHideDownload() && count($files_list) > 1) {
672
+            throw new NotFoundException('Downloading more than 1 file');
673
+        }
674
+
675
+        foreach ($files_list as $file) {
676
+            $subNode = $node->get($file);
677
+            $this->singleFileDownloaded($share, $subNode);
678
+        }
679
+
680
+    }
681
+
682
+    /**
683
+     * create activity if a single file was downloaded from a link share
684
+     *
685
+     * @param Share\IShare $share
686
+     * @throws NotFoundException when trying to download a folder of a "hide download" share
687
+     */
688
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
689
+        if ($share->getHideDownload() && $node instanceof Folder) {
690
+            throw new NotFoundException('Downloading a folder');
691
+        }
692
+
693
+        $fileId = $node->getId();
694
+
695
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
696
+        $userNodeList = $userFolder->getById($fileId);
697
+        $userNode = $userNodeList[0];
698
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
699
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
700
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
701
+
702
+        $parameters = [$userPath];
703
+
704
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
705
+            if ($node instanceof \OCP\Files\File) {
706
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
707
+            } else {
708
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
709
+            }
710
+            $parameters[] = $share->getSharedWith();
711
+        } else {
712
+            if ($node instanceof \OCP\Files\File) {
713
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
714
+            } else {
715
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
716
+            }
717
+        }
718
+
719
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
720
+
721
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
722
+            $parameters[0] = $ownerPath;
723
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
724
+        }
725
+    }
726
+
727
+    /**
728
+     * publish activity
729
+     *
730
+     * @param string $subject
731
+     * @param array $parameters
732
+     * @param string $affectedUser
733
+     * @param int $fileId
734
+     * @param string $filePath
735
+     */
736
+    protected function publishActivity($subject,
737
+                                        array $parameters,
738
+                                        $affectedUser,
739
+                                        $fileId,
740
+                                        $filePath) {
741
+
742
+        $event = $this->activityManager->generateEvent();
743
+        $event->setApp('files_sharing')
744
+            ->setType('public_links')
745
+            ->setSubject($subject, $parameters)
746
+            ->setAffectedUser($affectedUser)
747
+            ->setObject('files', $fileId, $filePath);
748
+        $this->activityManager->publish($event);
749
+    }
750 750
 
751 751
 
752 752
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
 
230 230
 	protected function authSucceeded() {
231 231
 		// For share this was always set so it is still used in other apps
232
-		$this->session->set('public_link_authenticated', (string)$this->share->getId());
232
+		$this->session->set('public_link_authenticated', (string) $this->share->getId());
233 233
 	}
234 234
 
235 235
 	protected function authFailed() {
@@ -250,7 +250,7 @@  discard block
 block discarded – undo
250 250
 		$itemType = $itemSource = $uidOwner = '';
251 251
 		$token = $share;
252 252
 		$exception = null;
253
-		if($share instanceof \OCP\Share\IShare) {
253
+		if ($share instanceof \OCP\Share\IShare) {
254 254
 			try {
255 255
 				$token = $share->getToken();
256 256
 				$uidOwner = $share->getSharedBy();
@@ -269,7 +269,7 @@  discard block
 block discarded – undo
269 269
 			'errorCode' => $errorCode,
270 270
 			'errorMessage' => $errorMessage,
271 271
 		]);
272
-		if(!is_null($exception)) {
272
+		if (!is_null($exception)) {
273 273
 			throw $exception;
274 274
 		}
275 275
 	}
@@ -483,7 +483,7 @@  discard block
 block discarded – undo
483 483
 
484 484
 		// OpenGraph Support: http://ogp.me/
485 485
 		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
486
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
486
+		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName().($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : '')]);
487 487
 		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
488 488
 		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
489 489
 		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
@@ -553,7 +553,7 @@  discard block
 block discarded – undo
553 553
 
554 554
 		$share = $this->shareManager->getShareByToken($token);
555 555
 
556
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
556
+		if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
557 557
 			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
558 558
 		}
559 559
 
@@ -636,7 +636,7 @@  discard block
 block discarded – undo
636 636
 
637 637
 		$this->emitAccessShareHook($share);
638 638
 
639
-		$server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
639
+		$server_params = ['head' => $this->request->getMethod() === 'HEAD'];
640 640
 
641 641
 		/**
642 642
 		 * Http range requests support
Please login to merge, or discard this patch.
apps/files_external/templates/settings.php 2 patches
Indentation   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -1,51 +1,51 @@  discard block
 block discarded – undo
1 1
 <?php
2
-	use \OCA\Files_External\Lib\Backend\Backend;
2
+    use \OCA\Files_External\Lib\Backend\Backend;
3 3
 use \OCA\Files_External\Lib\Auth\AuthMechanism;
4 4
 use \OCA\Files_External\Lib\DefinitionParameter;
5 5
 use \OCA\Files_External\Service\BackendService;
6 6
 
7 7
 $canCreateMounts = $_['visibilityType'] === BackendService::VISIBILITY_ADMIN || $_['allowUserMounting'];
8 8
 
9
-	$l->t("Enable encryption");
10
-	$l->t("Enable previews");
11
-	$l->t("Enable sharing");
12
-	$l->t("Check for changes");
13
-	$l->t("Never");
14
-	$l->t("Once every direct access");
15
-	$l->t('Read only');
9
+    $l->t("Enable encryption");
10
+    $l->t("Enable previews");
11
+    $l->t("Enable sharing");
12
+    $l->t("Check for changes");
13
+    $l->t("Never");
14
+    $l->t("Once every direct access");
15
+    $l->t('Read only');
16 16
 
17
-	script('files_external', [
18
-		'settings',
19
-		'templates'
20
-	]);
21
-	style('files_external', 'settings');
17
+    script('files_external', [
18
+        'settings',
19
+        'templates'
20
+    ]);
21
+    style('files_external', 'settings');
22 22
 
23
-	// load custom JS
24
-	foreach ($_['backends'] as $backend) {
25
-		/** @var Backend $backend */
26
-		$scripts = $backend->getCustomJs();
27
-		foreach ($scripts as $script) {
28
-			script('files_external', $script);
29
-		}
30
-	}
31
-	foreach ($_['authMechanisms'] as $authMechanism) {
32
-		/** @var AuthMechanism $authMechanism */
33
-		$scripts = $authMechanism->getCustomJs();
34
-		foreach ($scripts as $script) {
35
-			script('files_external', $script);
36
-		}
37
-	}
23
+    // load custom JS
24
+    foreach ($_['backends'] as $backend) {
25
+        /** @var Backend $backend */
26
+        $scripts = $backend->getCustomJs();
27
+        foreach ($scripts as $script) {
28
+            script('files_external', $script);
29
+        }
30
+    }
31
+    foreach ($_['authMechanisms'] as $authMechanism) {
32
+        /** @var AuthMechanism $authMechanism */
33
+        $scripts = $authMechanism->getCustomJs();
34
+        foreach ($scripts as $script) {
35
+            script('files_external', $script);
36
+        }
37
+    }
38 38
 
39
-	function writeParameterInput($parameter, $options, $classes = []) {
40
-		$value = '';
41
-		if (isset($options[$parameter->getName()])) {
42
-			$value = $options[$parameter->getName()];
43
-		}
44
-		$placeholder = $parameter->getText();
45
-		$is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL);
39
+    function writeParameterInput($parameter, $options, $classes = []) {
40
+        $value = '';
41
+        if (isset($options[$parameter->getName()])) {
42
+            $value = $options[$parameter->getName()];
43
+        }
44
+        $placeholder = $parameter->getText();
45
+        $is_optional = $parameter->isFlagSet(DefinitionParameter::FLAG_OPTIONAL);
46 46
 
47
-		switch ($parameter->getType()) {
48
-		case DefinitionParameter::VALUE_PASSWORD: ?>
47
+        switch ($parameter->getType()) {
48
+        case DefinitionParameter::VALUE_PASSWORD: ?>
49 49
 			<?php if ($is_optional) { $classes[] = 'optional'; } ?>
50 50
 			<input type="password"
51 51
 				<?php if (!empty($classes)): ?> class="<?php p(implode(' ', $classes)); ?>"<?php endif; ?>
@@ -54,8 +54,8 @@  discard block
 block discarded – undo
54 54
 				placeholder="<?php p($placeholder); ?>"
55 55
 			/>
56 56
 			<?php
57
-			break;
58
-		case DefinitionParameter::VALUE_BOOLEAN: ?>
57
+            break;
58
+        case DefinitionParameter::VALUE_BOOLEAN: ?>
59 59
 			<?php $checkboxId = uniqid("checkbox_"); ?>
60 60
 			<div>
61 61
 			<label>
@@ -69,16 +69,16 @@  discard block
 block discarded – undo
69 69
 			</label>
70 70
 			</div>
71 71
 			<?php
72
-			break;
73
-		case DefinitionParameter::VALUE_HIDDEN: ?>
72
+            break;
73
+        case DefinitionParameter::VALUE_HIDDEN: ?>
74 74
 			<input type="hidden"
75 75
 				<?php if (!empty($classes)): ?> class="<?php p(implode(' ', $classes)); ?>"<?php endif; ?>
76 76
 				data-parameter="<?php p($parameter->getName()); ?>"
77 77
 				value="<?php p($value); ?>"
78 78
 			/>
79 79
 			<?php
80
-			break;
81
-		default: ?>
80
+            break;
81
+        default: ?>
82 82
 			<?php if ($is_optional) { $classes[] = 'optional'; } ?>
83 83
 			<input type="text"
84 84
 				<?php if (!empty($classes)): ?> class="<?php p(implode(' ', $classes)); ?>"<?php endif; ?>
@@ -87,8 +87,8 @@  discard block
 block discarded – undo
87 87
 				placeholder="<?php p($placeholder); ?>"
88 88
 			/>
89 89
 			<?php
90
-		}
91
-	}
90
+        }
91
+    }
92 92
 ?>
93 93
 
94 94
 <div id="emptycontent" class="hidden">
@@ -134,13 +134,13 @@  discard block
 block discarded – undo
134 134
 							<?php p($l->t('Add storage')); ?>
135 135
 						</option>
136 136
 						<?php
137
-							$sortedBackends = array_filter($_['backends'], function ($backend) use ($_) {
138
-								return $backend->isVisibleFor($_['visibilityType']);
139
-							});
140
-							uasort($sortedBackends, function ($a, $b) {
141
-								return strcasecmp($a->getText(), $b->getText());
142
-							});
143
-						?>
137
+                            $sortedBackends = array_filter($_['backends'], function ($backend) use ($_) {
138
+                                return $backend->isVisibleFor($_['visibilityType']);
139
+                            });
140
+                            uasort($sortedBackends, function ($a, $b) {
141
+                                return strcasecmp($a->getText(), $b->getText());
142
+                            });
143
+                        ?>
144 144
 						<?php foreach ($sortedBackends as $backend): ?>
145 145
 							<?php if ($backend->getDeprecateTo()) continue; // ignore deprecated backends?>
146 146
 							<option value="<?php p($backend->getIdentifier()); ?>"><?php p($backend->getText()); ?></option>
@@ -172,10 +172,10 @@  discard block
 block discarded – undo
172 172
 
173 173
 		<p id="userMountingBackends"<?php if (!$_['allowUserMounting']): ?> class="hidden"<?php endif; ?>>
174 174
 			<?php
175
-				$userBackends = array_filter($_['backends'], function ($backend) {
176
-					return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL);
177
-				});
178
-			?>
175
+                $userBackends = array_filter($_['backends'], function ($backend) {
176
+                    return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL);
177
+                });
178
+            ?>
179 179
 			<?php $i = 0; foreach ($userBackends as $backend): ?>
180 180
 				<?php if ($deprecateTo = $backend->getDeprecateTo()): ?>
181 181
 					<input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" />
Please login to merge, or discard this patch.
Braces   +24 added lines, -6 removed lines patch added patch discarded remove patch
@@ -100,7 +100,10 @@  discard block
 block discarded – undo
100 100
 	<h2 class="inlineblock" data-anchor-name="external-storage"><?php p($l->t('External storages')); ?></h2>
101 101
 	<a target="_blank" rel="noreferrer" class="icon-info" title="<?php p($l->t('Open documentation'));?>" href="<?php p(link_to_docs('admin-external-storage')); ?>"></a>
102 102
 	<p class="settings-hint"><?php p($l->t('External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow users to mount their own external storage services.')); ?></p>
103
-	<?php if (isset($_['dependencies']) and ($_['dependencies'] !== '') and $canCreateMounts) print_unescaped(''.$_['dependencies'].''); ?>
103
+	<?php if (isset($_['dependencies']) and ($_['dependencies'] !== '') and $canCreateMounts) {
104
+    print_unescaped(''.$_['dependencies'].'');
105
+}
106
+?>
104 107
 	<table id="externalStorage" class="grid" data-admin='<?php print_unescaped(json_encode($_['visibilityType'] === BackendService::VISIBILITY_ADMIN)); ?>'>
105 108
 		<thead>
106 109
 			<tr>
@@ -109,7 +112,10 @@  discard block
 block discarded – undo
109 112
 				<th><?php p($l->t('External storage')); ?></th>
110 113
 				<th><?php p($l->t('Authentication')); ?></th>
111 114
 				<th><?php p($l->t('Configuration')); ?></th>
112
-				<?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?>
115
+				<?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN) {
116
+    print_unescaped('<th>'.$l->t('Available for').'</th>');
117
+}
118
+?>
113 119
 				<th>&nbsp;</th>
114 120
 				<th>&nbsp;</th>
115 121
 				<th>&nbsp;</th>
@@ -142,7 +148,10 @@  discard block
 block discarded – undo
142 148
 							});
143 149
 						?>
144 150
 						<?php foreach ($sortedBackends as $backend): ?>
145
-							<?php if ($backend->getDeprecateTo()) continue; // ignore deprecated backends?>
151
+							<?php if ($backend->getDeprecateTo()) {
152
+    continue;
153
+}
154
+// ignore deprecated backends?>
146 155
 							<option value="<?php p($backend->getIdentifier()); ?>"><?php p($backend->getText()); ?></option>
147 156
 						<?php endforeach; ?>
148 157
 					</select>
@@ -167,7 +176,10 @@  discard block
 block discarded – undo
167 176
 
168 177
 	<?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?>
169 178
 		<input type="checkbox" name="allowUserMounting" id="allowUserMounting" class="checkbox"
170
-			value="1" <?php if ($_['allowUserMounting']) print_unescaped(' checked="checked"'); ?> />
179
+			value="1" <?php if ($_['allowUserMounting']) {
180
+    print_unescaped(' checked="checked"');
181
+}
182
+?> />
171 183
 		<label for="allowUserMounting"><?php p($l->t('Allow users to mount external storage')); ?></label> <span id="userMountingMsg" class="msg"></span>
172 184
 
173 185
 		<p id="userMountingBackends"<?php if (!$_['allowUserMounting']): ?> class="hidden"<?php endif; ?>>
@@ -179,8 +191,14 @@  discard block
 block discarded – undo
179 191
 			<?php $i = 0; foreach ($userBackends as $backend): ?>
180 192
 				<?php if ($deprecateTo = $backend->getDeprecateTo()): ?>
181 193
 					<input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" />
182
-				<?php else: ?>
183
-					<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" class="checkbox" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
194
+				<?php else {
195
+    : ?>
196
+					<input type="checkbox" id="allowUserMountingBackends<?php p($i);
197
+}
198
+?>" class="checkbox" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) {
199
+    print_unescaped(' checked="checked"');
200
+}
201
+?> />
184 202
 					<label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br />
185 203
 				<?php endif; ?>
186 204
 				<?php $i++; ?>
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SFTP.php 2 patches
Indentation   +437 added lines, -437 removed lines patch added patch discarded remove patch
@@ -46,441 +46,441 @@
 block discarded – undo
46 46
  * provide access to SFTP servers.
47 47
  */
48 48
 class SFTP extends \OC\Files\Storage\Common {
49
-	private $host;
50
-	private $user;
51
-	private $root;
52
-	private $port = 22;
53
-
54
-	private $auth = [];
55
-
56
-	/**
57
-	 * @var \phpseclib\Net\SFTP
58
-	 */
59
-	protected $client;
60
-
61
-	/**
62
-	 * @param string $host protocol://server:port
63
-	 * @return array [$server, $port]
64
-	 */
65
-	private function splitHost($host) {
66
-		$input = $host;
67
-		if (strpos($host, '://') === false) {
68
-			// add a protocol to fix parse_url behavior with ipv6
69
-			$host = 'http://' . $host;
70
-		}
71
-
72
-		$parsed = parse_url($host);
73
-		if(is_array($parsed) && isset($parsed['port'])) {
74
-			return [$parsed['host'], $parsed['port']];
75
-		} else if (is_array($parsed)) {
76
-			return [$parsed['host'], 22];
77
-		} else {
78
-			return [$input, 22];
79
-		}
80
-	}
81
-
82
-	/**
83
-	 * {@inheritdoc}
84
-	 */
85
-	public function __construct($params) {
86
-		// Register sftp://
87
-		Stream::register();
88
-
89
-		$parsedHost =  $this->splitHost($params['host']);
90
-
91
-		$this->host = $parsedHost[0];
92
-		$this->port = $parsedHost[1];
93
-
94
-		if (!isset($params['user'])) {
95
-			throw new \UnexpectedValueException('no authentication parameters specified');
96
-		}
97
-		$this->user = $params['user'];
98
-
99
-		if (isset($params['public_key_auth'])) {
100
-			$this->auth[] = $params['public_key_auth'];
101
-		}
102
-		if (isset($params['password']) && $params['password'] !== '') {
103
-			$this->auth[] = $params['password'];
104
-		}
105
-
106
-		if ($this->auth === []) {
107
-			throw new \UnexpectedValueException('no authentication parameters specified');
108
-		}
109
-
110
-		$this->root
111
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
112
-
113
-		$this->root = '/' . ltrim($this->root, '/');
114
-		$this->root = rtrim($this->root, '/') . '/';
115
-	}
116
-
117
-	/**
118
-	 * Returns the connection.
119
-	 *
120
-	 * @return \phpseclib\Net\SFTP connected client instance
121
-	 * @throws \Exception when the connection failed
122
-	 */
123
-	public function getConnection() {
124
-		if (!is_null($this->client)) {
125
-			return $this->client;
126
-		}
127
-
128
-		$hostKeys = $this->readHostKeys();
129
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
130
-
131
-		// The SSH Host Key MUST be verified before login().
132
-		$currentHostKey = $this->client->getServerPublicHostKey();
133
-		if (array_key_exists($this->host, $hostKeys)) {
134
-			if ($hostKeys[$this->host] !== $currentHostKey) {
135
-				throw new \Exception('Host public key does not match known key');
136
-			}
137
-		} else {
138
-			$hostKeys[$this->host] = $currentHostKey;
139
-			$this->writeHostKeys($hostKeys);
140
-		}
141
-
142
-		$login = false;
143
-		foreach ($this->auth as $auth) {
144
-			$login = $this->client->login($this->user, $auth);
145
-			if ($login === true) {
146
-				break;
147
-			}
148
-		}
149
-
150
-		if ($login === false) {
151
-			throw new \Exception('Login failed');
152
-		}
153
-		return $this->client;
154
-	}
155
-
156
-	/**
157
-	 * {@inheritdoc}
158
-	 */
159
-	public function test() {
160
-		if (
161
-			!isset($this->host)
162
-			|| !isset($this->user)
163
-		) {
164
-			return false;
165
-		}
166
-		return $this->getConnection()->nlist() !== false;
167
-	}
168
-
169
-	/**
170
-	 * {@inheritdoc}
171
-	 */
172
-	public function getId() {
173
-		$id = 'sftp::' . $this->user . '@' . $this->host;
174
-		if ($this->port !== 22) {
175
-			$id .= ':' . $this->port;
176
-		}
177
-		// note: this will double the root slash,
178
-		// we should not change it to keep compatible with
179
-		// old storage ids
180
-		$id .= '/' . $this->root;
181
-		return $id;
182
-	}
183
-
184
-	/**
185
-	 * @return string
186
-	 */
187
-	public function getHost() {
188
-		return $this->host;
189
-	}
190
-
191
-	/**
192
-	 * @return string
193
-	 */
194
-	public function getRoot() {
195
-		return $this->root;
196
-	}
197
-
198
-	/**
199
-	 * @return mixed
200
-	 */
201
-	public function getUser() {
202
-		return $this->user;
203
-	}
204
-
205
-	/**
206
-	 * @param string $path
207
-	 * @return string
208
-	 */
209
-	private function absPath($path) {
210
-		return $this->root . $this->cleanPath($path);
211
-	}
212
-
213
-	/**
214
-	 * @return string|false
215
-	 */
216
-	private function hostKeysPath() {
217
-		try {
218
-			$storage_view = \OCP\Files::getStorage('files_external');
219
-			if ($storage_view) {
220
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
221
-					$storage_view->getAbsolutePath('') .
222
-					'ssh_hostKeys';
223
-			}
224
-		} catch (\Exception $e) {
225
-		}
226
-		return false;
227
-	}
228
-
229
-	/**
230
-	 * @param $keys
231
-	 * @return bool
232
-	 */
233
-	protected function writeHostKeys($keys) {
234
-		try {
235
-			$keyPath = $this->hostKeysPath();
236
-			if ($keyPath && file_exists($keyPath)) {
237
-				$fp = fopen($keyPath, 'w');
238
-				foreach ($keys as $host => $key) {
239
-					fwrite($fp, $host . '::' . $key . "\n");
240
-				}
241
-				fclose($fp);
242
-				return true;
243
-			}
244
-		} catch (\Exception $e) {
245
-		}
246
-		return false;
247
-	}
248
-
249
-	/**
250
-	 * @return array
251
-	 */
252
-	protected function readHostKeys() {
253
-		try {
254
-			$keyPath = $this->hostKeysPath();
255
-			if (file_exists($keyPath)) {
256
-				$hosts = [];
257
-				$keys = [];
258
-				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
259
-				if ($lines) {
260
-					foreach ($lines as $line) {
261
-						$hostKeyArray = explode("::", $line, 2);
262
-						if (count($hostKeyArray) === 2) {
263
-							$hosts[] = $hostKeyArray[0];
264
-							$keys[] = $hostKeyArray[1];
265
-						}
266
-					}
267
-					return array_combine($hosts, $keys);
268
-				}
269
-			}
270
-		} catch (\Exception $e) {
271
-		}
272
-		return [];
273
-	}
274
-
275
-	/**
276
-	 * {@inheritdoc}
277
-	 */
278
-	public function mkdir($path) {
279
-		try {
280
-			return $this->getConnection()->mkdir($this->absPath($path));
281
-		} catch (\Exception $e) {
282
-			return false;
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * {@inheritdoc}
288
-	 */
289
-	public function rmdir($path) {
290
-		try {
291
-			$result = $this->getConnection()->delete($this->absPath($path), true);
292
-			// workaround: stray stat cache entry when deleting empty folders
293
-			// see https://github.com/phpseclib/phpseclib/issues/706
294
-			$this->getConnection()->clearStatCache();
295
-			return $result;
296
-		} catch (\Exception $e) {
297
-			return false;
298
-		}
299
-	}
300
-
301
-	/**
302
-	 * {@inheritdoc}
303
-	 */
304
-	public function opendir($path) {
305
-		try {
306
-			$list = $this->getConnection()->nlist($this->absPath($path));
307
-			if ($list === false) {
308
-				return false;
309
-			}
310
-
311
-			$id = md5('sftp:' . $path);
312
-			$dirStream = [];
313
-			foreach($list as $file) {
314
-				if ($file !== '.' && $file !== '..') {
315
-					$dirStream[] = $file;
316
-				}
317
-			}
318
-			return IteratorDirectory::wrap($dirStream);
319
-		} catch(\Exception $e) {
320
-			return false;
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * {@inheritdoc}
326
-	 */
327
-	public function filetype($path) {
328
-		try {
329
-			$stat = $this->getConnection()->stat($this->absPath($path));
330
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
331
-				return 'file';
332
-			}
333
-
334
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
335
-				return 'dir';
336
-			}
337
-		} catch (\Exception $e) {
338
-
339
-		}
340
-		return false;
341
-	}
342
-
343
-	/**
344
-	 * {@inheritdoc}
345
-	 */
346
-	public function file_exists($path) {
347
-		try {
348
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
349
-		} catch (\Exception $e) {
350
-			return false;
351
-		}
352
-	}
353
-
354
-	/**
355
-	 * {@inheritdoc}
356
-	 */
357
-	public function unlink($path) {
358
-		try {
359
-			return $this->getConnection()->delete($this->absPath($path), true);
360
-		} catch (\Exception $e) {
361
-			return false;
362
-		}
363
-	}
364
-
365
-	/**
366
-	 * {@inheritdoc}
367
-	 */
368
-	public function fopen($path, $mode) {
369
-		try {
370
-			$absPath = $this->absPath($path);
371
-			switch($mode) {
372
-				case 'r':
373
-				case 'rb':
374
-					if (!$this->file_exists($path)) {
375
-						return false;
376
-					}
377
-					SFTPReadStream::register();
378
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
379
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
380
-					return RetryWrapper::wrap($handle);
381
-				case 'w':
382
-				case 'wb':
383
-					SFTPWriteStream::register();
384
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
385
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
386
-				case 'a':
387
-				case 'ab':
388
-				case 'r+':
389
-				case 'w+':
390
-				case 'wb+':
391
-				case 'a+':
392
-				case 'x':
393
-				case 'x+':
394
-				case 'c':
395
-				case 'c+':
396
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
397
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
398
-					return RetryWrapper::wrap($handle);
399
-			}
400
-		} catch (\Exception $e) {
401
-		}
402
-		return false;
403
-	}
404
-
405
-	/**
406
-	 * {@inheritdoc}
407
-	 */
408
-	public function touch($path, $mtime=null) {
409
-		try {
410
-			if (!is_null($mtime)) {
411
-				return false;
412
-			}
413
-			if (!$this->file_exists($path)) {
414
-				$this->getConnection()->put($this->absPath($path), '');
415
-			} else {
416
-				return false;
417
-			}
418
-		} catch (\Exception $e) {
419
-			return false;
420
-		}
421
-		return true;
422
-	}
423
-
424
-	/**
425
-	 * @param string $path
426
-	 * @param string $target
427
-	 * @throws \Exception
428
-	 */
429
-	public function getFile($path, $target) {
430
-		$this->getConnection()->get($path, $target);
431
-	}
432
-
433
-	/**
434
-	 * @param string $path
435
-	 * @param string $target
436
-	 * @throws \Exception
437
-	 */
438
-	public function uploadFile($path, $target) {
439
-		$this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
440
-	}
441
-
442
-	/**
443
-	 * {@inheritdoc}
444
-	 */
445
-	public function rename($source, $target) {
446
-		try {
447
-			if ($this->file_exists($target)) {
448
-				$this->unlink($target);
449
-			}
450
-			return $this->getConnection()->rename(
451
-				$this->absPath($source),
452
-				$this->absPath($target)
453
-			);
454
-		} catch (\Exception $e) {
455
-			return false;
456
-		}
457
-	}
458
-
459
-	/**
460
-	 * {@inheritdoc}
461
-	 */
462
-	public function stat($path) {
463
-		try {
464
-			$stat = $this->getConnection()->stat($this->absPath($path));
465
-
466
-			$mtime = $stat ? $stat['mtime'] : -1;
467
-			$size = $stat ? $stat['size'] : 0;
468
-
469
-			return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
470
-		} catch (\Exception $e) {
471
-			return false;
472
-		}
473
-	}
474
-
475
-	/**
476
-	 * @param string $path
477
-	 * @return string
478
-	 */
479
-	public function constructUrl($path) {
480
-		// Do not pass the password here. We want to use the Net_SFTP object
481
-		// supplied via stream context or fail. We only supply username and
482
-		// hostname because this might show up in logs (they are not used).
483
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
484
-		return $url;
485
-	}
49
+    private $host;
50
+    private $user;
51
+    private $root;
52
+    private $port = 22;
53
+
54
+    private $auth = [];
55
+
56
+    /**
57
+     * @var \phpseclib\Net\SFTP
58
+     */
59
+    protected $client;
60
+
61
+    /**
62
+     * @param string $host protocol://server:port
63
+     * @return array [$server, $port]
64
+     */
65
+    private function splitHost($host) {
66
+        $input = $host;
67
+        if (strpos($host, '://') === false) {
68
+            // add a protocol to fix parse_url behavior with ipv6
69
+            $host = 'http://' . $host;
70
+        }
71
+
72
+        $parsed = parse_url($host);
73
+        if(is_array($parsed) && isset($parsed['port'])) {
74
+            return [$parsed['host'], $parsed['port']];
75
+        } else if (is_array($parsed)) {
76
+            return [$parsed['host'], 22];
77
+        } else {
78
+            return [$input, 22];
79
+        }
80
+    }
81
+
82
+    /**
83
+     * {@inheritdoc}
84
+     */
85
+    public function __construct($params) {
86
+        // Register sftp://
87
+        Stream::register();
88
+
89
+        $parsedHost =  $this->splitHost($params['host']);
90
+
91
+        $this->host = $parsedHost[0];
92
+        $this->port = $parsedHost[1];
93
+
94
+        if (!isset($params['user'])) {
95
+            throw new \UnexpectedValueException('no authentication parameters specified');
96
+        }
97
+        $this->user = $params['user'];
98
+
99
+        if (isset($params['public_key_auth'])) {
100
+            $this->auth[] = $params['public_key_auth'];
101
+        }
102
+        if (isset($params['password']) && $params['password'] !== '') {
103
+            $this->auth[] = $params['password'];
104
+        }
105
+
106
+        if ($this->auth === []) {
107
+            throw new \UnexpectedValueException('no authentication parameters specified');
108
+        }
109
+
110
+        $this->root
111
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
112
+
113
+        $this->root = '/' . ltrim($this->root, '/');
114
+        $this->root = rtrim($this->root, '/') . '/';
115
+    }
116
+
117
+    /**
118
+     * Returns the connection.
119
+     *
120
+     * @return \phpseclib\Net\SFTP connected client instance
121
+     * @throws \Exception when the connection failed
122
+     */
123
+    public function getConnection() {
124
+        if (!is_null($this->client)) {
125
+            return $this->client;
126
+        }
127
+
128
+        $hostKeys = $this->readHostKeys();
129
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
130
+
131
+        // The SSH Host Key MUST be verified before login().
132
+        $currentHostKey = $this->client->getServerPublicHostKey();
133
+        if (array_key_exists($this->host, $hostKeys)) {
134
+            if ($hostKeys[$this->host] !== $currentHostKey) {
135
+                throw new \Exception('Host public key does not match known key');
136
+            }
137
+        } else {
138
+            $hostKeys[$this->host] = $currentHostKey;
139
+            $this->writeHostKeys($hostKeys);
140
+        }
141
+
142
+        $login = false;
143
+        foreach ($this->auth as $auth) {
144
+            $login = $this->client->login($this->user, $auth);
145
+            if ($login === true) {
146
+                break;
147
+            }
148
+        }
149
+
150
+        if ($login === false) {
151
+            throw new \Exception('Login failed');
152
+        }
153
+        return $this->client;
154
+    }
155
+
156
+    /**
157
+     * {@inheritdoc}
158
+     */
159
+    public function test() {
160
+        if (
161
+            !isset($this->host)
162
+            || !isset($this->user)
163
+        ) {
164
+            return false;
165
+        }
166
+        return $this->getConnection()->nlist() !== false;
167
+    }
168
+
169
+    /**
170
+     * {@inheritdoc}
171
+     */
172
+    public function getId() {
173
+        $id = 'sftp::' . $this->user . '@' . $this->host;
174
+        if ($this->port !== 22) {
175
+            $id .= ':' . $this->port;
176
+        }
177
+        // note: this will double the root slash,
178
+        // we should not change it to keep compatible with
179
+        // old storage ids
180
+        $id .= '/' . $this->root;
181
+        return $id;
182
+    }
183
+
184
+    /**
185
+     * @return string
186
+     */
187
+    public function getHost() {
188
+        return $this->host;
189
+    }
190
+
191
+    /**
192
+     * @return string
193
+     */
194
+    public function getRoot() {
195
+        return $this->root;
196
+    }
197
+
198
+    /**
199
+     * @return mixed
200
+     */
201
+    public function getUser() {
202
+        return $this->user;
203
+    }
204
+
205
+    /**
206
+     * @param string $path
207
+     * @return string
208
+     */
209
+    private function absPath($path) {
210
+        return $this->root . $this->cleanPath($path);
211
+    }
212
+
213
+    /**
214
+     * @return string|false
215
+     */
216
+    private function hostKeysPath() {
217
+        try {
218
+            $storage_view = \OCP\Files::getStorage('files_external');
219
+            if ($storage_view) {
220
+                return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
221
+                    $storage_view->getAbsolutePath('') .
222
+                    'ssh_hostKeys';
223
+            }
224
+        } catch (\Exception $e) {
225
+        }
226
+        return false;
227
+    }
228
+
229
+    /**
230
+     * @param $keys
231
+     * @return bool
232
+     */
233
+    protected function writeHostKeys($keys) {
234
+        try {
235
+            $keyPath = $this->hostKeysPath();
236
+            if ($keyPath && file_exists($keyPath)) {
237
+                $fp = fopen($keyPath, 'w');
238
+                foreach ($keys as $host => $key) {
239
+                    fwrite($fp, $host . '::' . $key . "\n");
240
+                }
241
+                fclose($fp);
242
+                return true;
243
+            }
244
+        } catch (\Exception $e) {
245
+        }
246
+        return false;
247
+    }
248
+
249
+    /**
250
+     * @return array
251
+     */
252
+    protected function readHostKeys() {
253
+        try {
254
+            $keyPath = $this->hostKeysPath();
255
+            if (file_exists($keyPath)) {
256
+                $hosts = [];
257
+                $keys = [];
258
+                $lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
259
+                if ($lines) {
260
+                    foreach ($lines as $line) {
261
+                        $hostKeyArray = explode("::", $line, 2);
262
+                        if (count($hostKeyArray) === 2) {
263
+                            $hosts[] = $hostKeyArray[0];
264
+                            $keys[] = $hostKeyArray[1];
265
+                        }
266
+                    }
267
+                    return array_combine($hosts, $keys);
268
+                }
269
+            }
270
+        } catch (\Exception $e) {
271
+        }
272
+        return [];
273
+    }
274
+
275
+    /**
276
+     * {@inheritdoc}
277
+     */
278
+    public function mkdir($path) {
279
+        try {
280
+            return $this->getConnection()->mkdir($this->absPath($path));
281
+        } catch (\Exception $e) {
282
+            return false;
283
+        }
284
+    }
285
+
286
+    /**
287
+     * {@inheritdoc}
288
+     */
289
+    public function rmdir($path) {
290
+        try {
291
+            $result = $this->getConnection()->delete($this->absPath($path), true);
292
+            // workaround: stray stat cache entry when deleting empty folders
293
+            // see https://github.com/phpseclib/phpseclib/issues/706
294
+            $this->getConnection()->clearStatCache();
295
+            return $result;
296
+        } catch (\Exception $e) {
297
+            return false;
298
+        }
299
+    }
300
+
301
+    /**
302
+     * {@inheritdoc}
303
+     */
304
+    public function opendir($path) {
305
+        try {
306
+            $list = $this->getConnection()->nlist($this->absPath($path));
307
+            if ($list === false) {
308
+                return false;
309
+            }
310
+
311
+            $id = md5('sftp:' . $path);
312
+            $dirStream = [];
313
+            foreach($list as $file) {
314
+                if ($file !== '.' && $file !== '..') {
315
+                    $dirStream[] = $file;
316
+                }
317
+            }
318
+            return IteratorDirectory::wrap($dirStream);
319
+        } catch(\Exception $e) {
320
+            return false;
321
+        }
322
+    }
323
+
324
+    /**
325
+     * {@inheritdoc}
326
+     */
327
+    public function filetype($path) {
328
+        try {
329
+            $stat = $this->getConnection()->stat($this->absPath($path));
330
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
331
+                return 'file';
332
+            }
333
+
334
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
335
+                return 'dir';
336
+            }
337
+        } catch (\Exception $e) {
338
+
339
+        }
340
+        return false;
341
+    }
342
+
343
+    /**
344
+     * {@inheritdoc}
345
+     */
346
+    public function file_exists($path) {
347
+        try {
348
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
349
+        } catch (\Exception $e) {
350
+            return false;
351
+        }
352
+    }
353
+
354
+    /**
355
+     * {@inheritdoc}
356
+     */
357
+    public function unlink($path) {
358
+        try {
359
+            return $this->getConnection()->delete($this->absPath($path), true);
360
+        } catch (\Exception $e) {
361
+            return false;
362
+        }
363
+    }
364
+
365
+    /**
366
+     * {@inheritdoc}
367
+     */
368
+    public function fopen($path, $mode) {
369
+        try {
370
+            $absPath = $this->absPath($path);
371
+            switch($mode) {
372
+                case 'r':
373
+                case 'rb':
374
+                    if (!$this->file_exists($path)) {
375
+                        return false;
376
+                    }
377
+                    SFTPReadStream::register();
378
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
379
+                    $handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
380
+                    return RetryWrapper::wrap($handle);
381
+                case 'w':
382
+                case 'wb':
383
+                    SFTPWriteStream::register();
384
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
385
+                    return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
386
+                case 'a':
387
+                case 'ab':
388
+                case 'r+':
389
+                case 'w+':
390
+                case 'wb+':
391
+                case 'a+':
392
+                case 'x':
393
+                case 'x+':
394
+                case 'c':
395
+                case 'c+':
396
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
397
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
398
+                    return RetryWrapper::wrap($handle);
399
+            }
400
+        } catch (\Exception $e) {
401
+        }
402
+        return false;
403
+    }
404
+
405
+    /**
406
+     * {@inheritdoc}
407
+     */
408
+    public function touch($path, $mtime=null) {
409
+        try {
410
+            if (!is_null($mtime)) {
411
+                return false;
412
+            }
413
+            if (!$this->file_exists($path)) {
414
+                $this->getConnection()->put($this->absPath($path), '');
415
+            } else {
416
+                return false;
417
+            }
418
+        } catch (\Exception $e) {
419
+            return false;
420
+        }
421
+        return true;
422
+    }
423
+
424
+    /**
425
+     * @param string $path
426
+     * @param string $target
427
+     * @throws \Exception
428
+     */
429
+    public function getFile($path, $target) {
430
+        $this->getConnection()->get($path, $target);
431
+    }
432
+
433
+    /**
434
+     * @param string $path
435
+     * @param string $target
436
+     * @throws \Exception
437
+     */
438
+    public function uploadFile($path, $target) {
439
+        $this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
440
+    }
441
+
442
+    /**
443
+     * {@inheritdoc}
444
+     */
445
+    public function rename($source, $target) {
446
+        try {
447
+            if ($this->file_exists($target)) {
448
+                $this->unlink($target);
449
+            }
450
+            return $this->getConnection()->rename(
451
+                $this->absPath($source),
452
+                $this->absPath($target)
453
+            );
454
+        } catch (\Exception $e) {
455
+            return false;
456
+        }
457
+    }
458
+
459
+    /**
460
+     * {@inheritdoc}
461
+     */
462
+    public function stat($path) {
463
+        try {
464
+            $stat = $this->getConnection()->stat($this->absPath($path));
465
+
466
+            $mtime = $stat ? $stat['mtime'] : -1;
467
+            $size = $stat ? $stat['size'] : 0;
468
+
469
+            return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
470
+        } catch (\Exception $e) {
471
+            return false;
472
+        }
473
+    }
474
+
475
+    /**
476
+     * @param string $path
477
+     * @return string
478
+     */
479
+    public function constructUrl($path) {
480
+        // Do not pass the password here. We want to use the Net_SFTP object
481
+        // supplied via stream context or fail. We only supply username and
482
+        // hostname because this might show up in logs (they are not used).
483
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
484
+        return $url;
485
+    }
486 486
 }
Please login to merge, or discard this patch.
Spacing   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -66,11 +66,11 @@  discard block
 block discarded – undo
66 66
 		$input = $host;
67 67
 		if (strpos($host, '://') === false) {
68 68
 			// add a protocol to fix parse_url behavior with ipv6
69
-			$host = 'http://' . $host;
69
+			$host = 'http://'.$host;
70 70
 		}
71 71
 
72 72
 		$parsed = parse_url($host);
73
-		if(is_array($parsed) && isset($parsed['port'])) {
73
+		if (is_array($parsed) && isset($parsed['port'])) {
74 74
 			return [$parsed['host'], $parsed['port']];
75 75
 		} else if (is_array($parsed)) {
76 76
 			return [$parsed['host'], 22];
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 		// Register sftp://
87 87
 		Stream::register();
88 88
 
89
-		$parsedHost =  $this->splitHost($params['host']);
89
+		$parsedHost = $this->splitHost($params['host']);
90 90
 
91 91
 		$this->host = $parsedHost[0];
92 92
 		$this->port = $parsedHost[1];
@@ -110,8 +110,8 @@  discard block
 block discarded – undo
110 110
 		$this->root
111 111
 			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
112 112
 
113
-		$this->root = '/' . ltrim($this->root, '/');
114
-		$this->root = rtrim($this->root, '/') . '/';
113
+		$this->root = '/'.ltrim($this->root, '/');
114
+		$this->root = rtrim($this->root, '/').'/';
115 115
 	}
116 116
 
117 117
 	/**
@@ -170,14 +170,14 @@  discard block
 block discarded – undo
170 170
 	 * {@inheritdoc}
171 171
 	 */
172 172
 	public function getId() {
173
-		$id = 'sftp::' . $this->user . '@' . $this->host;
173
+		$id = 'sftp::'.$this->user.'@'.$this->host;
174 174
 		if ($this->port !== 22) {
175
-			$id .= ':' . $this->port;
175
+			$id .= ':'.$this->port;
176 176
 		}
177 177
 		// note: this will double the root slash,
178 178
 		// we should not change it to keep compatible with
179 179
 		// old storage ids
180
-		$id .= '/' . $this->root;
180
+		$id .= '/'.$this->root;
181 181
 		return $id;
182 182
 	}
183 183
 
@@ -207,7 +207,7 @@  discard block
 block discarded – undo
207 207
 	 * @return string
208 208
 	 */
209 209
 	private function absPath($path) {
210
-		return $this->root . $this->cleanPath($path);
210
+		return $this->root.$this->cleanPath($path);
211 211
 	}
212 212
 
213 213
 	/**
@@ -217,8 +217,8 @@  discard block
 block discarded – undo
217 217
 		try {
218 218
 			$storage_view = \OCP\Files::getStorage('files_external');
219 219
 			if ($storage_view) {
220
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
221
-					$storage_view->getAbsolutePath('') .
220
+				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').
221
+					$storage_view->getAbsolutePath('').
222 222
 					'ssh_hostKeys';
223 223
 			}
224 224
 		} catch (\Exception $e) {
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
 			if ($keyPath && file_exists($keyPath)) {
237 237
 				$fp = fopen($keyPath, 'w');
238 238
 				foreach ($keys as $host => $key) {
239
-					fwrite($fp, $host . '::' . $key . "\n");
239
+					fwrite($fp, $host.'::'.$key."\n");
240 240
 				}
241 241
 				fclose($fp);
242 242
 				return true;
@@ -308,15 +308,15 @@  discard block
 block discarded – undo
308 308
 				return false;
309 309
 			}
310 310
 
311
-			$id = md5('sftp:' . $path);
311
+			$id = md5('sftp:'.$path);
312 312
 			$dirStream = [];
313
-			foreach($list as $file) {
313
+			foreach ($list as $file) {
314 314
 				if ($file !== '.' && $file !== '..') {
315 315
 					$dirStream[] = $file;
316 316
 				}
317 317
 			}
318 318
 			return IteratorDirectory::wrap($dirStream);
319
-		} catch(\Exception $e) {
319
+		} catch (\Exception $e) {
320 320
 			return false;
321 321
 		}
322 322
 	}
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 	public function fopen($path, $mode) {
369 369
 		try {
370 370
 			$absPath = $this->absPath($path);
371
-			switch($mode) {
371
+			switch ($mode) {
372 372
 				case 'r':
373 373
 				case 'rb':
374 374
 					if (!$this->file_exists($path)) {
@@ -376,13 +376,13 @@  discard block
 block discarded – undo
376 376
 					}
377 377
 					SFTPReadStream::register();
378 378
 					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
379
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
379
+					$handle = fopen('sftpread://'.trim($absPath, '/'), 'r', false, $context);
380 380
 					return RetryWrapper::wrap($handle);
381 381
 				case 'w':
382 382
 				case 'wb':
383 383
 					SFTPWriteStream::register();
384 384
 					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
385
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
385
+					return fopen('sftpwrite://'.trim($absPath, '/'), 'w', false, $context);
386 386
 				case 'a':
387 387
 				case 'ab':
388 388
 				case 'r+':
@@ -405,7 +405,7 @@  discard block
 block discarded – undo
405 405
 	/**
406 406
 	 * {@inheritdoc}
407 407
 	 */
408
-	public function touch($path, $mtime=null) {
408
+	public function touch($path, $mtime = null) {
409 409
 		try {
410 410
 			if (!is_null($mtime)) {
411 411
 				return false;
@@ -480,7 +480,7 @@  discard block
 block discarded – undo
480 480
 		// Do not pass the password here. We want to use the Net_SFTP object
481 481
 		// supplied via stream context or fail. We only supply username and
482 482
 		// hostname because this might show up in logs (they are not used).
483
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
483
+		$url = 'sftp://'.urlencode($this->user).'@'.$this->host.':'.$this->port.$this->root.$path;
484 484
 		return $url;
485 485
 	}
486 486
 }
Please login to merge, or discard this patch.