Completed
Push — master ( 186b12...070ec6 )
by
unknown
25:38
created
lib/private/Files/Template/TemplateManager.php 1 patch
Indentation   +367 added lines, -367 removed lines patch added patch discarded remove patch
@@ -40,373 +40,373 @@
 block discarded – undo
40 40
  * @psalm-import-type FilesTemplateFile from ResponseDefinitions
41 41
  */
42 42
 class TemplateManager implements ITemplateManager {
43
-	/** @var list<callable(): TemplateFileCreator> */
44
-	private array $registeredTypes = [];
45
-	/** @var list<TemplateFileCreator> */
46
-	private array $types = [];
47
-	/** @var array<class-string<ICustomTemplateProvider>, ICustomTemplateProvider>|null */
48
-	private ?array $providers = null;
49
-	private IL10n $l10n;
50
-	private ?string $userId;
51
-
52
-	public function __construct(
53
-		private readonly ContainerInterface $serverContainer,
54
-		private readonly IEventDispatcher $eventDispatcher,
55
-		private readonly Coordinator $bootstrapCoordinator,
56
-		private readonly IRootFolder $rootFolder,
57
-		IUserSession $userSession,
58
-		private readonly IUserManager $userManager,
59
-		private readonly IPreview $previewManager,
60
-		private readonly IConfig $config,
61
-		private readonly IFactory $l10nFactory,
62
-		private readonly LoggerInterface $logger,
63
-		private readonly IFilenameValidator $filenameValidator,
64
-	) {
65
-		$this->l10n = $l10nFactory->get('lib');
66
-		$this->userId = $userSession->getUser()?->getUID();
67
-	}
68
-
69
-	#[Override]
70
-	public function registerTemplateFileCreator(callable $callback): void {
71
-		$this->registeredTypes[] = $callback;
72
-	}
73
-
74
-	/**
75
-	 * @return array<class-string<ICustomTemplateProvider>, ICustomTemplateProvider>
76
-	 */
77
-	private function getRegisteredProviders(): array {
78
-		if ($this->providers !== null) {
79
-			return $this->providers;
80
-		}
81
-
82
-		$context = $this->bootstrapCoordinator->getRegistrationContext();
83
-
84
-		$this->providers = [];
85
-		foreach ($context->getTemplateProviders() as $provider) {
86
-			$class = $provider->getService();
87
-			$this->providers[$class] = $this->serverContainer->get($class);
88
-		}
89
-		return $this->providers;
90
-	}
91
-
92
-	/**
93
-	 * @return list<TemplateFileCreator>
94
-	 */
95
-	private function getTypes(): array {
96
-		if (!empty($this->types)) {
97
-			return $this->types;
98
-		}
99
-		$this->eventDispatcher->dispatchTyped(new RegisterTemplateCreatorEvent($this));
100
-		foreach ($this->registeredTypes as $registeredType) {
101
-			$this->types[] = $registeredType();
102
-		}
103
-		return $this->types;
104
-	}
105
-
106
-	#[Override]
107
-	public function listCreators(): array {
108
-		$types = $this->getTypes();
109
-		usort($types, function (TemplateFileCreator $a, TemplateFileCreator $b) {
110
-			return $a->getOrder() - $b->getOrder();
111
-		});
112
-		return $types;
113
-	}
114
-
115
-	#[Override]
116
-	public function listTemplates(): array {
117
-		return array_values(array_map(function (TemplateFileCreator $entry) {
118
-			return array_merge($entry->jsonSerialize(), [
119
-				'templates' => $this->getTemplateFiles($entry)
120
-			]);
121
-		}, $this->listCreators()));
122
-	}
123
-
124
-	#[Override]
125
-	public function listTemplateFields(int $fileId): array {
126
-		foreach ($this->listCreators() as $creator) {
127
-			$fields = $this->getTemplateFields($creator, $fileId);
128
-			if (empty($fields)) {
129
-				continue;
130
-			}
131
-
132
-			return $fields;
133
-		}
134
-
135
-		return [];
136
-	}
137
-
138
-	#[Override]
139
-	public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user', array $templateFields = []): array {
140
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
141
-		try {
142
-			$userFolder->get($filePath);
143
-			throw new GenericFileException($this->l10n->t('File already exists'));
144
-		} catch (NotFoundException $e) {
145
-		}
146
-		try {
147
-			if (!$userFolder->nodeExists(dirname($filePath))) {
148
-				throw new GenericFileException($this->l10n->t('Invalid path'));
149
-			}
150
-			/** @var Folder $folder */
151
-			$folder = $userFolder->get(dirname($filePath));
152
-			$template = null;
153
-			if ($templateType === 'user' && $templateId !== '') {
154
-				$template = $userFolder->get($templateId);
155
-			} else {
156
-				$matchingProvider = array_filter($this->getRegisteredProviders(), function (ICustomTemplateProvider $provider) use ($templateType) {
157
-					return $templateType === get_class($provider);
158
-				});
159
-				$provider = array_shift($matchingProvider);
160
-				if ($provider) {
161
-					$template = $provider->getCustomTemplate($templateId);
162
-				}
163
-			}
164
-
165
-			$filename = basename($filePath);
166
-			$this->filenameValidator->validateFilename($filename);
167
-			$targetFile = $folder->newFile($filename, ($template instanceof File ? $template->fopen('rb') : null));
168
-
169
-			$this->eventDispatcher->dispatchTyped(new FileCreatedFromTemplateEvent($template, $targetFile, $templateFields));
170
-			/** @var File $file */
171
-			$file = $userFolder->get($filePath);
172
-			return $this->formatFile($file);
173
-		} catch (\Exception $e) {
174
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
175
-			throw new GenericFileException($this->l10n->t('Failed to create file from template'));
176
-		}
177
-	}
178
-
179
-	/**
180
-	 * @throws \OCP\Files\NotFoundException
181
-	 * @throws \OCP\Files\NotPermittedException
182
-	 * @throws \OC\User\NoUserException
183
-	 */
184
-	private function getTemplateFolder(): Folder {
185
-		if ($this->getTemplatePath() !== '') {
186
-			$path = $this->rootFolder->getUserFolder($this->userId)->get($this->getTemplatePath());
187
-			if ($path instanceof Folder) {
188
-				return $path;
189
-			}
190
-		}
191
-		throw new NotFoundException();
192
-	}
193
-
194
-	/**
195
-	 * @return list<Template>
196
-	 */
197
-	private function getTemplateFiles(TemplateFileCreator $type): array {
198
-		$templates = array_merge(
199
-			$this->getProviderTemplates($type),
200
-			$this->getUserTemplates($type)
201
-		);
202
-
203
-		$this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($templates, false));
204
-
205
-		return $templates;
206
-	}
207
-
208
-	/**
209
-	 * @return list<Template>
210
-	 */
211
-	private function getProviderTemplates(TemplateFileCreator $type): array {
212
-		$templates = [];
213
-		foreach ($this->getRegisteredProviders() as $provider) {
214
-			foreach ($type->getMimetypes() as $mimetype) {
215
-				foreach ($provider->getCustomTemplates($mimetype) as $template) {
216
-					$templateId = $template->jsonSerialize()['templateId'];
217
-					$templates[$templateId] = $template;
218
-				}
219
-			}
220
-		}
221
-
222
-		return array_values($templates);
223
-	}
224
-
225
-	/**
226
-	 * @return list<Template>
227
-	 */
228
-	private function getUserTemplates(TemplateFileCreator $type): array {
229
-		$templates = [];
230
-
231
-		try {
232
-			$userTemplateFolder = $this->getTemplateFolder();
233
-		} catch (\Exception $e) {
234
-			return $templates;
235
-		}
236
-
237
-		foreach ($type->getMimetypes() as $mimetype) {
238
-			foreach ($userTemplateFolder->searchByMime($mimetype) as $templateFile) {
239
-				if (!($templateFile instanceof File)) {
240
-					continue;
241
-				}
242
-				$template = new Template(
243
-					'user',
244
-					$this->rootFolder->getUserFolder($this->userId)->getRelativePath($templateFile->getPath()),
245
-					$templateFile
246
-				);
247
-				$template->setHasPreview($this->previewManager->isAvailable($templateFile));
248
-				$templates[] = $template;
249
-			}
250
-		}
251
-
252
-		return $templates;
253
-	}
254
-
255
-	/*
43
+    /** @var list<callable(): TemplateFileCreator> */
44
+    private array $registeredTypes = [];
45
+    /** @var list<TemplateFileCreator> */
46
+    private array $types = [];
47
+    /** @var array<class-string<ICustomTemplateProvider>, ICustomTemplateProvider>|null */
48
+    private ?array $providers = null;
49
+    private IL10n $l10n;
50
+    private ?string $userId;
51
+
52
+    public function __construct(
53
+        private readonly ContainerInterface $serverContainer,
54
+        private readonly IEventDispatcher $eventDispatcher,
55
+        private readonly Coordinator $bootstrapCoordinator,
56
+        private readonly IRootFolder $rootFolder,
57
+        IUserSession $userSession,
58
+        private readonly IUserManager $userManager,
59
+        private readonly IPreview $previewManager,
60
+        private readonly IConfig $config,
61
+        private readonly IFactory $l10nFactory,
62
+        private readonly LoggerInterface $logger,
63
+        private readonly IFilenameValidator $filenameValidator,
64
+    ) {
65
+        $this->l10n = $l10nFactory->get('lib');
66
+        $this->userId = $userSession->getUser()?->getUID();
67
+    }
68
+
69
+    #[Override]
70
+    public function registerTemplateFileCreator(callable $callback): void {
71
+        $this->registeredTypes[] = $callback;
72
+    }
73
+
74
+    /**
75
+     * @return array<class-string<ICustomTemplateProvider>, ICustomTemplateProvider>
76
+     */
77
+    private function getRegisteredProviders(): array {
78
+        if ($this->providers !== null) {
79
+            return $this->providers;
80
+        }
81
+
82
+        $context = $this->bootstrapCoordinator->getRegistrationContext();
83
+
84
+        $this->providers = [];
85
+        foreach ($context->getTemplateProviders() as $provider) {
86
+            $class = $provider->getService();
87
+            $this->providers[$class] = $this->serverContainer->get($class);
88
+        }
89
+        return $this->providers;
90
+    }
91
+
92
+    /**
93
+     * @return list<TemplateFileCreator>
94
+     */
95
+    private function getTypes(): array {
96
+        if (!empty($this->types)) {
97
+            return $this->types;
98
+        }
99
+        $this->eventDispatcher->dispatchTyped(new RegisterTemplateCreatorEvent($this));
100
+        foreach ($this->registeredTypes as $registeredType) {
101
+            $this->types[] = $registeredType();
102
+        }
103
+        return $this->types;
104
+    }
105
+
106
+    #[Override]
107
+    public function listCreators(): array {
108
+        $types = $this->getTypes();
109
+        usort($types, function (TemplateFileCreator $a, TemplateFileCreator $b) {
110
+            return $a->getOrder() - $b->getOrder();
111
+        });
112
+        return $types;
113
+    }
114
+
115
+    #[Override]
116
+    public function listTemplates(): array {
117
+        return array_values(array_map(function (TemplateFileCreator $entry) {
118
+            return array_merge($entry->jsonSerialize(), [
119
+                'templates' => $this->getTemplateFiles($entry)
120
+            ]);
121
+        }, $this->listCreators()));
122
+    }
123
+
124
+    #[Override]
125
+    public function listTemplateFields(int $fileId): array {
126
+        foreach ($this->listCreators() as $creator) {
127
+            $fields = $this->getTemplateFields($creator, $fileId);
128
+            if (empty($fields)) {
129
+                continue;
130
+            }
131
+
132
+            return $fields;
133
+        }
134
+
135
+        return [];
136
+    }
137
+
138
+    #[Override]
139
+    public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user', array $templateFields = []): array {
140
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
141
+        try {
142
+            $userFolder->get($filePath);
143
+            throw new GenericFileException($this->l10n->t('File already exists'));
144
+        } catch (NotFoundException $e) {
145
+        }
146
+        try {
147
+            if (!$userFolder->nodeExists(dirname($filePath))) {
148
+                throw new GenericFileException($this->l10n->t('Invalid path'));
149
+            }
150
+            /** @var Folder $folder */
151
+            $folder = $userFolder->get(dirname($filePath));
152
+            $template = null;
153
+            if ($templateType === 'user' && $templateId !== '') {
154
+                $template = $userFolder->get($templateId);
155
+            } else {
156
+                $matchingProvider = array_filter($this->getRegisteredProviders(), function (ICustomTemplateProvider $provider) use ($templateType) {
157
+                    return $templateType === get_class($provider);
158
+                });
159
+                $provider = array_shift($matchingProvider);
160
+                if ($provider) {
161
+                    $template = $provider->getCustomTemplate($templateId);
162
+                }
163
+            }
164
+
165
+            $filename = basename($filePath);
166
+            $this->filenameValidator->validateFilename($filename);
167
+            $targetFile = $folder->newFile($filename, ($template instanceof File ? $template->fopen('rb') : null));
168
+
169
+            $this->eventDispatcher->dispatchTyped(new FileCreatedFromTemplateEvent($template, $targetFile, $templateFields));
170
+            /** @var File $file */
171
+            $file = $userFolder->get($filePath);
172
+            return $this->formatFile($file);
173
+        } catch (\Exception $e) {
174
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
175
+            throw new GenericFileException($this->l10n->t('Failed to create file from template'));
176
+        }
177
+    }
178
+
179
+    /**
180
+     * @throws \OCP\Files\NotFoundException
181
+     * @throws \OCP\Files\NotPermittedException
182
+     * @throws \OC\User\NoUserException
183
+     */
184
+    private function getTemplateFolder(): Folder {
185
+        if ($this->getTemplatePath() !== '') {
186
+            $path = $this->rootFolder->getUserFolder($this->userId)->get($this->getTemplatePath());
187
+            if ($path instanceof Folder) {
188
+                return $path;
189
+            }
190
+        }
191
+        throw new NotFoundException();
192
+    }
193
+
194
+    /**
195
+     * @return list<Template>
196
+     */
197
+    private function getTemplateFiles(TemplateFileCreator $type): array {
198
+        $templates = array_merge(
199
+            $this->getProviderTemplates($type),
200
+            $this->getUserTemplates($type)
201
+        );
202
+
203
+        $this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($templates, false));
204
+
205
+        return $templates;
206
+    }
207
+
208
+    /**
209
+     * @return list<Template>
210
+     */
211
+    private function getProviderTemplates(TemplateFileCreator $type): array {
212
+        $templates = [];
213
+        foreach ($this->getRegisteredProviders() as $provider) {
214
+            foreach ($type->getMimetypes() as $mimetype) {
215
+                foreach ($provider->getCustomTemplates($mimetype) as $template) {
216
+                    $templateId = $template->jsonSerialize()['templateId'];
217
+                    $templates[$templateId] = $template;
218
+                }
219
+            }
220
+        }
221
+
222
+        return array_values($templates);
223
+    }
224
+
225
+    /**
226
+     * @return list<Template>
227
+     */
228
+    private function getUserTemplates(TemplateFileCreator $type): array {
229
+        $templates = [];
230
+
231
+        try {
232
+            $userTemplateFolder = $this->getTemplateFolder();
233
+        } catch (\Exception $e) {
234
+            return $templates;
235
+        }
236
+
237
+        foreach ($type->getMimetypes() as $mimetype) {
238
+            foreach ($userTemplateFolder->searchByMime($mimetype) as $templateFile) {
239
+                if (!($templateFile instanceof File)) {
240
+                    continue;
241
+                }
242
+                $template = new Template(
243
+                    'user',
244
+                    $this->rootFolder->getUserFolder($this->userId)->getRelativePath($templateFile->getPath()),
245
+                    $templateFile
246
+                );
247
+                $template->setHasPreview($this->previewManager->isAvailable($templateFile));
248
+                $templates[] = $template;
249
+            }
250
+        }
251
+
252
+        return $templates;
253
+    }
254
+
255
+    /*
256 256
 	 * @return list<Field>
257 257
 	 */
258
-	private function getTemplateFields(TemplateFileCreator $type, int $fileId): array {
259
-		$providerTemplates = $this->getProviderTemplates($type);
260
-		$userTemplates = $this->getUserTemplates($type);
261
-
262
-		$matchedTemplates = array_filter(
263
-			array_merge($providerTemplates, $userTemplates),
264
-			fn (Template $template): bool => $template->jsonSerialize()['fileid'] === $fileId);
265
-
266
-		if (empty($matchedTemplates)) {
267
-			return [];
268
-		}
269
-
270
-		$this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($matchedTemplates, true));
271
-
272
-		return array_values(array_map(static fn (Template $template): array => $template->jsonSerialize()['fields'] ?? [], $matchedTemplates));
273
-	}
274
-
275
-	/**
276
-	 * @return FilesTemplateFile
277
-	 * @throws NotFoundException
278
-	 * @throws \OCP\Files\InvalidPathException
279
-	 */
280
-	private function formatFile(File $file): array {
281
-		return [
282
-			'basename' => $file->getName(),
283
-			'etag' => $file->getEtag(),
284
-			'fileid' => $file->getId() ?? -1,
285
-			'filename' => $this->rootFolder->getUserFolder($this->userId)->getRelativePath($file->getPath()),
286
-			'lastmod' => $file->getMTime(),
287
-			'mime' => $file->getMimetype(),
288
-			'size' => $file->getSize(),
289
-			'type' => $file->getType(),
290
-			'hasPreview' => $this->previewManager->isAvailable($file),
291
-			'permissions' => $file->getPermissions(),
292
-		];
293
-	}
294
-
295
-	public function hasTemplateDirectory(): bool {
296
-		try {
297
-			$this->getTemplateFolder();
298
-			return true;
299
-		} catch (\Exception $e) {
300
-		}
301
-		return false;
302
-	}
303
-
304
-	#[Override]
305
-	public function setTemplatePath(string $path): void {
306
-		$this->config->setUserValue($this->userId, 'core', 'templateDirectory', $path);
307
-	}
308
-
309
-	#[Override]
310
-	public function getTemplatePath(): string {
311
-		return $this->config->getUserValue($this->userId, 'core', 'templateDirectory', '');
312
-	}
313
-
314
-	#[Override]
315
-	public function initializeTemplateDirectory(?string $path = null, ?string $userId = null, $copyTemplates = true): string {
316
-		if ($userId !== null) {
317
-			$this->userId = $userId;
318
-		}
319
-
320
-		$defaultSkeletonDirectory = \OC::$SERVERROOT . '/core/skeleton';
321
-		$defaultTemplateDirectory = \OC::$SERVERROOT . '/core/skeleton/Templates';
322
-		$skeletonPath = $this->config->getSystemValueString('skeletondirectory', $defaultSkeletonDirectory);
323
-		$skeletonTemplatePath = $this->config->getSystemValueString('templatedirectory', $defaultTemplateDirectory);
324
-		$isDefaultSkeleton = $skeletonPath === $defaultSkeletonDirectory;
325
-		$isDefaultTemplates = $skeletonTemplatePath === $defaultTemplateDirectory;
326
-		$userLang = $this->l10nFactory->getUserLanguage($this->userManager->get($this->userId));
327
-
328
-		if ($skeletonTemplatePath === '') {
329
-			$this->setTemplatePath('');
330
-			return '';
331
-		}
332
-
333
-		try {
334
-			$l10n = $this->l10nFactory->get('lib', $userLang);
335
-			$userFolder = $this->rootFolder->getUserFolder($this->userId);
336
-			$userTemplatePath = $path ?? $this->config->getAppValue('core', 'defaultTemplateDirectory', $l10n->t('Templates')) . '/';
337
-
338
-			// Initial user setup without a provided path
339
-			if ($path === null) {
340
-				// All locations are default so we just need to rename the directory to the users language
341
-				if ($isDefaultSkeleton && $isDefaultTemplates) {
342
-					if (!$userFolder->nodeExists('Templates')) {
343
-						return '';
344
-					}
345
-					$newPath = Filesystem::normalizePath($userFolder->getPath() . '/' . $userTemplatePath);
346
-					if ($newPath !== $userFolder->get('Templates')->getPath()) {
347
-						$userFolder->get('Templates')->move($newPath);
348
-					}
349
-					$this->setTemplatePath($userTemplatePath);
350
-					return $userTemplatePath;
351
-				}
352
-
353
-				if ($isDefaultSkeleton && !empty($skeletonTemplatePath) && !$isDefaultTemplates && $userFolder->nodeExists('Templates')) {
354
-					$shippedSkeletonTemplates = $userFolder->get('Templates');
355
-					$shippedSkeletonTemplates->delete();
356
-				}
357
-			}
358
-
359
-			$folder = $userFolder->getOrCreateFolder($userTemplatePath);
360
-
361
-			$folderIsEmpty = count($folder->getDirectoryListing()) === 0;
362
-
363
-			if (!$copyTemplates) {
364
-				$this->setTemplatePath($userTemplatePath);
365
-				return $userTemplatePath;
366
-			}
367
-
368
-			if (!$isDefaultTemplates && $folderIsEmpty) {
369
-				$localizedSkeletonTemplatePath = $this->getLocalizedTemplatePath($skeletonTemplatePath, $userLang);
370
-				if (!empty($localizedSkeletonTemplatePath) && file_exists($localizedSkeletonTemplatePath)) {
371
-					\OC_Util::copyr($localizedSkeletonTemplatePath, $folder);
372
-					$userFolder->getStorage()->getScanner()->scan($folder->getInternalPath(), Scanner::SCAN_RECURSIVE);
373
-					$this->setTemplatePath($userTemplatePath);
374
-					return $userTemplatePath;
375
-				}
376
-			}
377
-
378
-			if ($path !== null && $isDefaultSkeleton && $isDefaultTemplates && $folderIsEmpty) {
379
-				$localizedSkeletonPath = $this->getLocalizedTemplatePath($skeletonPath . '/Templates', $userLang);
380
-				if (!empty($localizedSkeletonPath) && file_exists($localizedSkeletonPath)) {
381
-					\OC_Util::copyr($localizedSkeletonPath, $folder);
382
-					$userFolder->getStorage()->getScanner()->scan($folder->getInternalPath(), Scanner::SCAN_RECURSIVE);
383
-					$this->setTemplatePath($userTemplatePath);
384
-					return $userTemplatePath;
385
-				}
386
-			}
387
-
388
-			$this->setTemplatePath($path ?? '');
389
-			return $this->getTemplatePath();
390
-		} catch (\Throwable $e) {
391
-			$this->logger->error('Failed to initialize templates directory to user language ' . $userLang . ' for ' . $userId, ['app' => 'files_templates', 'exception' => $e]);
392
-		}
393
-		$this->setTemplatePath('');
394
-		return $this->getTemplatePath();
395
-	}
396
-
397
-	private function getLocalizedTemplatePath(string $skeletonTemplatePath, string $userLang): string {
398
-		$localizedSkeletonTemplatePath = str_replace('{lang}', $userLang, $skeletonTemplatePath);
399
-
400
-		if (!file_exists($localizedSkeletonTemplatePath)) {
401
-			$dialectStart = strpos($userLang, '_');
402
-			if ($dialectStart !== false) {
403
-				$localizedSkeletonTemplatePath = str_replace('{lang}', substr($userLang, 0, $dialectStart), $skeletonTemplatePath);
404
-			}
405
-			if ($dialectStart === false || !file_exists($localizedSkeletonTemplatePath)) {
406
-				$localizedSkeletonTemplatePath = str_replace('{lang}', 'default', $skeletonTemplatePath);
407
-			}
408
-		}
409
-
410
-		return $localizedSkeletonTemplatePath;
411
-	}
258
+    private function getTemplateFields(TemplateFileCreator $type, int $fileId): array {
259
+        $providerTemplates = $this->getProviderTemplates($type);
260
+        $userTemplates = $this->getUserTemplates($type);
261
+
262
+        $matchedTemplates = array_filter(
263
+            array_merge($providerTemplates, $userTemplates),
264
+            fn (Template $template): bool => $template->jsonSerialize()['fileid'] === $fileId);
265
+
266
+        if (empty($matchedTemplates)) {
267
+            return [];
268
+        }
269
+
270
+        $this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($matchedTemplates, true));
271
+
272
+        return array_values(array_map(static fn (Template $template): array => $template->jsonSerialize()['fields'] ?? [], $matchedTemplates));
273
+    }
274
+
275
+    /**
276
+     * @return FilesTemplateFile
277
+     * @throws NotFoundException
278
+     * @throws \OCP\Files\InvalidPathException
279
+     */
280
+    private function formatFile(File $file): array {
281
+        return [
282
+            'basename' => $file->getName(),
283
+            'etag' => $file->getEtag(),
284
+            'fileid' => $file->getId() ?? -1,
285
+            'filename' => $this->rootFolder->getUserFolder($this->userId)->getRelativePath($file->getPath()),
286
+            'lastmod' => $file->getMTime(),
287
+            'mime' => $file->getMimetype(),
288
+            'size' => $file->getSize(),
289
+            'type' => $file->getType(),
290
+            'hasPreview' => $this->previewManager->isAvailable($file),
291
+            'permissions' => $file->getPermissions(),
292
+        ];
293
+    }
294
+
295
+    public function hasTemplateDirectory(): bool {
296
+        try {
297
+            $this->getTemplateFolder();
298
+            return true;
299
+        } catch (\Exception $e) {
300
+        }
301
+        return false;
302
+    }
303
+
304
+    #[Override]
305
+    public function setTemplatePath(string $path): void {
306
+        $this->config->setUserValue($this->userId, 'core', 'templateDirectory', $path);
307
+    }
308
+
309
+    #[Override]
310
+    public function getTemplatePath(): string {
311
+        return $this->config->getUserValue($this->userId, 'core', 'templateDirectory', '');
312
+    }
313
+
314
+    #[Override]
315
+    public function initializeTemplateDirectory(?string $path = null, ?string $userId = null, $copyTemplates = true): string {
316
+        if ($userId !== null) {
317
+            $this->userId = $userId;
318
+        }
319
+
320
+        $defaultSkeletonDirectory = \OC::$SERVERROOT . '/core/skeleton';
321
+        $defaultTemplateDirectory = \OC::$SERVERROOT . '/core/skeleton/Templates';
322
+        $skeletonPath = $this->config->getSystemValueString('skeletondirectory', $defaultSkeletonDirectory);
323
+        $skeletonTemplatePath = $this->config->getSystemValueString('templatedirectory', $defaultTemplateDirectory);
324
+        $isDefaultSkeleton = $skeletonPath === $defaultSkeletonDirectory;
325
+        $isDefaultTemplates = $skeletonTemplatePath === $defaultTemplateDirectory;
326
+        $userLang = $this->l10nFactory->getUserLanguage($this->userManager->get($this->userId));
327
+
328
+        if ($skeletonTemplatePath === '') {
329
+            $this->setTemplatePath('');
330
+            return '';
331
+        }
332
+
333
+        try {
334
+            $l10n = $this->l10nFactory->get('lib', $userLang);
335
+            $userFolder = $this->rootFolder->getUserFolder($this->userId);
336
+            $userTemplatePath = $path ?? $this->config->getAppValue('core', 'defaultTemplateDirectory', $l10n->t('Templates')) . '/';
337
+
338
+            // Initial user setup without a provided path
339
+            if ($path === null) {
340
+                // All locations are default so we just need to rename the directory to the users language
341
+                if ($isDefaultSkeleton && $isDefaultTemplates) {
342
+                    if (!$userFolder->nodeExists('Templates')) {
343
+                        return '';
344
+                    }
345
+                    $newPath = Filesystem::normalizePath($userFolder->getPath() . '/' . $userTemplatePath);
346
+                    if ($newPath !== $userFolder->get('Templates')->getPath()) {
347
+                        $userFolder->get('Templates')->move($newPath);
348
+                    }
349
+                    $this->setTemplatePath($userTemplatePath);
350
+                    return $userTemplatePath;
351
+                }
352
+
353
+                if ($isDefaultSkeleton && !empty($skeletonTemplatePath) && !$isDefaultTemplates && $userFolder->nodeExists('Templates')) {
354
+                    $shippedSkeletonTemplates = $userFolder->get('Templates');
355
+                    $shippedSkeletonTemplates->delete();
356
+                }
357
+            }
358
+
359
+            $folder = $userFolder->getOrCreateFolder($userTemplatePath);
360
+
361
+            $folderIsEmpty = count($folder->getDirectoryListing()) === 0;
362
+
363
+            if (!$copyTemplates) {
364
+                $this->setTemplatePath($userTemplatePath);
365
+                return $userTemplatePath;
366
+            }
367
+
368
+            if (!$isDefaultTemplates && $folderIsEmpty) {
369
+                $localizedSkeletonTemplatePath = $this->getLocalizedTemplatePath($skeletonTemplatePath, $userLang);
370
+                if (!empty($localizedSkeletonTemplatePath) && file_exists($localizedSkeletonTemplatePath)) {
371
+                    \OC_Util::copyr($localizedSkeletonTemplatePath, $folder);
372
+                    $userFolder->getStorage()->getScanner()->scan($folder->getInternalPath(), Scanner::SCAN_RECURSIVE);
373
+                    $this->setTemplatePath($userTemplatePath);
374
+                    return $userTemplatePath;
375
+                }
376
+            }
377
+
378
+            if ($path !== null && $isDefaultSkeleton && $isDefaultTemplates && $folderIsEmpty) {
379
+                $localizedSkeletonPath = $this->getLocalizedTemplatePath($skeletonPath . '/Templates', $userLang);
380
+                if (!empty($localizedSkeletonPath) && file_exists($localizedSkeletonPath)) {
381
+                    \OC_Util::copyr($localizedSkeletonPath, $folder);
382
+                    $userFolder->getStorage()->getScanner()->scan($folder->getInternalPath(), Scanner::SCAN_RECURSIVE);
383
+                    $this->setTemplatePath($userTemplatePath);
384
+                    return $userTemplatePath;
385
+                }
386
+            }
387
+
388
+            $this->setTemplatePath($path ?? '');
389
+            return $this->getTemplatePath();
390
+        } catch (\Throwable $e) {
391
+            $this->logger->error('Failed to initialize templates directory to user language ' . $userLang . ' for ' . $userId, ['app' => 'files_templates', 'exception' => $e]);
392
+        }
393
+        $this->setTemplatePath('');
394
+        return $this->getTemplatePath();
395
+    }
396
+
397
+    private function getLocalizedTemplatePath(string $skeletonTemplatePath, string $userLang): string {
398
+        $localizedSkeletonTemplatePath = str_replace('{lang}', $userLang, $skeletonTemplatePath);
399
+
400
+        if (!file_exists($localizedSkeletonTemplatePath)) {
401
+            $dialectStart = strpos($userLang, '_');
402
+            if ($dialectStart !== false) {
403
+                $localizedSkeletonTemplatePath = str_replace('{lang}', substr($userLang, 0, $dialectStart), $skeletonTemplatePath);
404
+            }
405
+            if ($dialectStart === false || !file_exists($localizedSkeletonTemplatePath)) {
406
+                $localizedSkeletonTemplatePath = str_replace('{lang}', 'default', $skeletonTemplatePath);
407
+            }
408
+        }
409
+
410
+        return $localizedSkeletonTemplatePath;
411
+    }
412 412
 }
Please login to merge, or discard this patch.