Passed
Push — master ( 95eeba...7858b8 )
by Julius
15:04 queued 15s
created
apps/files/lib/DirectEditingCapabilities.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -30,23 +30,23 @@
 block discarded – undo
30 30
 use OCP\IURLGenerator;
31 31
 
32 32
 class DirectEditingCapabilities implements ICapability, IInitialStateExcludedCapability {
33
-	protected DirectEditingService $directEditingService;
34
-	protected IURLGenerator $urlGenerator;
33
+    protected DirectEditingService $directEditingService;
34
+    protected IURLGenerator $urlGenerator;
35 35
 
36
-	public function __construct(DirectEditingService $directEditingService, IURLGenerator $urlGenerator) {
37
-		$this->directEditingService = $directEditingService;
38
-		$this->urlGenerator = $urlGenerator;
39
-	}
36
+    public function __construct(DirectEditingService $directEditingService, IURLGenerator $urlGenerator) {
37
+        $this->directEditingService = $directEditingService;
38
+        $this->urlGenerator = $urlGenerator;
39
+    }
40 40
 
41
-	public function getCapabilities() {
42
-		return [
43
-			'files' => [
44
-				'directEditing' => [
45
-					'url' => $this->urlGenerator->linkToOCSRouteAbsolute('files.DirectEditing.info'),
46
-					'etag' => $this->directEditingService->getDirectEditingETag(),
47
-					'supportsFileId' => true,
48
-				]
49
-			],
50
-		];
51
-	}
41
+    public function getCapabilities() {
42
+        return [
43
+            'files' => [
44
+                'directEditing' => [
45
+                    'url' => $this->urlGenerator->linkToOCSRouteAbsolute('files.DirectEditing.info'),
46
+                    'etag' => $this->directEditingService->getDirectEditingETag(),
47
+                    'supportsFileId' => true,
48
+                ]
49
+            ],
50
+        ];
51
+    }
52 52
 }
Please login to merge, or discard this patch.
apps/files/lib/Controller/DirectEditingController.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -35,97 +35,97 @@
 block discarded – undo
35 35
 use OCP\IURLGenerator;
36 36
 
37 37
 class DirectEditingController extends OCSController {
38
-	/** @var IEventDispatcher */
39
-	private $eventDispatcher;
40
-
41
-	/** @var IManager */
42
-	private $directEditingManager;
43
-
44
-	/** @var IURLGenerator */
45
-	private $urlGenerator;
46
-
47
-	/** @var ILogger */
48
-	private $logger;
49
-
50
-	/** @var DirectEditingService */
51
-	private $directEditingService;
52
-
53
-	public function __construct($appName, IRequest $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge,
54
-								IEventDispatcher $eventDispatcher, IURLGenerator $urlGenerator, IManager $manager, DirectEditingService $directEditingService, ILogger $logger) {
55
-		parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge);
56
-
57
-		$this->eventDispatcher = $eventDispatcher;
58
-		$this->directEditingManager = $manager;
59
-		$this->directEditingService = $directEditingService;
60
-		$this->logger = $logger;
61
-		$this->urlGenerator = $urlGenerator;
62
-	}
63
-
64
-	/**
65
-	 * @NoAdminRequired
66
-	 */
67
-	public function info(): DataResponse {
68
-		$response = new DataResponse($this->directEditingService->getDirectEditingCapabilitites());
69
-		$response->setETag($this->directEditingService->getDirectEditingETag());
70
-		return $response;
71
-	}
72
-
73
-	/**
74
-	 * @NoAdminRequired
75
-	 */
76
-	public function create(string $path, string $editorId, string $creatorId, string $templateId = null): DataResponse {
77
-		if (!$this->directEditingManager->isEnabled()) {
78
-			return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
79
-		}
80
-		$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
81
-
82
-		try {
83
-			$token = $this->directEditingManager->create($path, $editorId, $creatorId, $templateId);
84
-			return new DataResponse([
85
-				'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
86
-			]);
87
-		} catch (Exception $e) {
88
-			$this->logger->logException($e, ['message' => 'Exception when creating a new file through direct editing']);
89
-			return new DataResponse(['message' => 'Failed to create file: ' . $e->getMessage()], Http::STATUS_FORBIDDEN);
90
-		}
91
-	}
92
-
93
-	/**
94
-	 * @NoAdminRequired
95
-	 */
96
-	public function open(string $path, string $editorId = null, ?int $fileId = null): DataResponse {
97
-		if (!$this->directEditingManager->isEnabled()) {
98
-			return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
99
-		}
100
-		$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
101
-
102
-		try {
103
-			$token = $this->directEditingManager->open($path, $editorId, $fileId);
104
-			return new DataResponse([
105
-				'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
106
-			]);
107
-		} catch (Exception $e) {
108
-			$this->logger->logException($e, ['message' => 'Exception when opening a file through direct editing']);
109
-			return new DataResponse(['message' => 'Failed to open file: ' . $e->getMessage()], Http::STATUS_FORBIDDEN);
110
-		}
111
-	}
112
-
113
-
114
-
115
-	/**
116
-	 * @NoAdminRequired
117
-	 */
118
-	public function templates(string $editorId, string $creatorId): DataResponse {
119
-		if (!$this->directEditingManager->isEnabled()) {
120
-			return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
121
-		}
122
-		$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
123
-
124
-		try {
125
-			return new DataResponse($this->directEditingManager->getTemplates($editorId, $creatorId));
126
-		} catch (Exception $e) {
127
-			$this->logger->logException($e);
128
-			return new DataResponse(['message' => 'Failed to obtain template list: ' . $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
129
-		}
130
-	}
38
+    /** @var IEventDispatcher */
39
+    private $eventDispatcher;
40
+
41
+    /** @var IManager */
42
+    private $directEditingManager;
43
+
44
+    /** @var IURLGenerator */
45
+    private $urlGenerator;
46
+
47
+    /** @var ILogger */
48
+    private $logger;
49
+
50
+    /** @var DirectEditingService */
51
+    private $directEditingService;
52
+
53
+    public function __construct($appName, IRequest $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge,
54
+                                IEventDispatcher $eventDispatcher, IURLGenerator $urlGenerator, IManager $manager, DirectEditingService $directEditingService, ILogger $logger) {
55
+        parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge);
56
+
57
+        $this->eventDispatcher = $eventDispatcher;
58
+        $this->directEditingManager = $manager;
59
+        $this->directEditingService = $directEditingService;
60
+        $this->logger = $logger;
61
+        $this->urlGenerator = $urlGenerator;
62
+    }
63
+
64
+    /**
65
+     * @NoAdminRequired
66
+     */
67
+    public function info(): DataResponse {
68
+        $response = new DataResponse($this->directEditingService->getDirectEditingCapabilitites());
69
+        $response->setETag($this->directEditingService->getDirectEditingETag());
70
+        return $response;
71
+    }
72
+
73
+    /**
74
+     * @NoAdminRequired
75
+     */
76
+    public function create(string $path, string $editorId, string $creatorId, string $templateId = null): DataResponse {
77
+        if (!$this->directEditingManager->isEnabled()) {
78
+            return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
79
+        }
80
+        $this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
81
+
82
+        try {
83
+            $token = $this->directEditingManager->create($path, $editorId, $creatorId, $templateId);
84
+            return new DataResponse([
85
+                'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
86
+            ]);
87
+        } catch (Exception $e) {
88
+            $this->logger->logException($e, ['message' => 'Exception when creating a new file through direct editing']);
89
+            return new DataResponse(['message' => 'Failed to create file: ' . $e->getMessage()], Http::STATUS_FORBIDDEN);
90
+        }
91
+    }
92
+
93
+    /**
94
+     * @NoAdminRequired
95
+     */
96
+    public function open(string $path, string $editorId = null, ?int $fileId = null): DataResponse {
97
+        if (!$this->directEditingManager->isEnabled()) {
98
+            return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
99
+        }
100
+        $this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
101
+
102
+        try {
103
+            $token = $this->directEditingManager->open($path, $editorId, $fileId);
104
+            return new DataResponse([
105
+                'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
106
+            ]);
107
+        } catch (Exception $e) {
108
+            $this->logger->logException($e, ['message' => 'Exception when opening a file through direct editing']);
109
+            return new DataResponse(['message' => 'Failed to open file: ' . $e->getMessage()], Http::STATUS_FORBIDDEN);
110
+        }
111
+    }
112
+
113
+
114
+
115
+    /**
116
+     * @NoAdminRequired
117
+     */
118
+    public function templates(string $editorId, string $creatorId): DataResponse {
119
+        if (!$this->directEditingManager->isEnabled()) {
120
+            return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
121
+        }
122
+        $this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
123
+
124
+        try {
125
+            return new DataResponse($this->directEditingManager->getTemplates($editorId, $creatorId));
126
+        } catch (Exception $e) {
127
+            $this->logger->logException($e);
128
+            return new DataResponse(['message' => 'Failed to obtain template list: ' . $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
129
+        }
130
+    }
131 131
 }
Please login to merge, or discard this patch.
lib/private/DirectEditing/Manager.php 2 patches
Indentation   +282 added lines, -282 removed lines patch added patch discarded remove patch
@@ -52,286 +52,286 @@
 block discarded – undo
52 52
 use function in_array;
53 53
 
54 54
 class Manager implements IManager {
55
-	private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ;
56
-
57
-	public const TABLE_TOKENS = 'direct_edit';
58
-
59
-	/** @var IEditor[] */
60
-	private $editors = [];
61
-	/** @var IDBConnection */
62
-	private $connection;
63
-	/** @var IUserSession */
64
-	private $userSession;
65
-	/** @var ISecureRandom */
66
-	private $random;
67
-	/** @var string|null */
68
-	private $userId;
69
-	/** @var IRootFolder */
70
-	private $rootFolder;
71
-	/** @var IL10N */
72
-	private $l10n;
73
-	/** @var EncryptionManager */
74
-	private $encryptionManager;
75
-
76
-	public function __construct(
77
-		ISecureRandom $random,
78
-		IDBConnection $connection,
79
-		IUserSession $userSession,
80
-		IRootFolder $rootFolder,
81
-		IFactory $l10nFactory,
82
-		EncryptionManager $encryptionManager
83
-	) {
84
-		$this->random = $random;
85
-		$this->connection = $connection;
86
-		$this->userSession = $userSession;
87
-		$this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null;
88
-		$this->rootFolder = $rootFolder;
89
-		$this->l10n = $l10nFactory->get('lib');
90
-		$this->encryptionManager = $encryptionManager;
91
-	}
92
-
93
-	public function registerDirectEditor(IEditor $directEditor): void {
94
-		$this->editors[$directEditor->getId()] = $directEditor;
95
-	}
96
-
97
-	public function getEditors(): array {
98
-		return $this->editors;
99
-	}
100
-
101
-	public function getTemplates(string $editor, string $type): array {
102
-		if (!array_key_exists($editor, $this->editors)) {
103
-			throw new \RuntimeException('No matching editor found');
104
-		}
105
-		$templates = [];
106
-		foreach ($this->editors[$editor]->getCreators() as $creator) {
107
-			if ($creator->getId() === $type) {
108
-				$templates = [
109
-					'empty' => [
110
-						'id' => 'empty',
111
-						'title' => $this->l10n->t('Empty file'),
112
-						'preview' => null
113
-					]
114
-				];
115
-
116
-				if ($creator instanceof ACreateFromTemplate) {
117
-					$templates = $creator->getTemplates();
118
-				}
119
-
120
-				$templates = array_map(function ($template) use ($creator) {
121
-					$template['extension'] = $creator->getExtension();
122
-					$template['mimetype'] = $creator->getMimetype();
123
-					return $template;
124
-				}, $templates);
125
-			}
126
-		}
127
-		$return = [];
128
-		$return['templates'] = $templates;
129
-		return $return;
130
-	}
131
-
132
-	public function create(string $path, string $editorId, string $creatorId, $templateId = null): string {
133
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
134
-		if ($userFolder->nodeExists($path)) {
135
-			throw new \RuntimeException('File already exists');
136
-		} else {
137
-			if (!$userFolder->nodeExists(dirname($path))) {
138
-				throw new \RuntimeException('Invalid path');
139
-			}
140
-			/** @var Folder $folder */
141
-			$folder = $userFolder->get(dirname($path));
142
-			$file = $folder->newFile(basename($path));
143
-			$editor = $this->getEditor($editorId);
144
-			$creators = $editor->getCreators();
145
-			foreach ($creators as $creator) {
146
-				if ($creator->getId() === $creatorId) {
147
-					$creator->create($file, $creatorId, $templateId);
148
-					return $this->createToken($editorId, $file, $path);
149
-				}
150
-			}
151
-		}
152
-
153
-		throw new \RuntimeException('No creator found');
154
-	}
155
-
156
-	public function open(string $filePath, string $editorId = null, ?int $fileId = null): string {
157
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
158
-		$file = $userFolder->get($filePath);
159
-		if ($fileId !== null && $file instanceof Folder) {
160
-			$files = $file->getById($fileId);
161
-
162
-			// Workaround to always open files with edit permissions if multiple occurences of
163
-			// the same file id are in the user home, ideally we should also track the path of the file when opening
164
-			usort($files, function (Node $a, Node $b) {
165
-				return ($b->getPermissions() & Constants::PERMISSION_UPDATE) <=> ($a->getPermissions() & Constants::PERMISSION_UPDATE);
166
-			});
167
-			$file = array_shift($files);
168
-		}
169
-
170
-		if (!$file instanceof File) {
171
-			throw new NotFoundException();
172
-		}
173
-
174
-		$filePath = $userFolder->getRelativePath($file->getPath());
175
-
176
-		if ($editorId === null) {
177
-			$editorId = $this->findEditorForFile($file);
178
-		}
179
-		if (!array_key_exists($editorId, $this->editors)) {
180
-			throw new \RuntimeException("Editor $editorId is unknown");
181
-		}
182
-
183
-		return $this->createToken($editorId, $file, $filePath);
184
-	}
185
-
186
-	private function findEditorForFile(File $file) {
187
-		foreach ($this->editors as $editor) {
188
-			if (in_array($file->getMimeType(), $editor->getMimetypes())) {
189
-				return $editor->getId();
190
-			}
191
-		}
192
-		throw new \RuntimeException('No default editor found for files mimetype');
193
-	}
194
-
195
-	public function edit(string $token): Response {
196
-		try {
197
-			/** @var IEditor $editor */
198
-			$tokenObject = $this->getToken($token);
199
-			if ($tokenObject->hasBeenAccessed()) {
200
-				throw new \RuntimeException('Token has already been used and can only be used for followup requests');
201
-			}
202
-			$editor = $this->getEditor($tokenObject->getEditor());
203
-			$this->accessToken($token);
204
-		} catch (Throwable $throwable) {
205
-			$this->invalidateToken($token);
206
-			return new NotFoundResponse();
207
-		}
208
-
209
-		try {
210
-			$this->invokeTokenScope($tokenObject->getUser());
211
-			return $editor->open($tokenObject);
212
-		} finally {
213
-			$this->revertTokenScope();
214
-		}
215
-	}
216
-
217
-	public function editSecure(File $file, string $editorId): TemplateResponse {
218
-		// TODO: Implementation in follow up
219
-	}
220
-
221
-	private function getEditor($editorId): IEditor {
222
-		if (!array_key_exists($editorId, $this->editors)) {
223
-			throw new \RuntimeException('No editor found');
224
-		}
225
-		return $this->editors[$editorId];
226
-	}
227
-
228
-	public function getToken(string $token): IToken {
229
-		$query = $this->connection->getQueryBuilder();
230
-		$query->select('*')->from(self::TABLE_TOKENS)
231
-			->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
232
-		$result = $query->execute();
233
-		if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) {
234
-			return new Token($this, $tokenRow);
235
-		}
236
-		throw new \RuntimeException('Failed to validate the token');
237
-	}
238
-
239
-	public function cleanup(): int {
240
-		$query = $this->connection->getQueryBuilder();
241
-		$query->delete(self::TABLE_TOKENS)
242
-			->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME)));
243
-		return $query->execute();
244
-	}
245
-
246
-	public function refreshToken(string $token): bool {
247
-		$query = $this->connection->getQueryBuilder();
248
-		$query->update(self::TABLE_TOKENS)
249
-			->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
250
-			->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
251
-		$result = $query->execute();
252
-		return $result !== 0;
253
-	}
254
-
255
-
256
-	public function invalidateToken(string $token): bool {
257
-		$query = $this->connection->getQueryBuilder();
258
-		$query->delete(self::TABLE_TOKENS)
259
-			->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
260
-		$result = $query->execute();
261
-		return $result !== 0;
262
-	}
263
-
264
-	public function accessToken(string $token): bool {
265
-		$query = $this->connection->getQueryBuilder();
266
-		$query->update(self::TABLE_TOKENS)
267
-			->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
268
-			->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
269
-			->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
270
-		$result = $query->execute();
271
-		return $result !== 0;
272
-	}
273
-
274
-	public function invokeTokenScope($userId): void {
275
-		\OC_User::setIncognitoMode(true);
276
-		\OC_User::setUserId($userId);
277
-	}
278
-
279
-	public function revertTokenScope(): void {
280
-		$this->userSession->setUser(null);
281
-		\OC_User::setIncognitoMode(false);
282
-	}
283
-
284
-	public function createToken($editorId, File $file, string $filePath, IShare $share = null): string {
285
-		$token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
286
-		$query = $this->connection->getQueryBuilder();
287
-		$query->insert(self::TABLE_TOKENS)
288
-			->values([
289
-				'token' => $query->createNamedParameter($token),
290
-				'editor_id' => $query->createNamedParameter($editorId),
291
-				'file_id' => $query->createNamedParameter($file->getId()),
292
-				'file_path' => $query->createNamedParameter($filePath),
293
-				'user_id' => $query->createNamedParameter($this->userId),
294
-				'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null),
295
-				'timestamp' => $query->createNamedParameter(time())
296
-			]);
297
-		$query->execute();
298
-		return $token;
299
-	}
300
-
301
-	/**
302
-	 * @param $userId
303
-	 * @param $fileId
304
-	 * @param null $filePath
305
-	 * @return Node
306
-	 * @throws NotFoundException
307
-	 */
308
-	public function getFileForToken($userId, $fileId, $filePath = null): Node {
309
-		$userFolder = $this->rootFolder->getUserFolder($userId);
310
-		if ($filePath !== null) {
311
-			return $userFolder->get($filePath);
312
-		}
313
-		$files = $userFolder->getById($fileId);
314
-		if (count($files) === 0) {
315
-			throw new NotFoundException('File nound found by id ' . $fileId);
316
-		}
317
-		return $files[0];
318
-	}
319
-
320
-	public function isEnabled(): bool {
321
-		if (!$this->encryptionManager->isEnabled()) {
322
-			return true;
323
-		}
324
-
325
-		try {
326
-			$moduleId = $this->encryptionManager->getDefaultEncryptionModuleId();
327
-			$module = $this->encryptionManager->getEncryptionModule($moduleId);
328
-			/** @var \OCA\Encryption\Util $util */
329
-			$util = \OC::$server->get(\OCA\Encryption\Util::class);
330
-			if ($module->isReadyForUser($this->userId) && $util->isMasterKeyEnabled()) {
331
-				return true;
332
-			}
333
-		} catch (Throwable $e) {
334
-		}
335
-		return false;
336
-	}
55
+    private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ;
56
+
57
+    public const TABLE_TOKENS = 'direct_edit';
58
+
59
+    /** @var IEditor[] */
60
+    private $editors = [];
61
+    /** @var IDBConnection */
62
+    private $connection;
63
+    /** @var IUserSession */
64
+    private $userSession;
65
+    /** @var ISecureRandom */
66
+    private $random;
67
+    /** @var string|null */
68
+    private $userId;
69
+    /** @var IRootFolder */
70
+    private $rootFolder;
71
+    /** @var IL10N */
72
+    private $l10n;
73
+    /** @var EncryptionManager */
74
+    private $encryptionManager;
75
+
76
+    public function __construct(
77
+        ISecureRandom $random,
78
+        IDBConnection $connection,
79
+        IUserSession $userSession,
80
+        IRootFolder $rootFolder,
81
+        IFactory $l10nFactory,
82
+        EncryptionManager $encryptionManager
83
+    ) {
84
+        $this->random = $random;
85
+        $this->connection = $connection;
86
+        $this->userSession = $userSession;
87
+        $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null;
88
+        $this->rootFolder = $rootFolder;
89
+        $this->l10n = $l10nFactory->get('lib');
90
+        $this->encryptionManager = $encryptionManager;
91
+    }
92
+
93
+    public function registerDirectEditor(IEditor $directEditor): void {
94
+        $this->editors[$directEditor->getId()] = $directEditor;
95
+    }
96
+
97
+    public function getEditors(): array {
98
+        return $this->editors;
99
+    }
100
+
101
+    public function getTemplates(string $editor, string $type): array {
102
+        if (!array_key_exists($editor, $this->editors)) {
103
+            throw new \RuntimeException('No matching editor found');
104
+        }
105
+        $templates = [];
106
+        foreach ($this->editors[$editor]->getCreators() as $creator) {
107
+            if ($creator->getId() === $type) {
108
+                $templates = [
109
+                    'empty' => [
110
+                        'id' => 'empty',
111
+                        'title' => $this->l10n->t('Empty file'),
112
+                        'preview' => null
113
+                    ]
114
+                ];
115
+
116
+                if ($creator instanceof ACreateFromTemplate) {
117
+                    $templates = $creator->getTemplates();
118
+                }
119
+
120
+                $templates = array_map(function ($template) use ($creator) {
121
+                    $template['extension'] = $creator->getExtension();
122
+                    $template['mimetype'] = $creator->getMimetype();
123
+                    return $template;
124
+                }, $templates);
125
+            }
126
+        }
127
+        $return = [];
128
+        $return['templates'] = $templates;
129
+        return $return;
130
+    }
131
+
132
+    public function create(string $path, string $editorId, string $creatorId, $templateId = null): string {
133
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
134
+        if ($userFolder->nodeExists($path)) {
135
+            throw new \RuntimeException('File already exists');
136
+        } else {
137
+            if (!$userFolder->nodeExists(dirname($path))) {
138
+                throw new \RuntimeException('Invalid path');
139
+            }
140
+            /** @var Folder $folder */
141
+            $folder = $userFolder->get(dirname($path));
142
+            $file = $folder->newFile(basename($path));
143
+            $editor = $this->getEditor($editorId);
144
+            $creators = $editor->getCreators();
145
+            foreach ($creators as $creator) {
146
+                if ($creator->getId() === $creatorId) {
147
+                    $creator->create($file, $creatorId, $templateId);
148
+                    return $this->createToken($editorId, $file, $path);
149
+                }
150
+            }
151
+        }
152
+
153
+        throw new \RuntimeException('No creator found');
154
+    }
155
+
156
+    public function open(string $filePath, string $editorId = null, ?int $fileId = null): string {
157
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
158
+        $file = $userFolder->get($filePath);
159
+        if ($fileId !== null && $file instanceof Folder) {
160
+            $files = $file->getById($fileId);
161
+
162
+            // Workaround to always open files with edit permissions if multiple occurences of
163
+            // the same file id are in the user home, ideally we should also track the path of the file when opening
164
+            usort($files, function (Node $a, Node $b) {
165
+                return ($b->getPermissions() & Constants::PERMISSION_UPDATE) <=> ($a->getPermissions() & Constants::PERMISSION_UPDATE);
166
+            });
167
+            $file = array_shift($files);
168
+        }
169
+
170
+        if (!$file instanceof File) {
171
+            throw new NotFoundException();
172
+        }
173
+
174
+        $filePath = $userFolder->getRelativePath($file->getPath());
175
+
176
+        if ($editorId === null) {
177
+            $editorId = $this->findEditorForFile($file);
178
+        }
179
+        if (!array_key_exists($editorId, $this->editors)) {
180
+            throw new \RuntimeException("Editor $editorId is unknown");
181
+        }
182
+
183
+        return $this->createToken($editorId, $file, $filePath);
184
+    }
185
+
186
+    private function findEditorForFile(File $file) {
187
+        foreach ($this->editors as $editor) {
188
+            if (in_array($file->getMimeType(), $editor->getMimetypes())) {
189
+                return $editor->getId();
190
+            }
191
+        }
192
+        throw new \RuntimeException('No default editor found for files mimetype');
193
+    }
194
+
195
+    public function edit(string $token): Response {
196
+        try {
197
+            /** @var IEditor $editor */
198
+            $tokenObject = $this->getToken($token);
199
+            if ($tokenObject->hasBeenAccessed()) {
200
+                throw new \RuntimeException('Token has already been used and can only be used for followup requests');
201
+            }
202
+            $editor = $this->getEditor($tokenObject->getEditor());
203
+            $this->accessToken($token);
204
+        } catch (Throwable $throwable) {
205
+            $this->invalidateToken($token);
206
+            return new NotFoundResponse();
207
+        }
208
+
209
+        try {
210
+            $this->invokeTokenScope($tokenObject->getUser());
211
+            return $editor->open($tokenObject);
212
+        } finally {
213
+            $this->revertTokenScope();
214
+        }
215
+    }
216
+
217
+    public function editSecure(File $file, string $editorId): TemplateResponse {
218
+        // TODO: Implementation in follow up
219
+    }
220
+
221
+    private function getEditor($editorId): IEditor {
222
+        if (!array_key_exists($editorId, $this->editors)) {
223
+            throw new \RuntimeException('No editor found');
224
+        }
225
+        return $this->editors[$editorId];
226
+    }
227
+
228
+    public function getToken(string $token): IToken {
229
+        $query = $this->connection->getQueryBuilder();
230
+        $query->select('*')->from(self::TABLE_TOKENS)
231
+            ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
232
+        $result = $query->execute();
233
+        if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) {
234
+            return new Token($this, $tokenRow);
235
+        }
236
+        throw new \RuntimeException('Failed to validate the token');
237
+    }
238
+
239
+    public function cleanup(): int {
240
+        $query = $this->connection->getQueryBuilder();
241
+        $query->delete(self::TABLE_TOKENS)
242
+            ->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME)));
243
+        return $query->execute();
244
+    }
245
+
246
+    public function refreshToken(string $token): bool {
247
+        $query = $this->connection->getQueryBuilder();
248
+        $query->update(self::TABLE_TOKENS)
249
+            ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
250
+            ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
251
+        $result = $query->execute();
252
+        return $result !== 0;
253
+    }
254
+
255
+
256
+    public function invalidateToken(string $token): bool {
257
+        $query = $this->connection->getQueryBuilder();
258
+        $query->delete(self::TABLE_TOKENS)
259
+            ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
260
+        $result = $query->execute();
261
+        return $result !== 0;
262
+    }
263
+
264
+    public function accessToken(string $token): bool {
265
+        $query = $this->connection->getQueryBuilder();
266
+        $query->update(self::TABLE_TOKENS)
267
+            ->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
268
+            ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
269
+            ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
270
+        $result = $query->execute();
271
+        return $result !== 0;
272
+    }
273
+
274
+    public function invokeTokenScope($userId): void {
275
+        \OC_User::setIncognitoMode(true);
276
+        \OC_User::setUserId($userId);
277
+    }
278
+
279
+    public function revertTokenScope(): void {
280
+        $this->userSession->setUser(null);
281
+        \OC_User::setIncognitoMode(false);
282
+    }
283
+
284
+    public function createToken($editorId, File $file, string $filePath, IShare $share = null): string {
285
+        $token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
286
+        $query = $this->connection->getQueryBuilder();
287
+        $query->insert(self::TABLE_TOKENS)
288
+            ->values([
289
+                'token' => $query->createNamedParameter($token),
290
+                'editor_id' => $query->createNamedParameter($editorId),
291
+                'file_id' => $query->createNamedParameter($file->getId()),
292
+                'file_path' => $query->createNamedParameter($filePath),
293
+                'user_id' => $query->createNamedParameter($this->userId),
294
+                'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null),
295
+                'timestamp' => $query->createNamedParameter(time())
296
+            ]);
297
+        $query->execute();
298
+        return $token;
299
+    }
300
+
301
+    /**
302
+     * @param $userId
303
+     * @param $fileId
304
+     * @param null $filePath
305
+     * @return Node
306
+     * @throws NotFoundException
307
+     */
308
+    public function getFileForToken($userId, $fileId, $filePath = null): Node {
309
+        $userFolder = $this->rootFolder->getUserFolder($userId);
310
+        if ($filePath !== null) {
311
+            return $userFolder->get($filePath);
312
+        }
313
+        $files = $userFolder->getById($fileId);
314
+        if (count($files) === 0) {
315
+            throw new NotFoundException('File nound found by id ' . $fileId);
316
+        }
317
+        return $files[0];
318
+    }
319
+
320
+    public function isEnabled(): bool {
321
+        if (!$this->encryptionManager->isEnabled()) {
322
+            return true;
323
+        }
324
+
325
+        try {
326
+            $moduleId = $this->encryptionManager->getDefaultEncryptionModuleId();
327
+            $module = $this->encryptionManager->getEncryptionModule($moduleId);
328
+            /** @var \OCA\Encryption\Util $util */
329
+            $util = \OC::$server->get(\OCA\Encryption\Util::class);
330
+            if ($module->isReadyForUser($this->userId) && $util->isMasterKeyEnabled()) {
331
+                return true;
332
+            }
333
+        } catch (Throwable $e) {
334
+        }
335
+        return false;
336
+    }
337 337
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -52,7 +52,7 @@  discard block
 block discarded – undo
52 52
 use function in_array;
53 53
 
54 54
 class Manager implements IManager {
55
-	private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ;
55
+	private const TOKEN_CLEANUP_TIME = 12 * 60 * 60;
56 56
 
57 57
 	public const TABLE_TOKENS = 'direct_edit';
58 58
 
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 					$templates = $creator->getTemplates();
118 118
 				}
119 119
 
120
-				$templates = array_map(function ($template) use ($creator) {
120
+				$templates = array_map(function($template) use ($creator) {
121 121
 					$template['extension'] = $creator->getExtension();
122 122
 					$template['mimetype'] = $creator->getMimetype();
123 123
 					return $template;
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
 
162 162
 			// Workaround to always open files with edit permissions if multiple occurences of
163 163
 			// the same file id are in the user home, ideally we should also track the path of the file when opening
164
-			usort($files, function (Node $a, Node $b) {
164
+			usort($files, function(Node $a, Node $b) {
165 165
 				return ($b->getPermissions() & Constants::PERMISSION_UPDATE) <=> ($a->getPermissions() & Constants::PERMISSION_UPDATE);
166 166
 			});
167 167
 			$file = array_shift($files);
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
 				'file_id' => $query->createNamedParameter($file->getId()),
292 292
 				'file_path' => $query->createNamedParameter($filePath),
293 293
 				'user_id' => $query->createNamedParameter($this->userId),
294
-				'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null),
294
+				'share_id' => $query->createNamedParameter($share !== null ? $share->getId() : null),
295 295
 				'timestamp' => $query->createNamedParameter(time())
296 296
 			]);
297 297
 		$query->execute();
@@ -312,7 +312,7 @@  discard block
 block discarded – undo
312 312
 		}
313 313
 		$files = $userFolder->getById($fileId);
314 314
 		if (count($files) === 0) {
315
-			throw new NotFoundException('File nound found by id ' . $fileId);
315
+			throw new NotFoundException('File nound found by id '.$fileId);
316 316
 		}
317 317
 		return $files[0];
318 318
 	}
Please login to merge, or discard this patch.