Completed
Push — master ( 445cce...52a994 )
by John
20:56
created
tests/lib/Preview/PreviewMapperTest.php 2 patches
Indentation   +62 added lines, -62 removed lines patch added patch discarded remove patch
@@ -19,76 +19,76 @@
 block discarded – undo
19 19
 
20 20
 #[\PHPUnit\Framework\Attributes\Group('DB')]
21 21
 class PreviewMapperTest extends TestCase {
22
-	private PreviewMapper $previewMapper;
23
-	private IDBConnection $connection;
24
-	private IGenerator $snowflake;
22
+    private PreviewMapper $previewMapper;
23
+    private IDBConnection $connection;
24
+    private IGenerator $snowflake;
25 25
 
26
-	public function setUp(): void {
27
-		$this->previewMapper = Server::get(PreviewMapper::class);
28
-		$this->connection = Server::get(IDBConnection::class);
29
-		$this->snowflake = Server::get(IGenerator::class);
26
+    public function setUp(): void {
27
+        $this->previewMapper = Server::get(PreviewMapper::class);
28
+        $this->connection = Server::get(IDBConnection::class);
29
+        $this->snowflake = Server::get(IGenerator::class);
30 30
 
31
-		$qb = $this->connection->getQueryBuilder();
32
-		$qb->delete('preview_locations')->executeStatement();
31
+        $qb = $this->connection->getQueryBuilder();
32
+        $qb->delete('preview_locations')->executeStatement();
33 33
 
34
-		$qb = $this->connection->getQueryBuilder();
35
-		$qb->delete('preview_versions')->executeStatement();
34
+        $qb = $this->connection->getQueryBuilder();
35
+        $qb->delete('preview_versions')->executeStatement();
36 36
 
37
-		$qb = $this->connection->getQueryBuilder();
38
-		$qb->delete('previews')->executeStatement();
39
-	}
37
+        $qb = $this->connection->getQueryBuilder();
38
+        $qb->delete('previews')->executeStatement();
39
+    }
40 40
 
41
-	public function testGetAvailablePreviews(): void {
42
-		// Empty
43
-		$this->assertEquals([], $this->previewMapper->getAvailablePreviews([]));
41
+    public function testGetAvailablePreviews(): void {
42
+        // Empty
43
+        $this->assertEquals([], $this->previewMapper->getAvailablePreviews([]));
44 44
 
45
-		// No preview available
46
-		$this->assertEquals([42 => []], $this->previewMapper->getAvailablePreviews([42]));
45
+        // No preview available
46
+        $this->assertEquals([42 => []], $this->previewMapper->getAvailablePreviews([42]));
47 47
 
48
-		$this->createPreviewForFileId(42);
49
-		$previews = $this->previewMapper->getAvailablePreviews([42]);
50
-		$this->assertNotEmpty($previews[42]);
51
-		$this->assertNull($previews[42][0]->getLocationId());
52
-		$this->assertNull($previews[42][0]->getBucketName());
53
-		$this->assertNull($previews[42][0]->getObjectStoreName());
48
+        $this->createPreviewForFileId(42);
49
+        $previews = $this->previewMapper->getAvailablePreviews([42]);
50
+        $this->assertNotEmpty($previews[42]);
51
+        $this->assertNull($previews[42][0]->getLocationId());
52
+        $this->assertNull($previews[42][0]->getBucketName());
53
+        $this->assertNull($previews[42][0]->getObjectStoreName());
54 54
 
55
-		$this->createPreviewForFileId(43, 2);
56
-		$previews = $this->previewMapper->getAvailablePreviews([43]);
57
-		$this->assertNotEmpty($previews[43]);
58
-		$this->assertEquals('preview-2', $previews[43][0]->getBucketName());
59
-		$this->assertEquals('default', $previews[43][0]->getObjectStoreName());
60
-	}
55
+        $this->createPreviewForFileId(43, 2);
56
+        $previews = $this->previewMapper->getAvailablePreviews([43]);
57
+        $this->assertNotEmpty($previews[43]);
58
+        $this->assertEquals('preview-2', $previews[43][0]->getBucketName());
59
+        $this->assertEquals('default', $previews[43][0]->getObjectStoreName());
60
+    }
61 61
 
62
-	private function createPreviewForFileId(int $fileId, ?int $bucket = null): void {
63
-		$locationId = null;
64
-		if ($bucket) {
65
-			$qb = $this->connection->getQueryBuilder();
66
-			$locationId = $this->snowflake->nextId();
67
-			$qb->insert('preview_locations')
68
-				->values([
69
-					'id' => $locationId,
70
-					'bucket_name' => $qb->createNamedParameter('preview-' . $bucket),
71
-					'object_store_name' => $qb->createNamedParameter('default'),
72
-				]);
73
-			$qb->executeStatement();
74
-		}
75
-		$preview = new Preview();
76
-		$preview->setId($this->snowflake->nextId());
77
-		$preview->setFileId($fileId);
78
-		$preview->setStorageId(1);
79
-		$preview->setCropped(true);
80
-		$preview->setMax(true);
81
-		$preview->setWidth(100);
82
-		$preview->setSourceMimeType('image/jpeg');
83
-		$preview->setHeight(100);
84
-		$preview->setSize(100);
85
-		$preview->setMtime(time());
86
-		$preview->setMimetype('image/jpeg');
87
-		$preview->setEtag('abcdefg');
62
+    private function createPreviewForFileId(int $fileId, ?int $bucket = null): void {
63
+        $locationId = null;
64
+        if ($bucket) {
65
+            $qb = $this->connection->getQueryBuilder();
66
+            $locationId = $this->snowflake->nextId();
67
+            $qb->insert('preview_locations')
68
+                ->values([
69
+                    'id' => $locationId,
70
+                    'bucket_name' => $qb->createNamedParameter('preview-' . $bucket),
71
+                    'object_store_name' => $qb->createNamedParameter('default'),
72
+                ]);
73
+            $qb->executeStatement();
74
+        }
75
+        $preview = new Preview();
76
+        $preview->setId($this->snowflake->nextId());
77
+        $preview->setFileId($fileId);
78
+        $preview->setStorageId(1);
79
+        $preview->setCropped(true);
80
+        $preview->setMax(true);
81
+        $preview->setWidth(100);
82
+        $preview->setSourceMimeType('image/jpeg');
83
+        $preview->setHeight(100);
84
+        $preview->setSize(100);
85
+        $preview->setMtime(time());
86
+        $preview->setMimetype('image/jpeg');
87
+        $preview->setEtag('abcdefg');
88 88
 
89
-		if ($locationId !== null) {
90
-			$preview->setLocationId($locationId);
91
-		}
92
-		$this->previewMapper->insert($preview);
93
-	}
89
+        if ($locationId !== null) {
90
+            $preview->setLocationId($locationId);
91
+        }
92
+        $this->previewMapper->insert($preview);
93
+    }
94 94
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -67,7 +67,7 @@
 block discarded – undo
67 67
 			$qb->insert('preview_locations')
68 68
 				->values([
69 69
 					'id' => $locationId,
70
-					'bucket_name' => $qb->createNamedParameter('preview-' . $bucket),
70
+					'bucket_name' => $qb->createNamedParameter('preview-'.$bucket),
71 71
 					'object_store_name' => $qb->createNamedParameter('default'),
72 72
 				]);
73 73
 			$qb->executeStatement();
Please login to merge, or discard this patch.
tests/lib/Preview/MovePreviewJobTest.php 1 patch
Indentation   +191 added lines, -191 removed lines patch added patch discarded remove patch
@@ -32,195 +32,195 @@
 block discarded – undo
32 32
 
33 33
 #[\PHPUnit\Framework\Attributes\Group('DB')]
34 34
 class MovePreviewJobTest extends TestCase {
35
-	private IAppData $previewAppData;
36
-	private PreviewMapper $previewMapper;
37
-	private IAppConfig&MockObject $appConfig;
38
-	private IConfig $config;
39
-	private StorageFactory $storageFactory;
40
-	private PreviewService $previewService;
41
-	private IDBConnection $db;
42
-	private IMimeTypeLoader&MockObject $mimeTypeLoader;
43
-	private IMimeTypeDetector&MockObject $mimeTypeDetector;
44
-	private LoggerInterface&MockObject $logger;
45
-
46
-	public function setUp(): void {
47
-		parent::setUp();
48
-		$this->previewAppData = Server::get(IAppDataFactory::class)->get('preview');
49
-		$this->previewMapper = Server::get(PreviewMapper::class);
50
-		$this->config = Server::get(IConfig::class);
51
-		$this->appConfig = $this->createMock(IAppConfig::class);
52
-		$this->appConfig->expects($this->any())
53
-			->method('getValueBool')
54
-			->willReturn(false);
55
-		$this->appConfig->expects($this->any())
56
-			->method('setValueBool')
57
-			->willReturn(true);
58
-		$this->storageFactory = Server::get(StorageFactory::class);
59
-		$this->previewService = Server::get(PreviewService::class);
60
-		$this->db = Server::get(IDBConnection::class);
61
-
62
-		$qb = $this->db->getQueryBuilder();
63
-		$qb->delete('filecache')
64
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
65
-			->executeStatement();
66
-
67
-		$qb = $this->db->getQueryBuilder();
68
-		$qb->insert('filecache')
69
-			->values([
70
-				'fileid' => $qb->createNamedParameter(5),
71
-				'storage' => $qb->createNamedParameter(1),
72
-				'path' => $qb->createNamedParameter('test/abc'),
73
-				'path_hash' => $qb->createNamedParameter(md5('test')),
74
-				'parent' => $qb->createNamedParameter(0),
75
-				'name' => $qb->createNamedParameter('abc'),
76
-				'mimetype' => $qb->createNamedParameter(42),
77
-				'size' => $qb->createNamedParameter(1000),
78
-				'mtime' => $qb->createNamedParameter(1000),
79
-				'storage_mtime' => $qb->createNamedParameter(1000),
80
-				'encrypted' => $qb->createNamedParameter(0),
81
-				'unencrypted_size' => $qb->createNamedParameter(0),
82
-				'etag' => $qb->createNamedParameter('abcdefg'),
83
-				'permissions' => $qb->createNamedParameter(0),
84
-				'checksum' => $qb->createNamedParameter('abcdefg'),
85
-			])->executeStatement();
86
-
87
-		$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
88
-		$this->mimeTypeDetector->method('detectPath')->willReturn('image/png');
89
-		$this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
90
-		$this->mimeTypeLoader->method('getId')->with('image/png')->willReturn(42);
91
-		$this->mimeTypeLoader->method('getMimetypeById')->with(42)->willReturn('image/png');
92
-		$this->logger = $this->createMock(LoggerInterface::class);
93
-	}
94
-
95
-	public function tearDown(): void {
96
-		foreach ($this->previewAppData->getDirectoryListing() as $folder) {
97
-			$folder->delete();
98
-		}
99
-		$this->previewService->deleteAll();
100
-
101
-		$qb = $this->db->getQueryBuilder();
102
-		$qb->delete('filecache')
103
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
104
-			->executeStatement();
105
-	}
106
-
107
-	#[TestDox('Test the migration from the legacy flat hierarchy to the new database format')]
108
-	public function testMigrationLegacyPath(): void {
109
-		$folder = $this->previewAppData->newFolder('5');
110
-		$folder->newFile('64-64-crop.jpg', 'abcdefg');
111
-		$folder->newFile('128-128-crop.png', 'abcdefg');
112
-		$this->assertEquals(1, count($this->previewAppData->getDirectoryListing()));
113
-		$this->assertEquals(2, count($folder->getDirectoryListing()));
114
-		$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
115
-
116
-		$job = new MovePreviewJob(
117
-			Server::get(ITimeFactory::class),
118
-			$this->appConfig,
119
-			$this->config,
120
-			$this->previewMapper,
121
-			$this->storageFactory,
122
-			Server::get(IDBConnection::class),
123
-			Server::get(IRootFolder::class),
124
-			$this->mimeTypeDetector,
125
-			$this->mimeTypeLoader,
126
-			$this->logger,
127
-			Server::get(IGenerator::class),
128
-			Server::get(IAppDataFactory::class),
129
-		);
130
-		$this->invokePrivate($job, 'run', [[]]);
131
-		$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
132
-		$this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
133
-	}
134
-
135
-	private static function getInternalFolder(string $name): string {
136
-		return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
137
-	}
138
-
139
-	#[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
140
-	public function testMigrationPath(): void {
141
-		$folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
142
-		$folder->newFile('64-64-crop.jpg', 'abcdefg');
143
-		$folder->newFile('128-128-crop.png', 'abcdefg');
144
-
145
-		$folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
146
-		$this->assertEquals(2, count($folder->getDirectoryListing()));
147
-		$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
148
-
149
-		$job = new MovePreviewJob(
150
-			Server::get(ITimeFactory::class),
151
-			$this->appConfig,
152
-			$this->config,
153
-			$this->previewMapper,
154
-			$this->storageFactory,
155
-			Server::get(IDBConnection::class),
156
-			Server::get(IRootFolder::class),
157
-			$this->mimeTypeDetector,
158
-			$this->mimeTypeLoader,
159
-			$this->logger,
160
-			Server::get(IGenerator::class),
161
-			Server::get(IAppDataFactory::class)
162
-		);
163
-		$this->invokePrivate($job, 'run', [[]]);
164
-		$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
165
-		$this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
166
-	}
167
-
168
-	#[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
169
-	public function testMigrationPathWithVersion(): void {
170
-		$folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
171
-		// No version
172
-		$folder->newFile('128-128-crop.png', 'abcdefg');
173
-		$folder->newFile('256-256-max.png', 'abcdefg');
174
-		$folder->newFile('128-128.png', 'abcdefg');
175
-
176
-		// Version 1000
177
-		$folder->newFile('1000-128-128-crop.png', 'abcdefg');
178
-		$folder->newFile('1000-256-256-max.png', 'abcdefg');
179
-		$folder->newFile('1000-128-128.png', 'abcdefg');
180
-
181
-		// Version 1001
182
-		$folder->newFile('1001-128-128-crop.png', 'abcdefg');
183
-		$folder->newFile('1001-256-256-max.png', 'abcdefg');
184
-		$folder->newFile('1001-128-128.png', 'abcdefg');
185
-
186
-		$folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
187
-		$this->assertEquals(9, count($folder->getDirectoryListing()));
188
-		$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
189
-
190
-		$job = new MovePreviewJob(
191
-			Server::get(ITimeFactory::class),
192
-			$this->appConfig,
193
-			$this->config,
194
-			$this->previewMapper,
195
-			$this->storageFactory,
196
-			Server::get(IDBConnection::class),
197
-			Server::get(IRootFolder::class),
198
-			$this->mimeTypeDetector,
199
-			$this->mimeTypeLoader,
200
-			$this->logger,
201
-			Server::get(IGenerator::class),
202
-			Server::get(IAppDataFactory::class)
203
-		);
204
-		$this->invokePrivate($job, 'run', [[]]);
205
-		$previews = iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5));
206
-		$this->assertEquals(9, count($previews));
207
-		$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
208
-
209
-		$nameVersionMapping = [];
210
-		foreach ($previews as $preview) {
211
-			$nameVersionMapping[$preview->getName($this->mimeTypeLoader)] = $preview->getVersion();
212
-		}
213
-
214
-		$this->assertEquals([
215
-			'1000-128-128-crop.png' => 1000,
216
-			'1000-128-128.png' => 1000,
217
-			'1000-256-256-max.png' => 1000,
218
-			'1001-128-128-crop.png' => 1001,
219
-			'1001-128-128.png' => 1001,
220
-			'1001-256-256-max.png' => 1001,
221
-			'128-128-crop.png' => null,
222
-			'128-128.png' => null,
223
-			'256-256-max.png' => null,
224
-		], $nameVersionMapping);
225
-	}
35
+    private IAppData $previewAppData;
36
+    private PreviewMapper $previewMapper;
37
+    private IAppConfig&MockObject $appConfig;
38
+    private IConfig $config;
39
+    private StorageFactory $storageFactory;
40
+    private PreviewService $previewService;
41
+    private IDBConnection $db;
42
+    private IMimeTypeLoader&MockObject $mimeTypeLoader;
43
+    private IMimeTypeDetector&MockObject $mimeTypeDetector;
44
+    private LoggerInterface&MockObject $logger;
45
+
46
+    public function setUp(): void {
47
+        parent::setUp();
48
+        $this->previewAppData = Server::get(IAppDataFactory::class)->get('preview');
49
+        $this->previewMapper = Server::get(PreviewMapper::class);
50
+        $this->config = Server::get(IConfig::class);
51
+        $this->appConfig = $this->createMock(IAppConfig::class);
52
+        $this->appConfig->expects($this->any())
53
+            ->method('getValueBool')
54
+            ->willReturn(false);
55
+        $this->appConfig->expects($this->any())
56
+            ->method('setValueBool')
57
+            ->willReturn(true);
58
+        $this->storageFactory = Server::get(StorageFactory::class);
59
+        $this->previewService = Server::get(PreviewService::class);
60
+        $this->db = Server::get(IDBConnection::class);
61
+
62
+        $qb = $this->db->getQueryBuilder();
63
+        $qb->delete('filecache')
64
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
65
+            ->executeStatement();
66
+
67
+        $qb = $this->db->getQueryBuilder();
68
+        $qb->insert('filecache')
69
+            ->values([
70
+                'fileid' => $qb->createNamedParameter(5),
71
+                'storage' => $qb->createNamedParameter(1),
72
+                'path' => $qb->createNamedParameter('test/abc'),
73
+                'path_hash' => $qb->createNamedParameter(md5('test')),
74
+                'parent' => $qb->createNamedParameter(0),
75
+                'name' => $qb->createNamedParameter('abc'),
76
+                'mimetype' => $qb->createNamedParameter(42),
77
+                'size' => $qb->createNamedParameter(1000),
78
+                'mtime' => $qb->createNamedParameter(1000),
79
+                'storage_mtime' => $qb->createNamedParameter(1000),
80
+                'encrypted' => $qb->createNamedParameter(0),
81
+                'unencrypted_size' => $qb->createNamedParameter(0),
82
+                'etag' => $qb->createNamedParameter('abcdefg'),
83
+                'permissions' => $qb->createNamedParameter(0),
84
+                'checksum' => $qb->createNamedParameter('abcdefg'),
85
+            ])->executeStatement();
86
+
87
+        $this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
88
+        $this->mimeTypeDetector->method('detectPath')->willReturn('image/png');
89
+        $this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
90
+        $this->mimeTypeLoader->method('getId')->with('image/png')->willReturn(42);
91
+        $this->mimeTypeLoader->method('getMimetypeById')->with(42)->willReturn('image/png');
92
+        $this->logger = $this->createMock(LoggerInterface::class);
93
+    }
94
+
95
+    public function tearDown(): void {
96
+        foreach ($this->previewAppData->getDirectoryListing() as $folder) {
97
+            $folder->delete();
98
+        }
99
+        $this->previewService->deleteAll();
100
+
101
+        $qb = $this->db->getQueryBuilder();
102
+        $qb->delete('filecache')
103
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
104
+            ->executeStatement();
105
+    }
106
+
107
+    #[TestDox('Test the migration from the legacy flat hierarchy to the new database format')]
108
+    public function testMigrationLegacyPath(): void {
109
+        $folder = $this->previewAppData->newFolder('5');
110
+        $folder->newFile('64-64-crop.jpg', 'abcdefg');
111
+        $folder->newFile('128-128-crop.png', 'abcdefg');
112
+        $this->assertEquals(1, count($this->previewAppData->getDirectoryListing()));
113
+        $this->assertEquals(2, count($folder->getDirectoryListing()));
114
+        $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
115
+
116
+        $job = new MovePreviewJob(
117
+            Server::get(ITimeFactory::class),
118
+            $this->appConfig,
119
+            $this->config,
120
+            $this->previewMapper,
121
+            $this->storageFactory,
122
+            Server::get(IDBConnection::class),
123
+            Server::get(IRootFolder::class),
124
+            $this->mimeTypeDetector,
125
+            $this->mimeTypeLoader,
126
+            $this->logger,
127
+            Server::get(IGenerator::class),
128
+            Server::get(IAppDataFactory::class),
129
+        );
130
+        $this->invokePrivate($job, 'run', [[]]);
131
+        $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
132
+        $this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
133
+    }
134
+
135
+    private static function getInternalFolder(string $name): string {
136
+        return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
137
+    }
138
+
139
+    #[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
140
+    public function testMigrationPath(): void {
141
+        $folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
142
+        $folder->newFile('64-64-crop.jpg', 'abcdefg');
143
+        $folder->newFile('128-128-crop.png', 'abcdefg');
144
+
145
+        $folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
146
+        $this->assertEquals(2, count($folder->getDirectoryListing()));
147
+        $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
148
+
149
+        $job = new MovePreviewJob(
150
+            Server::get(ITimeFactory::class),
151
+            $this->appConfig,
152
+            $this->config,
153
+            $this->previewMapper,
154
+            $this->storageFactory,
155
+            Server::get(IDBConnection::class),
156
+            Server::get(IRootFolder::class),
157
+            $this->mimeTypeDetector,
158
+            $this->mimeTypeLoader,
159
+            $this->logger,
160
+            Server::get(IGenerator::class),
161
+            Server::get(IAppDataFactory::class)
162
+        );
163
+        $this->invokePrivate($job, 'run', [[]]);
164
+        $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
165
+        $this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
166
+    }
167
+
168
+    #[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
169
+    public function testMigrationPathWithVersion(): void {
170
+        $folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
171
+        // No version
172
+        $folder->newFile('128-128-crop.png', 'abcdefg');
173
+        $folder->newFile('256-256-max.png', 'abcdefg');
174
+        $folder->newFile('128-128.png', 'abcdefg');
175
+
176
+        // Version 1000
177
+        $folder->newFile('1000-128-128-crop.png', 'abcdefg');
178
+        $folder->newFile('1000-256-256-max.png', 'abcdefg');
179
+        $folder->newFile('1000-128-128.png', 'abcdefg');
180
+
181
+        // Version 1001
182
+        $folder->newFile('1001-128-128-crop.png', 'abcdefg');
183
+        $folder->newFile('1001-256-256-max.png', 'abcdefg');
184
+        $folder->newFile('1001-128-128.png', 'abcdefg');
185
+
186
+        $folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
187
+        $this->assertEquals(9, count($folder->getDirectoryListing()));
188
+        $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
189
+
190
+        $job = new MovePreviewJob(
191
+            Server::get(ITimeFactory::class),
192
+            $this->appConfig,
193
+            $this->config,
194
+            $this->previewMapper,
195
+            $this->storageFactory,
196
+            Server::get(IDBConnection::class),
197
+            Server::get(IRootFolder::class),
198
+            $this->mimeTypeDetector,
199
+            $this->mimeTypeLoader,
200
+            $this->logger,
201
+            Server::get(IGenerator::class),
202
+            Server::get(IAppDataFactory::class)
203
+        );
204
+        $this->invokePrivate($job, 'run', [[]]);
205
+        $previews = iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5));
206
+        $this->assertEquals(9, count($previews));
207
+        $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
208
+
209
+        $nameVersionMapping = [];
210
+        foreach ($previews as $preview) {
211
+            $nameVersionMapping[$preview->getName($this->mimeTypeLoader)] = $preview->getVersion();
212
+        }
213
+
214
+        $this->assertEquals([
215
+            '1000-128-128-crop.png' => 1000,
216
+            '1000-128-128.png' => 1000,
217
+            '1000-256-256-max.png' => 1000,
218
+            '1001-128-128-crop.png' => 1001,
219
+            '1001-128-128.png' => 1001,
220
+            '1001-256-256-max.png' => 1001,
221
+            '128-128-crop.png' => null,
222
+            '128-128.png' => null,
223
+            '256-256-max.png' => null,
224
+        ], $nameVersionMapping);
225
+    }
226 226
 }
Please login to merge, or discard this patch.
tests/lib/Snowflake/GeneratorTest.php 2 patches
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -21,60 +21,60 @@
 block discarded – undo
21 21
  * @package Test
22 22
  */
23 23
 class GeneratorTest extends TestCase {
24
-	private Decoder $decoder;
24
+    private Decoder $decoder;
25 25
 
26
-	public function setUp():void {
27
-		$this->decoder = new Decoder();
28
-	}
29
-	public function testGenerator(): void {
30
-		$generator = new Generator(new TimeFactory());
31
-		$snowflakeId = $generator->nextId();
32
-		$data = $this->decoder->decode($generator->nextId());
26
+    public function setUp():void {
27
+        $this->decoder = new Decoder();
28
+    }
29
+    public function testGenerator(): void {
30
+        $generator = new Generator(new TimeFactory());
31
+        $snowflakeId = $generator->nextId();
32
+        $data = $this->decoder->decode($generator->nextId());
33 33
 
34
-		$this->assertIsString($snowflakeId);
35
-		// Check timestamp
36
-		$this->assertGreaterThan(time() - 30, $data['createdAt']->format('U'));
34
+        $this->assertIsString($snowflakeId);
35
+        // Check timestamp
36
+        $this->assertGreaterThan(time() - 30, $data['createdAt']->format('U'));
37 37
 
38
-		// Check serverId
39
-		$this->assertGreaterThanOrEqual(0, $data['serverId']);
40
-		$this->assertLessThanOrEqual(1023, $data['serverId']);
38
+        // Check serverId
39
+        $this->assertGreaterThanOrEqual(0, $data['serverId']);
40
+        $this->assertLessThanOrEqual(1023, $data['serverId']);
41 41
 
42
-		// Check sequenceId
43
-		$this->assertGreaterThanOrEqual(0, $data['sequenceId']);
44
-		$this->assertLessThanOrEqual(4095, $data['sequenceId']);
42
+        // Check sequenceId
43
+        $this->assertGreaterThanOrEqual(0, $data['sequenceId']);
44
+        $this->assertLessThanOrEqual(4095, $data['sequenceId']);
45 45
 
46
-		// Check CLI
47
-		$this->assertTrue($data['isCli']);
48
-	}
46
+        // Check CLI
47
+        $this->assertTrue($data['isCli']);
48
+    }
49 49
 
50
-	#[DataProvider('provideSnowflakeData')]
51
-	public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
52
-		$dt = new \DateTimeImmutable($date);
53
-		$timeFactory = $this->createMock(ITimeFactory::class);
54
-		$timeFactory->method('now')->willReturn($dt);
50
+    #[DataProvider('provideSnowflakeData')]
51
+    public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
52
+        $dt = new \DateTimeImmutable($date);
53
+        $timeFactory = $this->createMock(ITimeFactory::class);
54
+        $timeFactory->method('now')->willReturn($dt);
55 55
 
56
-		$generator = new Generator($timeFactory);
57
-		$data = $this->decoder->decode($generator->nextId());
56
+        $generator = new Generator($timeFactory);
57
+        $data = $this->decoder->decode($generator->nextId());
58 58
 
59
-		$this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET));
60
-		$this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v'));
61
-	}
59
+        $this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET));
60
+        $this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v'));
61
+    }
62 62
 
63
-	public static function provideSnowflakeData(): array {
64
-		$tests = [
65
-			['2025-10-01 00:00:00.000000', 0, 0],
66
-			['2025-10-01 00:00:01.000000', 1, 0],
67
-			['2025-10-01 00:00:00.001000', 0, 1],
68
-			['2027-08-06 03:08:30.000975', 58244910, 0],
69
-			['2030-06-21 12:59:33.100875', 149000373, 100],
70
-			['2038-01-18 13:33:37.666666', 388157617, 666],
71
-		];
72
-		// Timestamp in 32 bits can't go after 2038. Add few cases for 64 bits.
73
-		if (PHP_INT_SIZE === 8) {
74
-			$tests[] = ['2039-12-31 23:59:59.999999', 449711999, 999];
75
-			$tests[] = ['2086-06-21 12:59:33.010875', 1916225973, 10];
76
-		}
63
+    public static function provideSnowflakeData(): array {
64
+        $tests = [
65
+            ['2025-10-01 00:00:00.000000', 0, 0],
66
+            ['2025-10-01 00:00:01.000000', 1, 0],
67
+            ['2025-10-01 00:00:00.001000', 0, 1],
68
+            ['2027-08-06 03:08:30.000975', 58244910, 0],
69
+            ['2030-06-21 12:59:33.100875', 149000373, 100],
70
+            ['2038-01-18 13:33:37.666666', 388157617, 666],
71
+        ];
72
+        // Timestamp in 32 bits can't go after 2038. Add few cases for 64 bits.
73
+        if (PHP_INT_SIZE === 8) {
74
+            $tests[] = ['2039-12-31 23:59:59.999999', 449711999, 999];
75
+            $tests[] = ['2086-06-21 12:59:33.010875', 1916225973, 10];
76
+        }
77 77
 
78
-		return $tests;
79
-	}
78
+        return $tests;
79
+    }
80 80
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -57,7 +57,7 @@
 block discarded – undo
57 57
 		$data = $this->decoder->decode($generator->nextId());
58 58
 
59 59
 		$this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET));
60
-		$this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v'));
60
+		$this->assertEquals($expectedMilliseconds, (int) $data['createdAt']->format('v'));
61 61
 	}
62 62
 
63 63
 	public static function provideSnowflakeData(): array {
Please login to merge, or discard this patch.
tests/lib/Snowflake/DecoderTest.php 2 patches
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -15,55 +15,55 @@
 block discarded – undo
15 15
  * @package Test
16 16
  */
17 17
 class DecoderTest extends TestCase {
18
-	private Decoder $decoder;
18
+    private Decoder $decoder;
19 19
 
20
-	public function setUp():void {
21
-		$this->decoder = new Decoder();
22
-	}
20
+    public function setUp():void {
21
+        $this->decoder = new Decoder();
22
+    }
23 23
 
24
-	#[DataProvider('provideSnowflakeIds')]
25
-	public function testDecode(
26
-		string $snowflakeId,
27
-		float $timestamp,
28
-		int $serverId,
29
-		int $sequenceId,
30
-		bool $isCli,
31
-	): void {
32
-		$data = $this->decoder->decode($snowflakeId);
24
+    #[DataProvider('provideSnowflakeIds')]
25
+    public function testDecode(
26
+        string $snowflakeId,
27
+        float $timestamp,
28
+        int $serverId,
29
+        int $sequenceId,
30
+        bool $isCli,
31
+    ): void {
32
+        $data = $this->decoder->decode($snowflakeId);
33 33
 
34
-		$this->assertEquals($timestamp, (float)$data['createdAt']->format('U.v'));
35
-		$this->assertEquals($serverId, $data['serverId']);
36
-		$this->assertEquals($sequenceId, $data['sequenceId']);
37
-		$this->assertEquals($isCli, $data['isCli']);
38
-	}
34
+        $this->assertEquals($timestamp, (float)$data['createdAt']->format('U.v'));
35
+        $this->assertEquals($serverId, $data['serverId']);
36
+        $this->assertEquals($sequenceId, $data['sequenceId']);
37
+        $this->assertEquals($isCli, $data['isCli']);
38
+    }
39 39
 
40
-	public static function provideSnowflakeIds(): array {
41
-		$data = [
42
-			['4688076898113587', 1760368327.984, 392, 2099, true],
43
-			// Max milliseconds
44
-			['4190109696', 1759276800.999, 0, 0, false],
45
-			// Max serverId
46
-			['4186112', 1759276800.0, 511, 0, false],
47
-			// Max sequenceId
48
-			['4095', 1759276800.0, 0, 4095, false],
49
-			// Max isCli
50
-			['4096', 1759276800.0, 0, 0, true],
51
-			// Min
52
-			['0', 1759276800, 0, 0, false],
53
-			// Other
54
-			['250159983611680096', 1817521710, 392, 1376, true],
55
-		];
40
+    public static function provideSnowflakeIds(): array {
41
+        $data = [
42
+            ['4688076898113587', 1760368327.984, 392, 2099, true],
43
+            // Max milliseconds
44
+            ['4190109696', 1759276800.999, 0, 0, false],
45
+            // Max serverId
46
+            ['4186112', 1759276800.0, 511, 0, false],
47
+            // Max sequenceId
48
+            ['4095', 1759276800.0, 0, 4095, false],
49
+            // Max isCli
50
+            ['4096', 1759276800.0, 0, 0, true],
51
+            // Min
52
+            ['0', 1759276800, 0, 0, false],
53
+            // Other
54
+            ['250159983611680096', 1817521710, 392, 1376, true],
55
+        ];
56 56
 
57
-		// 32 bits can't handle large timestamps correctly
58
-		if (PHP_INT_SIZE === 8) {
59
-			// Max all (can't happen because ms are up to 999)
60
-			$data[] = ['999999999999', 3906760448.023, 511, 4095, true];
61
-			// Max all (real)
62
-			$data[] = ['9223372036754112511', 3906760447.999, 511, 4095, true];
63
-			// Max seconds
64
-			$data[] = ['9223372032559808512', 3906760447, 0, 0, false];
65
-		}
57
+        // 32 bits can't handle large timestamps correctly
58
+        if (PHP_INT_SIZE === 8) {
59
+            // Max all (can't happen because ms are up to 999)
60
+            $data[] = ['999999999999', 3906760448.023, 511, 4095, true];
61
+            // Max all (real)
62
+            $data[] = ['9223372036754112511', 3906760447.999, 511, 4095, true];
63
+            // Max seconds
64
+            $data[] = ['9223372032559808512', 3906760447, 0, 0, false];
65
+        }
66 66
 
67
-		return $data;
68
-	}
67
+        return $data;
68
+    }
69 69
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,7 @@
 block discarded – undo
31 31
 	): void {
32 32
 		$data = $this->decoder->decode($snowflakeId);
33 33
 
34
-		$this->assertEquals($timestamp, (float)$data['createdAt']->format('U.v'));
34
+		$this->assertEquals($timestamp, (float) $data['createdAt']->format('U.v'));
35 35
 		$this->assertEquals($serverId, $data['serverId']);
36 36
 		$this->assertEquals($sequenceId, $data['sequenceId']);
37 37
 		$this->assertEquals($isCli, $data['isCli']);
Please login to merge, or discard this patch.
core/Migrations/Version33000Date20251023120529.php 2 patches
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -21,66 +21,66 @@
 block discarded – undo
21 21
  */
22 22
 #[AddIndex(table: 'preview_locations', type: IndexType::UNIQUE)]
23 23
 class Version33000Date20251023120529 extends SimpleMigrationStep {
24
-	public function __construct(
25
-		private readonly IDBConnection $connection,
26
-	) {
27
-	}
24
+    public function __construct(
25
+        private readonly IDBConnection $connection,
26
+    ) {
27
+    }
28 28
 
29
-	/**
30
-	 * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
31
-	 */
32
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
-		/** @var ISchemaWrapper $schema */
34
-		$schema = $schemaClosure();
29
+    /**
30
+     * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
31
+     */
32
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
+        /** @var ISchemaWrapper $schema */
34
+        $schema = $schemaClosure();
35 35
 
36
-		if ($schema->hasTable('preview_locations')) {
37
-			$table = $schema->getTable('preview_locations');
38
-			$table->addUniqueIndex(['bucket_name', 'object_store_name'], 'unique_bucket_store');
39
-		}
36
+        if ($schema->hasTable('preview_locations')) {
37
+            $table = $schema->getTable('preview_locations');
38
+            $table->addUniqueIndex(['bucket_name', 'object_store_name'], 'unique_bucket_store');
39
+        }
40 40
 
41
-		return $schema;
42
-	}
41
+        return $schema;
42
+    }
43 43
 
44
-	public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
45
-		// This shouldn't run on a production instance, only daily
46
-		$qb = $this->connection->getQueryBuilder();
47
-		$qb->select('*')
48
-			->from('preview_locations');
49
-		$result = $qb->executeQuery();
44
+    public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
45
+        // This shouldn't run on a production instance, only daily
46
+        $qb = $this->connection->getQueryBuilder();
47
+        $qb->select('*')
48
+            ->from('preview_locations');
49
+        $result = $qb->executeQuery();
50 50
 
51
-		$set = [];
51
+        $set = [];
52 52
 
53
-		while ($row = $result->fetch()) {
54
-			// Iterate over all the rows with duplicated rows
55
-			$id = $row['id'];
53
+        while ($row = $result->fetch()) {
54
+            // Iterate over all the rows with duplicated rows
55
+            $id = $row['id'];
56 56
 
57
-			if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
58
-				// duplicate
59
-				$authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
60
-				$qb = $this->connection->getQueryBuilder();
61
-				$qb->select('id')
62
-					->from('preview_locations')
63
-					->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($row['bucket_name'])))
64
-					->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($row['object_store_name'])))
65
-					->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($authoritativeId)));
57
+            if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
58
+                // duplicate
59
+                $authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
60
+                $qb = $this->connection->getQueryBuilder();
61
+                $qb->select('id')
62
+                    ->from('preview_locations')
63
+                    ->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($row['bucket_name'])))
64
+                    ->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($row['object_store_name'])))
65
+                    ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($authoritativeId)));
66 66
 
67
-				$result = $qb->executeQuery();
68
-				while ($row = $result->fetch()) {
69
-					// Update previews entries to the now de-duplicated id
70
-					$qb = $this->connection->getQueryBuilder();
71
-					$qb->update('previews')
72
-						->set('location_id', $qb->createNamedParameter($id))
73
-						->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
74
-					$qb->executeStatement();
67
+                $result = $qb->executeQuery();
68
+                while ($row = $result->fetch()) {
69
+                    // Update previews entries to the now de-duplicated id
70
+                    $qb = $this->connection->getQueryBuilder();
71
+                    $qb->update('previews')
72
+                        ->set('location_id', $qb->createNamedParameter($id))
73
+                        ->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
74
+                    $qb->executeStatement();
75 75
 
76
-					$qb = $this->connection->getQueryBuilder();
77
-					$qb->delete('preview_locations')
78
-						->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
79
-					$qb->executeStatement();
80
-				}
81
-				break;
82
-			}
83
-			$set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
84
-		}
85
-	}
76
+                    $qb = $this->connection->getQueryBuilder();
77
+                    $qb->delete('preview_locations')
78
+                        ->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
79
+                    $qb->executeStatement();
80
+                }
81
+                break;
82
+            }
83
+            $set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
84
+        }
85
+    }
86 86
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -54,9 +54,9 @@  discard block
 block discarded – undo
54 54
 			// Iterate over all the rows with duplicated rows
55 55
 			$id = $row['id'];
56 56
 
57
-			if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
57
+			if (isset($set[$row['bucket_name'].'_'.$row['object_store_name']])) {
58 58
 				// duplicate
59
-				$authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
59
+				$authoritativeId = $set[$row['bucket_name'].'_'.$row['object_store_name']];
60 60
 				$qb = $this->connection->getQueryBuilder();
61 61
 				$qb->select('id')
62 62
 					->from('preview_locations')
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
 				}
81 81
 				break;
82 82
 			}
83
-			$set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
83
+			$set[$row['bucket_name'].'_'.$row['object_store_name']] = $row['id'];
84 84
 		}
85 85
 	}
86 86
 }
Please login to merge, or discard this patch.
core/Migrations/Version33000Date20251023110529.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -21,24 +21,24 @@
 block discarded – undo
21 21
 #[ModifyColumn(table: 'previews', name: 'id', description: 'Remove auto-increment')]
22 22
 #[ModifyColumn(table: 'preview_versions', name: 'id', description: 'Remove auto-increment')]
23 23
 class Version33000Date20251023110529 extends SimpleMigrationStep {
24
-	/**
25
-	 * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
26
-	 */
27
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
28
-		$schema = $schemaClosure();
29
-
30
-		if ($schema->hasTable('preview_locations')) {
31
-			$schema->dropAutoincrementColumn('preview_locations', 'id');
32
-		}
33
-
34
-		if ($schema->hasTable('preview_versions')) {
35
-			$schema->dropAutoincrementColumn('preview_versions', 'id');
36
-		}
37
-
38
-		if ($schema->hasTable('previews')) {
39
-			$schema->dropAutoincrementColumn('previews', 'id');
40
-		}
41
-
42
-		return $schema;
43
-	}
24
+    /**
25
+     * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
26
+     */
27
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
28
+        $schema = $schemaClosure();
29
+
30
+        if ($schema->hasTable('preview_locations')) {
31
+            $schema->dropAutoincrementColumn('preview_locations', 'id');
32
+        }
33
+
34
+        if ($schema->hasTable('preview_versions')) {
35
+            $schema->dropAutoincrementColumn('preview_versions', 'id');
36
+        }
37
+
38
+        if ($schema->hasTable('previews')) {
39
+            $schema->dropAutoincrementColumn('previews', 'id');
40
+        }
41
+
42
+        return $schema;
43
+    }
44 44
 }
Please login to merge, or discard this patch.
core/register_command.php 1 patch
Indentation   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -124,143 +124,143 @@
 block discarded – undo
124 124
 $config = Server::get(IConfig::class);
125 125
 
126 126
 if ($config->getSystemValueBool('installed', false)) {
127
-	$application->add(Server::get(Disable::class));
128
-	$application->add(Server::get(Enable::class));
129
-	$application->add(Server::get(Install::class));
130
-	$application->add(Server::get(GetPath::class));
131
-	$application->add(Server::get(ListApps::class));
132
-	$application->add(Server::get(Remove::class));
133
-	$application->add(Server::get(Update::class));
127
+    $application->add(Server::get(Disable::class));
128
+    $application->add(Server::get(Enable::class));
129
+    $application->add(Server::get(Install::class));
130
+    $application->add(Server::get(GetPath::class));
131
+    $application->add(Server::get(ListApps::class));
132
+    $application->add(Server::get(Remove::class));
133
+    $application->add(Server::get(Update::class));
134 134
 
135
-	$application->add(Server::get(Cleanup::class));
136
-	$application->add(Server::get(Enforce::class));
137
-	$application->add(Server::get(Command\TwoFactorAuth\Enable::class));
138
-	$application->add(Server::get(Command\TwoFactorAuth\Disable::class));
139
-	$application->add(Server::get(State::class));
135
+    $application->add(Server::get(Cleanup::class));
136
+    $application->add(Server::get(Enforce::class));
137
+    $application->add(Server::get(Command\TwoFactorAuth\Enable::class));
138
+    $application->add(Server::get(Command\TwoFactorAuth\Disable::class));
139
+    $application->add(Server::get(State::class));
140 140
 
141
-	$application->add(Server::get(Mode::class));
142
-	$application->add(Server::get(Job::class));
143
-	$application->add(Server::get(ListCommand::class));
144
-	$application->add(Server::get(Delete::class));
145
-	$application->add(Server::get(JobWorker::class));
141
+    $application->add(Server::get(Mode::class));
142
+    $application->add(Server::get(Job::class));
143
+    $application->add(Server::get(ListCommand::class));
144
+    $application->add(Server::get(Delete::class));
145
+    $application->add(Server::get(JobWorker::class));
146 146
 
147
-	$application->add(Server::get(Test::class));
147
+    $application->add(Server::get(Test::class));
148 148
 
149
-	$application->add(Server::get(DeleteConfig::class));
150
-	$application->add(Server::get(GetConfig::class));
151
-	$application->add(Server::get(SetConfig::class));
152
-	$application->add(Server::get(Import::class));
153
-	$application->add(Server::get(ListConfigs::class));
154
-	$application->add(Server::get(Preset::class));
155
-	$application->add(Server::get(Command\Config\System\DeleteConfig::class));
156
-	$application->add(Server::get(Command\Config\System\GetConfig::class));
157
-	$application->add(Server::get(Command\Config\System\SetConfig::class));
149
+    $application->add(Server::get(DeleteConfig::class));
150
+    $application->add(Server::get(GetConfig::class));
151
+    $application->add(Server::get(SetConfig::class));
152
+    $application->add(Server::get(Import::class));
153
+    $application->add(Server::get(ListConfigs::class));
154
+    $application->add(Server::get(Preset::class));
155
+    $application->add(Server::get(Command\Config\System\DeleteConfig::class));
156
+    $application->add(Server::get(Command\Config\System\GetConfig::class));
157
+    $application->add(Server::get(Command\Config\System\SetConfig::class));
158 158
 
159
-	$application->add(Server::get(File::class));
160
-	$application->add(Server::get(Space::class));
161
-	$application->add(Server::get(Storage::class));
162
-	$application->add(Server::get(Storages::class));
159
+    $application->add(Server::get(File::class));
160
+    $application->add(Server::get(Space::class));
161
+    $application->add(Server::get(Storage::class));
162
+    $application->add(Server::get(Storages::class));
163 163
 
164
-	$application->add(Server::get(ConvertType::class));
165
-	$application->add(Server::get(ConvertMysqlToMB4::class));
166
-	$application->add(Server::get(ConvertFilecacheBigInt::class));
167
-	$application->add(Server::get(AddMissingColumns::class));
168
-	$application->add(Server::get(AddMissingIndices::class));
169
-	$application->add(Server::get(AddMissingPrimaryKeys::class));
170
-	$application->add(Server::get(ExpectedSchema::class));
171
-	$application->add(Server::get(ExportSchema::class));
164
+    $application->add(Server::get(ConvertType::class));
165
+    $application->add(Server::get(ConvertMysqlToMB4::class));
166
+    $application->add(Server::get(ConvertFilecacheBigInt::class));
167
+    $application->add(Server::get(AddMissingColumns::class));
168
+    $application->add(Server::get(AddMissingIndices::class));
169
+    $application->add(Server::get(AddMissingPrimaryKeys::class));
170
+    $application->add(Server::get(ExpectedSchema::class));
171
+    $application->add(Server::get(ExportSchema::class));
172 172
 
173
-	$application->add(Server::get(GenerateMetadataCommand::class));
174
-	$application->add(Server::get(PreviewCommand::class));
175
-	if ($config->getSystemValueBool('debug', false)) {
176
-		$application->add(Server::get(StatusCommand::class));
177
-		$application->add(Server::get(MigrateCommand::class));
178
-		$application->add(Server::get(GenerateCommand::class));
179
-		$application->add(Server::get(ExecuteCommand::class));
180
-	}
173
+    $application->add(Server::get(GenerateMetadataCommand::class));
174
+    $application->add(Server::get(PreviewCommand::class));
175
+    if ($config->getSystemValueBool('debug', false)) {
176
+        $application->add(Server::get(StatusCommand::class));
177
+        $application->add(Server::get(MigrateCommand::class));
178
+        $application->add(Server::get(GenerateCommand::class));
179
+        $application->add(Server::get(ExecuteCommand::class));
180
+    }
181 181
 
182
-	$application->add(Server::get(Command\Encryption\Disable::class));
183
-	$application->add(Server::get(Command\Encryption\Enable::class));
184
-	$application->add(Server::get(ListModules::class));
185
-	$application->add(Server::get(SetDefaultModule::class));
186
-	$application->add(Server::get(Command\Encryption\Status::class));
187
-	$application->add(Server::get(EncryptAll::class));
188
-	$application->add(Server::get(DecryptAll::class));
182
+    $application->add(Server::get(Command\Encryption\Disable::class));
183
+    $application->add(Server::get(Command\Encryption\Enable::class));
184
+    $application->add(Server::get(ListModules::class));
185
+    $application->add(Server::get(SetDefaultModule::class));
186
+    $application->add(Server::get(Command\Encryption\Status::class));
187
+    $application->add(Server::get(EncryptAll::class));
188
+    $application->add(Server::get(DecryptAll::class));
189 189
 
190
-	$application->add(Server::get(Manage::class));
191
-	$application->add(Server::get(Command\Log\File::class));
190
+    $application->add(Server::get(Manage::class));
191
+    $application->add(Server::get(Command\Log\File::class));
192 192
 
193
-	$application->add(Server::get(ChangeKeyStorageRoot::class));
194
-	$application->add(Server::get(ShowKeyStorageRoot::class));
195
-	$application->add(Server::get(MigrateKeyStorage::class));
193
+    $application->add(Server::get(ChangeKeyStorageRoot::class));
194
+    $application->add(Server::get(ShowKeyStorageRoot::class));
195
+    $application->add(Server::get(MigrateKeyStorage::class));
196 196
 
197
-	$application->add(Server::get(DataFingerprint::class));
198
-	$application->add(Server::get(UpdateDB::class));
199
-	$application->add(Server::get(UpdateJS::class));
200
-	$application->add(Server::get(Command\Maintenance\Mode::class));
201
-	$application->add(Server::get(UpdateHtaccess::class));
202
-	$application->add(Server::get(UpdateTheme::class));
197
+    $application->add(Server::get(DataFingerprint::class));
198
+    $application->add(Server::get(UpdateDB::class));
199
+    $application->add(Server::get(UpdateJS::class));
200
+    $application->add(Server::get(Command\Maintenance\Mode::class));
201
+    $application->add(Server::get(UpdateHtaccess::class));
202
+    $application->add(Server::get(UpdateTheme::class));
203 203
 
204
-	$application->add(Server::get(Upgrade::class));
205
-	$application->add(Server::get(Repair::class));
206
-	$application->add(Server::get(RepairShareOwnership::class));
204
+    $application->add(Server::get(Upgrade::class));
205
+    $application->add(Server::get(Repair::class));
206
+    $application->add(Server::get(RepairShareOwnership::class));
207 207
 
208
-	$application->add(Server::get(Command\Preview\Cleanup::class));
209
-	$application->add(Server::get(Generate::class));
210
-	$application->add(Server::get(ResetRenderedTexts::class));
208
+    $application->add(Server::get(Command\Preview\Cleanup::class));
209
+    $application->add(Server::get(Generate::class));
210
+    $application->add(Server::get(ResetRenderedTexts::class));
211 211
 
212
-	$application->add(Server::get(Add::class));
213
-	$application->add(Server::get(Command\User\Delete::class));
214
-	$application->add(Server::get(Command\User\Disable::class));
215
-	$application->add(Server::get(Command\User\Enable::class));
216
-	$application->add(Server::get(LastSeen::class));
217
-	$application->add(Server::get(Report::class));
218
-	$application->add(Server::get(ResetPassword::class));
219
-	$application->add(Server::get(Setting::class));
220
-	$application->add(Server::get(Profile::class));
221
-	$application->add(Server::get(Command\User\ListCommand::class));
222
-	$application->add(Server::get(ClearGeneratedAvatarCacheCommand::class));
223
-	$application->add(Server::get(Info::class));
224
-	$application->add(Server::get(SyncAccountDataCommand::class));
225
-	$application->add(Server::get(Command\User\AuthTokens\Add::class));
226
-	$application->add(Server::get(Command\User\AuthTokens\ListCommand::class));
227
-	$application->add(Server::get(Command\User\AuthTokens\Delete::class));
228
-	$application->add(Server::get(Verify::class));
229
-	$application->add(Server::get(Welcome::class));
212
+    $application->add(Server::get(Add::class));
213
+    $application->add(Server::get(Command\User\Delete::class));
214
+    $application->add(Server::get(Command\User\Disable::class));
215
+    $application->add(Server::get(Command\User\Enable::class));
216
+    $application->add(Server::get(LastSeen::class));
217
+    $application->add(Server::get(Report::class));
218
+    $application->add(Server::get(ResetPassword::class));
219
+    $application->add(Server::get(Setting::class));
220
+    $application->add(Server::get(Profile::class));
221
+    $application->add(Server::get(Command\User\ListCommand::class));
222
+    $application->add(Server::get(ClearGeneratedAvatarCacheCommand::class));
223
+    $application->add(Server::get(Info::class));
224
+    $application->add(Server::get(SyncAccountDataCommand::class));
225
+    $application->add(Server::get(Command\User\AuthTokens\Add::class));
226
+    $application->add(Server::get(Command\User\AuthTokens\ListCommand::class));
227
+    $application->add(Server::get(Command\User\AuthTokens\Delete::class));
228
+    $application->add(Server::get(Verify::class));
229
+    $application->add(Server::get(Welcome::class));
230 230
 
231
-	$application->add(Server::get(Command\Group\Add::class));
232
-	$application->add(Server::get(Command\Group\Delete::class));
233
-	$application->add(Server::get(Command\Group\ListCommand::class));
234
-	$application->add(Server::get(AddUser::class));
235
-	$application->add(Server::get(RemoveUser::class));
236
-	$application->add(Server::get(Command\Group\Info::class));
231
+    $application->add(Server::get(Command\Group\Add::class));
232
+    $application->add(Server::get(Command\Group\Delete::class));
233
+    $application->add(Server::get(Command\Group\ListCommand::class));
234
+    $application->add(Server::get(AddUser::class));
235
+    $application->add(Server::get(RemoveUser::class));
236
+    $application->add(Server::get(Command\Group\Info::class));
237 237
 
238
-	$application->add(Server::get(Command\SystemTag\ListCommand::class));
239
-	$application->add(Server::get(Command\SystemTag\Delete::class));
240
-	$application->add(Server::get(Command\SystemTag\Add::class));
241
-	$application->add(Server::get(Edit::class));
238
+    $application->add(Server::get(Command\SystemTag\ListCommand::class));
239
+    $application->add(Server::get(Command\SystemTag\Delete::class));
240
+    $application->add(Server::get(Command\SystemTag\Add::class));
241
+    $application->add(Server::get(Edit::class));
242 242
 
243
-	$application->add(Server::get(ListCertificates::class));
244
-	$application->add(Server::get(ExportCertificates::class));
245
-	$application->add(Server::get(ImportCertificate::class));
246
-	$application->add(Server::get(RemoveCertificate::class));
247
-	$application->add(Server::get(BruteforceAttempts::class));
248
-	$application->add(Server::get(BruteforceResetAttempts::class));
249
-	$application->add(Server::get(SetupChecks::class));
250
-	$application->add(Server::get(SnowflakeDecodeId::class));
251
-	$application->add(Server::get(Get::class));
243
+    $application->add(Server::get(ListCertificates::class));
244
+    $application->add(Server::get(ExportCertificates::class));
245
+    $application->add(Server::get(ImportCertificate::class));
246
+    $application->add(Server::get(RemoveCertificate::class));
247
+    $application->add(Server::get(BruteforceAttempts::class));
248
+    $application->add(Server::get(BruteforceResetAttempts::class));
249
+    $application->add(Server::get(SetupChecks::class));
250
+    $application->add(Server::get(SnowflakeDecodeId::class));
251
+    $application->add(Server::get(Get::class));
252 252
 
253
-	$application->add(Server::get(GetCommand::class));
254
-	$application->add(Server::get(EnabledCommand::class));
255
-	$application->add(Server::get(Command\TaskProcessing\ListCommand::class));
256
-	$application->add(Server::get(Statistics::class));
257
-	$application->add(Server::get(Command\TaskProcessing\Cleanup::class));
253
+    $application->add(Server::get(GetCommand::class));
254
+    $application->add(Server::get(EnabledCommand::class));
255
+    $application->add(Server::get(Command\TaskProcessing\ListCommand::class));
256
+    $application->add(Server::get(Statistics::class));
257
+    $application->add(Server::get(Command\TaskProcessing\Cleanup::class));
258 258
 
259
-	$application->add(Server::get(RedisCommand::class));
260
-	$application->add(Server::get(DistributedClear::class));
261
-	$application->add(Server::get(DistributedDelete::class));
262
-	$application->add(Server::get(DistributedGet::class));
263
-	$application->add(Server::get(DistributedSet::class));
259
+    $application->add(Server::get(RedisCommand::class));
260
+    $application->add(Server::get(DistributedClear::class));
261
+    $application->add(Server::get(DistributedDelete::class));
262
+    $application->add(Server::get(DistributedGet::class));
263
+    $application->add(Server::get(DistributedSet::class));
264 264
 } else {
265
-	$application->add(Server::get(Command\Maintenance\Install::class));
265
+    $application->add(Server::get(Command\Maintenance\Install::class));
266 266
 }
Please login to merge, or discard this patch.
core/Command/SnowflakeDecodeId.php 1 patch
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -15,34 +15,34 @@
 block discarded – undo
15 15
 use Symfony\Component\Console\Output\OutputInterface;
16 16
 
17 17
 class SnowflakeDecodeId extends Base {
18
-	protected function configure(): void {
19
-		parent::configure();
20
-
21
-		$this
22
-			->setName('decode-snowflake')
23
-			->setDescription('Decode Snowflake IDs used by Nextcloud')
24
-			->addArgument('snowflake-id', InputArgument::REQUIRED, 'Nextcloud Snowflake ID to decode');
25
-	}
26
-
27
-	protected function execute(InputInterface $input, OutputInterface $output): int {
28
-		$snowflakeId = $input->getArgument('snowflake-id');
29
-		$data = (new Decoder)->decode($snowflakeId);
30
-
31
-		$rows = [
32
-			['Snowflake ID', $snowflakeId],
33
-			['Seconds', $data['seconds']],
34
-			['Milliseconds', $data['milliseconds']],
35
-			['Created from CLI', $data['isCli'] ? 'yes' : 'no'],
36
-			['Server ID', $data['serverId']],
37
-			['Sequence ID', $data['sequenceId']],
38
-			['Creation timestamp', $data['createdAt']->format('U.v')],
39
-			['Creation date', $data['createdAt']->format('Y-m-d H:i:s.v')],
40
-		];
41
-
42
-		$table = new Table($output);
43
-		$table->setRows($rows);
44
-		$table->render();
45
-
46
-		return Base::SUCCESS;
47
-	}
18
+    protected function configure(): void {
19
+        parent::configure();
20
+
21
+        $this
22
+            ->setName('decode-snowflake')
23
+            ->setDescription('Decode Snowflake IDs used by Nextcloud')
24
+            ->addArgument('snowflake-id', InputArgument::REQUIRED, 'Nextcloud Snowflake ID to decode');
25
+    }
26
+
27
+    protected function execute(InputInterface $input, OutputInterface $output): int {
28
+        $snowflakeId = $input->getArgument('snowflake-id');
29
+        $data = (new Decoder)->decode($snowflakeId);
30
+
31
+        $rows = [
32
+            ['Snowflake ID', $snowflakeId],
33
+            ['Seconds', $data['seconds']],
34
+            ['Milliseconds', $data['milliseconds']],
35
+            ['Created from CLI', $data['isCli'] ? 'yes' : 'no'],
36
+            ['Server ID', $data['serverId']],
37
+            ['Sequence ID', $data['sequenceId']],
38
+            ['Creation timestamp', $data['createdAt']->format('U.v')],
39
+            ['Creation date', $data['createdAt']->format('Y-m-d H:i:s.v')],
40
+        ];
41
+
42
+        $table = new Table($output);
43
+        $table->setRows($rows);
44
+        $table->render();
45
+
46
+        return Base::SUCCESS;
47
+    }
48 48
 }
Please login to merge, or discard this patch.
core/BackgroundJobs/MovePreviewJob.php 2 patches
Indentation   +212 added lines, -212 removed lines patch added patch discarded remove patch
@@ -31,216 +31,216 @@
 block discarded – undo
31 31
 use Psr\Log\LoggerInterface;
32 32
 
33 33
 class MovePreviewJob extends TimedJob {
34
-	private IAppData $appData;
35
-	private string $previewRootPath;
36
-
37
-	public function __construct(
38
-		ITimeFactory $time,
39
-		private readonly IAppConfig $appConfig,
40
-		private readonly IConfig $config,
41
-		private readonly PreviewMapper $previewMapper,
42
-		private readonly StorageFactory $storageFactory,
43
-		private readonly IDBConnection $connection,
44
-		private readonly IRootFolder $rootFolder,
45
-		private readonly IMimeTypeDetector $mimeTypeDetector,
46
-		private readonly IMimeTypeLoader $mimeTypeLoader,
47
-		private readonly LoggerInterface $logger,
48
-		private readonly IGenerator $generator,
49
-		IAppDataFactory $appDataFactory,
50
-	) {
51
-		parent::__construct($time);
52
-
53
-		$this->appData = $appDataFactory->get('preview');
54
-		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
55
-		$this->setInterval(24 * 60 * 60);
56
-		$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
57
-	}
58
-
59
-	#[Override]
60
-	protected function run(mixed $argument): void {
61
-		if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
62
-			return;
63
-		}
64
-
65
-		$startTime = time();
66
-		while (true) {
67
-			$qb = $this->connection->getQueryBuilder();
68
-			$qb->select('path')
69
-				->from('filecache')
70
-				// Hierarchical preview folder structure
71
-				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
72
-				// Legacy flat preview folder structure
73
-				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
74
-				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75
-				->setMaxResults(100);
76
-
77
-			$result = $qb->executeQuery();
78
-			$foundPreviews = $this->processQueryResult($result);
79
-
80
-			if (!$foundPreviews) {
81
-				break;
82
-			}
83
-
84
-			// Stop if execution time is more than one hour.
85
-			if (time() - $startTime > 3600) {
86
-				return;
87
-			}
88
-		}
89
-
90
-		$this->appConfig->setValueBool('core', 'previewMovedDone', true);
91
-	}
92
-
93
-	private function processQueryResult(IResult $result): bool {
94
-		$foundPreview = false;
95
-		$fileIds = [];
96
-		$flatFileIds = [];
97
-		while ($row = $result->fetch()) {
98
-			$pathSplit = explode('/', $row['path']);
99
-			assert(count($pathSplit) >= 2);
100
-			$fileId = (int)$pathSplit[count($pathSplit) - 2];
101
-			if (count($pathSplit) === 11) {
102
-				// Hierarchical structure
103
-				if (!in_array($fileId, $fileIds)) {
104
-					$fileIds[] = $fileId;
105
-				}
106
-			} else {
107
-				// Flat structure
108
-				if (!in_array($fileId, $flatFileIds)) {
109
-					$flatFileIds[] = $fileId;
110
-				}
111
-			}
112
-			$foundPreview = true;
113
-		}
114
-
115
-		foreach ($fileIds as $fileId) {
116
-			$this->processPreviews($fileId, flatPath: false);
117
-		}
118
-
119
-		foreach ($flatFileIds as $fileId) {
120
-			$this->processPreviews($fileId, flatPath: true);
121
-		}
122
-		return $foundPreview;
123
-	}
124
-
125
-	/**
126
-	 * @param array<string|int, string[]> $previewFolders
127
-	 */
128
-	private function processPreviews(int $fileId, bool $flatPath): void {
129
-		$internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
130
-		$folder = $this->appData->getFolder($internalPath);
131
-
132
-		/**
133
-		 * @var list<array{file: SimpleFile, preview: Preview}> $previewFiles
134
-		 */
135
-		$previewFiles = [];
136
-
137
-		foreach ($folder->getDirectoryListing() as $previewFile) {
138
-			$path = $fileId . '/' . $previewFile->getName();
139
-			/** @var SimpleFile $previewFile */
140
-			$preview = Preview::fromPath($path, $this->mimeTypeDetector);
141
-			$preview->setId($this->generator->nextId());
142
-			if (!$preview) {
143
-				$this->logger->error('Unable to import old preview at path.');
144
-				continue;
145
-			}
146
-			$preview->setSize($previewFile->getSize());
147
-			$preview->setMtime($previewFile->getMtime());
148
-			$preview->setOldFileId($previewFile->getId());
149
-			$preview->setEncrypted(false);
150
-
151
-			$previewFiles[] = [
152
-				'file' => $previewFile,
153
-				'preview' => $preview,
154
-			];
155
-		}
156
-
157
-		$qb = $this->connection->getQueryBuilder();
158
-		$qb->select('storage', 'etag', 'mimetype')
159
-			->from('filecache')
160
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId)))
161
-			->setMaxResults(1);
162
-
163
-		$result = $qb->executeQuery();
164
-		$result = $result->fetchAll();
165
-
166
-		if (count($result) > 0) {
167
-			foreach ($previewFiles as $previewFile) {
168
-				/** @var Preview $preview */
169
-				$preview = $previewFile['preview'];
170
-				/** @var SimpleFile $file */
171
-				$file = $previewFile['file'];
172
-				$preview->setStorageId($result[0]['storage']);
173
-				$preview->setEtag($result[0]['etag']);
174
-				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
175
-				try {
176
-					$preview = $this->previewMapper->insert($preview);
177
-				} catch (Exception) {
178
-					// We already have this preview in the preview table, skip
179
-					$qb->delete('filecache')
180
-						->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
181
-						->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
182
-						->executeStatement();
183
-					continue;
184
-				}
185
-
186
-				try {
187
-					$this->storageFactory->migratePreview($preview, $file);
188
-					$qb = $this->connection->getQueryBuilder();
189
-					$qb->delete('filecache')
190
-						->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
191
-						->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
192
-						->executeStatement();
193
-					// Do not call $file->delete() as this will also delete the file from the file system
194
-				} catch (\Exception $e) {
195
-					$this->previewMapper->delete($preview);
196
-					throw $e;
197
-				}
198
-			}
199
-		} else {
200
-			// No matching fileId, delete preview
201
-			try {
202
-				$this->connection->beginTransaction();
203
-				foreach ($previewFiles as $previewFile) {
204
-					/** @var SimpleFile $file */
205
-					$file = $previewFile['file'];
206
-					$file->delete();
207
-				}
208
-				$this->connection->commit();
209
-			} catch (Exception) {
210
-				$this->connection->rollback();
211
-			}
212
-		}
213
-
214
-		$this->deleteFolder($internalPath);
215
-	}
216
-
217
-	public static function getInternalFolder(string $name, bool $flatPath): string {
218
-		if ($flatPath) {
219
-			return $name;
220
-		}
221
-		return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
222
-	}
223
-
224
-	private function deleteFolder(string $path): void {
225
-		$current = $path;
226
-
227
-		while (true) {
228
-			$appDataPath = $this->previewRootPath . $current;
229
-			$qb = $this->connection->getQueryBuilder();
230
-			$qb->delete('filecache')
231
-				->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
232
-				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
233
-				->executeStatement();
234
-
235
-			$current = dirname($current);
236
-			if ($current === '/' || $current === '.' || $current === '') {
237
-				break;
238
-			}
239
-
240
-			$folder = $this->appData->getFolder($current);
241
-			if (count($folder->getDirectoryListing()) !== 0) {
242
-				break;
243
-			}
244
-		}
245
-	}
34
+    private IAppData $appData;
35
+    private string $previewRootPath;
36
+
37
+    public function __construct(
38
+        ITimeFactory $time,
39
+        private readonly IAppConfig $appConfig,
40
+        private readonly IConfig $config,
41
+        private readonly PreviewMapper $previewMapper,
42
+        private readonly StorageFactory $storageFactory,
43
+        private readonly IDBConnection $connection,
44
+        private readonly IRootFolder $rootFolder,
45
+        private readonly IMimeTypeDetector $mimeTypeDetector,
46
+        private readonly IMimeTypeLoader $mimeTypeLoader,
47
+        private readonly LoggerInterface $logger,
48
+        private readonly IGenerator $generator,
49
+        IAppDataFactory $appDataFactory,
50
+    ) {
51
+        parent::__construct($time);
52
+
53
+        $this->appData = $appDataFactory->get('preview');
54
+        $this->setTimeSensitivity(self::TIME_INSENSITIVE);
55
+        $this->setInterval(24 * 60 * 60);
56
+        $this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
57
+    }
58
+
59
+    #[Override]
60
+    protected function run(mixed $argument): void {
61
+        if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
62
+            return;
63
+        }
64
+
65
+        $startTime = time();
66
+        while (true) {
67
+            $qb = $this->connection->getQueryBuilder();
68
+            $qb->select('path')
69
+                ->from('filecache')
70
+                // Hierarchical preview folder structure
71
+                ->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
72
+                // Legacy flat preview folder structure
73
+                ->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
74
+                ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75
+                ->setMaxResults(100);
76
+
77
+            $result = $qb->executeQuery();
78
+            $foundPreviews = $this->processQueryResult($result);
79
+
80
+            if (!$foundPreviews) {
81
+                break;
82
+            }
83
+
84
+            // Stop if execution time is more than one hour.
85
+            if (time() - $startTime > 3600) {
86
+                return;
87
+            }
88
+        }
89
+
90
+        $this->appConfig->setValueBool('core', 'previewMovedDone', true);
91
+    }
92
+
93
+    private function processQueryResult(IResult $result): bool {
94
+        $foundPreview = false;
95
+        $fileIds = [];
96
+        $flatFileIds = [];
97
+        while ($row = $result->fetch()) {
98
+            $pathSplit = explode('/', $row['path']);
99
+            assert(count($pathSplit) >= 2);
100
+            $fileId = (int)$pathSplit[count($pathSplit) - 2];
101
+            if (count($pathSplit) === 11) {
102
+                // Hierarchical structure
103
+                if (!in_array($fileId, $fileIds)) {
104
+                    $fileIds[] = $fileId;
105
+                }
106
+            } else {
107
+                // Flat structure
108
+                if (!in_array($fileId, $flatFileIds)) {
109
+                    $flatFileIds[] = $fileId;
110
+                }
111
+            }
112
+            $foundPreview = true;
113
+        }
114
+
115
+        foreach ($fileIds as $fileId) {
116
+            $this->processPreviews($fileId, flatPath: false);
117
+        }
118
+
119
+        foreach ($flatFileIds as $fileId) {
120
+            $this->processPreviews($fileId, flatPath: true);
121
+        }
122
+        return $foundPreview;
123
+    }
124
+
125
+    /**
126
+     * @param array<string|int, string[]> $previewFolders
127
+     */
128
+    private function processPreviews(int $fileId, bool $flatPath): void {
129
+        $internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
130
+        $folder = $this->appData->getFolder($internalPath);
131
+
132
+        /**
133
+         * @var list<array{file: SimpleFile, preview: Preview}> $previewFiles
134
+         */
135
+        $previewFiles = [];
136
+
137
+        foreach ($folder->getDirectoryListing() as $previewFile) {
138
+            $path = $fileId . '/' . $previewFile->getName();
139
+            /** @var SimpleFile $previewFile */
140
+            $preview = Preview::fromPath($path, $this->mimeTypeDetector);
141
+            $preview->setId($this->generator->nextId());
142
+            if (!$preview) {
143
+                $this->logger->error('Unable to import old preview at path.');
144
+                continue;
145
+            }
146
+            $preview->setSize($previewFile->getSize());
147
+            $preview->setMtime($previewFile->getMtime());
148
+            $preview->setOldFileId($previewFile->getId());
149
+            $preview->setEncrypted(false);
150
+
151
+            $previewFiles[] = [
152
+                'file' => $previewFile,
153
+                'preview' => $preview,
154
+            ];
155
+        }
156
+
157
+        $qb = $this->connection->getQueryBuilder();
158
+        $qb->select('storage', 'etag', 'mimetype')
159
+            ->from('filecache')
160
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId)))
161
+            ->setMaxResults(1);
162
+
163
+        $result = $qb->executeQuery();
164
+        $result = $result->fetchAll();
165
+
166
+        if (count($result) > 0) {
167
+            foreach ($previewFiles as $previewFile) {
168
+                /** @var Preview $preview */
169
+                $preview = $previewFile['preview'];
170
+                /** @var SimpleFile $file */
171
+                $file = $previewFile['file'];
172
+                $preview->setStorageId($result[0]['storage']);
173
+                $preview->setEtag($result[0]['etag']);
174
+                $preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
175
+                try {
176
+                    $preview = $this->previewMapper->insert($preview);
177
+                } catch (Exception) {
178
+                    // We already have this preview in the preview table, skip
179
+                    $qb->delete('filecache')
180
+                        ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
181
+                        ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
182
+                        ->executeStatement();
183
+                    continue;
184
+                }
185
+
186
+                try {
187
+                    $this->storageFactory->migratePreview($preview, $file);
188
+                    $qb = $this->connection->getQueryBuilder();
189
+                    $qb->delete('filecache')
190
+                        ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
191
+                        ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
192
+                        ->executeStatement();
193
+                    // Do not call $file->delete() as this will also delete the file from the file system
194
+                } catch (\Exception $e) {
195
+                    $this->previewMapper->delete($preview);
196
+                    throw $e;
197
+                }
198
+            }
199
+        } else {
200
+            // No matching fileId, delete preview
201
+            try {
202
+                $this->connection->beginTransaction();
203
+                foreach ($previewFiles as $previewFile) {
204
+                    /** @var SimpleFile $file */
205
+                    $file = $previewFile['file'];
206
+                    $file->delete();
207
+                }
208
+                $this->connection->commit();
209
+            } catch (Exception) {
210
+                $this->connection->rollback();
211
+            }
212
+        }
213
+
214
+        $this->deleteFolder($internalPath);
215
+    }
216
+
217
+    public static function getInternalFolder(string $name, bool $flatPath): string {
218
+        if ($flatPath) {
219
+            return $name;
220
+        }
221
+        return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
222
+    }
223
+
224
+    private function deleteFolder(string $path): void {
225
+        $current = $path;
226
+
227
+        while (true) {
228
+            $appDataPath = $this->previewRootPath . $current;
229
+            $qb = $this->connection->getQueryBuilder();
230
+            $qb->delete('filecache')
231
+                ->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
232
+                ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
233
+                ->executeStatement();
234
+
235
+            $current = dirname($current);
236
+            if ($current === '/' || $current === '.' || $current === '') {
237
+                break;
238
+            }
239
+
240
+            $folder = $this->appData->getFolder($current);
241
+            if (count($folder->getDirectoryListing()) !== 0) {
242
+                break;
243
+            }
244
+        }
245
+    }
246 246
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -53,7 +53,7 @@  discard block
 block discarded – undo
53 53
 		$this->appData = $appDataFactory->get('preview');
54 54
 		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
55 55
 		$this->setInterval(24 * 60 * 60);
56
-		$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
56
+		$this->previewRootPath = 'appdata_'.$this->config->getSystemValueString('instanceid').'/preview/';
57 57
 	}
58 58
 
59 59
 	#[Override]
@@ -68,9 +68,9 @@  discard block
 block discarded – undo
68 68
 			$qb->select('path')
69 69
 				->from('filecache')
70 70
 				// Hierarchical preview folder structure
71
-				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
71
+				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath.'%/%/%/%/%/%/%/%/%')))
72 72
 				// Legacy flat preview folder structure
73
-				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
73
+				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath.'%/%.%')))
74 74
 				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75 75
 				->setMaxResults(100);
76 76
 
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 		while ($row = $result->fetch()) {
98 98
 			$pathSplit = explode('/', $row['path']);
99 99
 			assert(count($pathSplit) >= 2);
100
-			$fileId = (int)$pathSplit[count($pathSplit) - 2];
100
+			$fileId = (int) $pathSplit[count($pathSplit) - 2];
101 101
 			if (count($pathSplit) === 11) {
102 102
 				// Hierarchical structure
103 103
 				if (!in_array($fileId, $fileIds)) {
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 	 * @param array<string|int, string[]> $previewFolders
127 127
 	 */
128 128
 	private function processPreviews(int $fileId, bool $flatPath): void {
129
-		$internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
129
+		$internalPath = $this->getInternalFolder((string) $fileId, $flatPath);
130 130
 		$folder = $this->appData->getFolder($internalPath);
131 131
 
132 132
 		/**
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
 		$previewFiles = [];
136 136
 
137 137
 		foreach ($folder->getDirectoryListing() as $previewFile) {
138
-			$path = $fileId . '/' . $previewFile->getName();
138
+			$path = $fileId.'/'.$previewFile->getName();
139 139
 			/** @var SimpleFile $previewFile */
140 140
 			$preview = Preview::fromPath($path, $this->mimeTypeDetector);
141 141
 			$preview->setId($this->generator->nextId());
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 				$file = $previewFile['file'];
172 172
 				$preview->setStorageId($result[0]['storage']);
173 173
 				$preview->setEtag($result[0]['etag']);
174
-				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
174
+				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int) $result[0]['mimetype']));
175 175
 				try {
176 176
 					$preview = $this->previewMapper->insert($preview);
177 177
 				} catch (Exception) {
@@ -218,14 +218,14 @@  discard block
 block discarded – undo
218 218
 		if ($flatPath) {
219 219
 			return $name;
220 220
 		}
221
-		return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
221
+		return implode('/', str_split(substr(md5($name), 0, 7))).'/'.$name;
222 222
 	}
223 223
 
224 224
 	private function deleteFolder(string $path): void {
225 225
 		$current = $path;
226 226
 
227 227
 		while (true) {
228
-			$appDataPath = $this->previewRootPath . $current;
228
+			$appDataPath = $this->previewRootPath.$current;
229 229
 			$qb = $this->connection->getQueryBuilder();
230 230
 			$qb->delete('filecache')
231 231
 				->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
Please login to merge, or discard this patch.