1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CloudControl\Cms\storage\repository { |
4
|
|
|
|
5
|
|
|
use CloudControl\Cms\storage\Document; |
6
|
|
|
use CloudControl\Cms\storage\Repository; |
7
|
|
|
use CloudControl\Cms\storage\storage\DocumentStorage; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Created by jensk on 4-9-2017. |
11
|
|
|
*/ |
12
|
|
|
class ContentRepository |
13
|
|
|
{ |
14
|
|
|
protected $storagePath; |
15
|
|
|
protected $contentDbHandle; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* ContentRepository constructor. |
19
|
|
|
* @param $storagePath |
20
|
|
|
*/ |
21
|
|
|
public function __construct($storagePath) |
22
|
|
|
{ |
23
|
|
|
$this->storagePath = $storagePath; |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @return \PDO |
28
|
|
|
*/ |
29
|
|
View Code Duplication |
public function getContentDbHandle() |
|
|
|
|
30
|
|
|
{ |
31
|
|
|
if ($this->contentDbHandle === null) { |
32
|
|
|
$this->contentDbHandle = new \PDO('sqlite:' . $this->storagePath . DIRECTORY_SEPARATOR . 'content.db'); |
33
|
|
|
} |
34
|
|
|
return $this->contentDbHandle; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Prepare the sql statement |
39
|
|
|
* @param $sql |
40
|
|
|
* @return \PDOStatement |
41
|
|
|
* @throws \Exception |
42
|
|
|
*/ |
43
|
|
|
protected function getDbStatement($sql) |
44
|
|
|
{ |
45
|
|
|
$db = $this->getContentDbHandle(); |
46
|
|
|
$stmt = $db->query($sql); |
47
|
|
View Code Duplication |
if ($stmt === false) { |
|
|
|
|
48
|
|
|
$errorInfo = $db->errorInfo(); |
49
|
|
|
$errorMsg = $errorInfo[2]; |
50
|
|
|
throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>'); |
51
|
|
|
} |
52
|
|
|
return $stmt; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @param Repository $repository |
57
|
|
|
* @param $document |
58
|
|
|
* @param $db |
59
|
|
|
* @param $documents |
60
|
|
|
* @param $key |
61
|
|
|
* @return mixed |
62
|
|
|
*/ |
63
|
|
|
private function setAssetsToDocumentFolders(Repository $repository, $document, $db, $documents, $key) |
64
|
|
|
{ |
65
|
|
|
if ($document->type === 'folder') { |
66
|
|
|
$document->dbHandle = $db; |
67
|
|
|
$document->documentStorage = new DocumentStorage($repository); |
68
|
|
|
$documents[$key] = $document; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return $documents; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @param Repository $repository |
76
|
|
|
* @param string $folderPath |
77
|
|
|
* @return array |
78
|
|
|
*/ |
79
|
|
|
public function getDocumentsWithState(Repository $repository, $folderPath = '/') |
80
|
|
|
{ |
81
|
|
|
$db = $this->getContentDbHandle(); |
82
|
|
|
$folderPathWithWildcard = $folderPath . '%'; |
83
|
|
|
|
84
|
|
|
$ifRootIndex = 1; |
85
|
|
|
if ($folderPath == '/') { |
86
|
|
|
$ifRootIndex = 0; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$sql = ' |
90
|
|
|
SELECT documents_unpublished.*, |
91
|
|
|
IFNULL(documents_published.state,"unpublished") as state, |
92
|
|
|
IFNULL(documents_published.publicationDate,NULL) as publicationDate, |
93
|
|
|
(documents_published.lastModificationDate != documents_unpublished.lastModificationDate) as unpublishedChanges |
94
|
|
|
FROM documents_unpublished |
95
|
|
|
LEFT JOIN documents_published |
96
|
|
|
ON documents_published.path = documents_unpublished.path |
97
|
|
|
WHERE documents_unpublished.`path` LIKE ' . $db->quote($folderPathWithWildcard) . ' |
98
|
|
|
AND substr(documents_unpublished.`path`, ' . (strlen($folderPath) + $ifRootIndex + 1) . ') NOT LIKE "%/%" |
99
|
|
|
AND length(documents_unpublished.`path`) > ' . (strlen($folderPath) + $ifRootIndex) . ' |
100
|
|
|
AND documents_unpublished.path != ' . $db->quote($folderPath) . ' |
101
|
|
|
ORDER BY documents_unpublished.`type` DESC, documents_unpublished.`path` ASC |
102
|
|
|
'; |
103
|
|
|
$stmt = $this->getDbStatement($sql); |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
$documents = $stmt->fetchAll(\PDO::FETCH_CLASS, '\CloudControl\Cms\storage\Document'); |
107
|
|
|
foreach ($documents as $key => $document) { |
108
|
|
|
$documents = $this->setAssetsToDocumentFolders($repository, $document, $db, $documents, $key); |
109
|
|
|
} |
110
|
|
|
//dump($documents); |
111
|
|
|
return $documents; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Get all documents and folders in a certain path |
116
|
|
|
* |
117
|
|
|
* @param Repository $repository |
118
|
|
|
* @param $folderPath |
119
|
|
|
* @param string $state |
120
|
|
|
* @return array |
121
|
|
|
* @throws \Exception |
122
|
|
|
*/ |
123
|
|
|
public function getDocumentsByPath(Repository $repository, $folderPath, $state = 'published') |
124
|
|
|
{ |
125
|
|
|
if (!in_array($state, Document::$DOCUMENT_STATES)) { |
126
|
|
|
throw new \Exception('Unsupported document state: ' . $state); |
127
|
|
|
} |
128
|
|
|
$db = $this->getContentDbHandle(); |
129
|
|
|
$folderPathWithWildcard = $folderPath . '%'; |
130
|
|
|
|
131
|
|
|
$sql = 'SELECT * |
132
|
|
|
FROM documents_' . $state . ' |
133
|
|
|
WHERE `path` LIKE ' . $db->quote($folderPathWithWildcard) . ' |
134
|
|
|
AND substr(`path`, ' . (strlen($folderPath) + 1) . ') NOT LIKE "%/%" |
135
|
|
|
AND path != ' . $db->quote($folderPath) . ' |
136
|
|
|
ORDER BY `type` DESC, `path` ASC'; |
137
|
|
|
$stmt = $this->getDbStatement($sql); |
138
|
|
|
|
139
|
|
|
$documents = $stmt->fetchAll(\PDO::FETCH_CLASS, '\CloudControl\Cms\storage\Document'); |
140
|
|
|
foreach ($documents as $key => $document) { |
141
|
|
|
$documents = $this->setAssetsToDocumentFolders($repository, $document, $db, $documents, $key); |
142
|
|
|
} |
143
|
|
|
return $documents; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @param \CloudControl\Cms\storage\Repository $repository |
148
|
|
|
* @param $path |
149
|
|
|
* @param string $state |
150
|
|
|
* @return bool|Document |
151
|
|
|
* @throws \Exception |
152
|
|
|
*/ |
153
|
|
|
public function getDocumentByPath(Repository $repository, $path, $state = 'published') |
154
|
|
|
{ |
155
|
|
|
if (!in_array($state, Document::$DOCUMENT_STATES)) { |
156
|
|
|
throw new \Exception('Unsupported document state: ' . $state); |
157
|
|
|
} |
158
|
|
|
$db = $this->getContentDbHandle(); |
159
|
|
|
$document = $this->fetchDocument(' |
160
|
|
|
SELECT * |
161
|
|
|
FROM documents_' . $state . ' |
162
|
|
|
WHERE path = ' . $db->quote($path) . ' |
163
|
|
|
'); |
164
|
|
|
if ($document instanceof Document && $document->type === 'folder') { |
165
|
|
|
$document->dbHandle = $db; |
166
|
|
|
$document->documentStorage = new DocumentStorage($repository); |
167
|
|
|
} |
168
|
|
|
return $document; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Return the result of the query as Document |
173
|
|
|
* @param $sql |
174
|
|
|
* @return mixed |
175
|
|
|
* @throws \Exception |
176
|
|
|
*/ |
177
|
|
|
protected function fetchDocument($sql) |
178
|
|
|
{ |
179
|
|
|
$stmt = $this->getDbStatement($sql); |
180
|
|
|
return $stmt->fetchObject('\CloudControl\Cms\storage\Document'); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @param Repository $repository |
185
|
|
|
* @param $path |
186
|
|
|
* @return bool|Document |
187
|
|
|
*/ |
188
|
|
|
public function getDocumentContainerByPath(Repository $repository, $path) |
189
|
|
|
{ |
190
|
|
|
$document = $this->getDocumentByPath($repository, $path, 'unpublished'); |
191
|
|
|
if ($document === false) { |
192
|
|
|
return false; |
193
|
|
|
} |
194
|
|
|
$slugLength = strlen($document->slug); |
195
|
|
|
$containerPath = substr($path, 0, -$slugLength); |
196
|
|
|
if ($containerPath === '/') { |
197
|
|
|
return $this->getRootFolder(); |
198
|
|
|
} |
199
|
|
|
if (substr($containerPath, -1) === '/') { |
200
|
|
|
$containerPath = substr($containerPath, 0, -1); |
201
|
|
|
} |
202
|
|
|
$containerFolder = $this->getDocumentByPath($repository, $containerPath, 'unpublished'); |
203
|
|
|
return $containerFolder; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Create a (non-existent) root folder Document and return it |
208
|
|
|
* @return Document |
209
|
|
|
*/ |
210
|
|
|
protected function getRootFolder() |
211
|
|
|
{ |
212
|
|
|
$rootFolder = new Document(); |
213
|
|
|
$rootFolder->path = '/'; |
214
|
|
|
$rootFolder->type = 'folder'; |
215
|
|
|
return $rootFolder; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Returns the count of all documents stored in the db |
220
|
|
|
* |
221
|
|
|
* @param string $state |
222
|
|
|
* |
223
|
|
|
* @return int |
224
|
|
|
* @throws \Exception |
225
|
|
|
*/ |
226
|
|
|
public function getTotalDocumentCount($state = 'published') |
227
|
|
|
{ |
228
|
|
|
if (!in_array($state, Document::$DOCUMENT_STATES)) { |
229
|
|
|
throw new \Exception('Unsupported document state: ' . $state); |
230
|
|
|
} |
231
|
|
|
$db = $this->getContentDbHandle(); |
232
|
|
|
$stmt = $db->query(' |
233
|
|
|
SELECT count(*) |
234
|
|
|
FROM documents_' . $state . ' |
235
|
|
|
WHERE `type` != "folder" |
236
|
|
|
'); |
237
|
|
|
$result = $stmt->fetch(\PDO::FETCH_ASSOC); |
238
|
|
|
if (!is_array($result)) { |
239
|
|
|
return 0; |
240
|
|
|
} |
241
|
|
|
return intval(current($result)); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @return array |
246
|
|
|
* @throws \Exception |
247
|
|
|
*/ |
248
|
|
|
public function getPublishedDocumentsNoFolders() |
249
|
|
|
{ |
250
|
|
|
$db = $this->getContentDbHandle(); |
251
|
|
|
$sql = ' |
252
|
|
|
SELECT * |
253
|
|
|
FROM documents_published |
254
|
|
|
WHERE `type` != "folder" |
255
|
|
|
'; |
256
|
|
|
$stmt = $db->query($sql); |
257
|
|
|
$result = $stmt->fetchAll(\PDO::FETCH_CLASS, '\CloudControl\Cms\storage\Document'); |
258
|
|
View Code Duplication |
if ($stmt === false || !$stmt->execute()) { |
|
|
|
|
259
|
|
|
$errorInfo = $db->errorInfo(); |
260
|
|
|
$errorMsg = $errorInfo[2]; |
261
|
|
|
throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>'); |
262
|
|
|
} |
263
|
|
|
return $result; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @param $path |
268
|
|
|
*/ |
269
|
|
|
public function publishDocumentByPath($path) |
270
|
|
|
{ |
271
|
|
|
$this->publishOrUnpublishDocumentByPath($path); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* @param $path |
276
|
|
|
*/ |
277
|
|
|
public function unpublishDocumentByPath($path) |
278
|
|
|
{ |
279
|
|
|
$this->publishOrUnpublishDocumentByPath($path, false); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @param $path |
284
|
|
|
* @param bool $publish |
285
|
|
|
* @throws \Exception |
286
|
|
|
*/ |
287
|
|
|
public function publishOrUnpublishDocumentByPath($path, $publish = true) |
288
|
|
|
{ |
289
|
|
|
if ($publish) { |
290
|
|
|
$sql = ' |
291
|
|
|
INSERT OR REPLACE INTO documents_published |
292
|
|
|
(`id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`publicationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`) |
293
|
|
|
SELECT `id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,"published" as state,`lastModificationDate`,`creationDate`,' . time() . ' as publicationDate, `lastModifiedBy`,`fields`,`bricks`,`dynamicBricks` |
294
|
|
|
FROM documents_unpublished |
295
|
|
|
WHERE `path` = :path |
296
|
|
|
'; |
297
|
|
|
} else { |
298
|
|
|
$sql = 'DELETE FROM documents_published |
299
|
|
|
WHERE `path` = :path'; |
300
|
|
|
} |
301
|
|
|
$db = $this->getContentDbHandle(); |
302
|
|
|
$stmt = $db->prepare($sql); |
303
|
|
View Code Duplication |
if ($stmt === false) { |
|
|
|
|
304
|
|
|
$errorInfo = $db->errorInfo(); |
305
|
|
|
$errorMsg = $errorInfo[2]; |
306
|
|
|
throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>'); |
307
|
|
|
} |
308
|
|
|
$stmt->bindValue(':path', $path); |
309
|
|
|
$stmt->execute(); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function cleanPublishedDeletedDocuments() |
313
|
|
|
{ |
314
|
|
|
$sql = ' DELETE FROM documents_published |
315
|
|
|
WHERE documents_published.path IN ( |
316
|
|
|
SELECT documents_published.path |
317
|
|
|
FROM documents_published |
318
|
|
|
LEFT JOIN documents_unpublished |
319
|
|
|
ON documents_unpublished.path = documents_published.path |
320
|
|
|
WHERE documents_unpublished.path IS NULL |
321
|
|
|
)'; |
322
|
|
|
$stmt = $this->getDbStatement($sql); |
323
|
|
|
$stmt->execute(); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Save the document to the database |
328
|
|
|
* |
329
|
|
|
* @param Document $documentObject |
330
|
|
|
* @param string $state |
331
|
|
|
* |
332
|
|
|
* @return bool |
333
|
|
|
* @throws \Exception |
334
|
|
|
*/ |
335
|
|
|
public function saveDocument($documentObject, $state = 'published') |
336
|
|
|
{ |
337
|
|
|
if (!in_array($state, Document::$DOCUMENT_STATES)) { |
338
|
|
|
throw new \Exception('Unsupported document state: ' . $state); |
339
|
|
|
} |
340
|
|
|
$db = $this->getContentDbHandle(); |
341
|
|
|
$stmt = $this->getDbStatement(' |
342
|
|
|
INSERT OR REPLACE INTO documents_' . $state . ' (`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`) |
343
|
|
|
VALUES( |
344
|
|
|
' . $db->quote($documentObject->path) . ', |
345
|
|
|
' . $db->quote($documentObject->title) . ', |
346
|
|
|
' . $db->quote($documentObject->slug) . ', |
347
|
|
|
' . $db->quote($documentObject->type) . ', |
348
|
|
|
' . $db->quote($documentObject->documentType) . ', |
349
|
|
|
' . $db->quote($documentObject->documentTypeSlug) . ', |
350
|
|
|
' . $db->quote($documentObject->state) . ', |
351
|
|
|
' . $db->quote($documentObject->lastModificationDate) . ', |
352
|
|
|
' . $db->quote($documentObject->creationDate) . ', |
353
|
|
|
' . $db->quote($documentObject->lastModifiedBy) . ', |
354
|
|
|
' . $db->quote(json_encode($documentObject->fields)) . ', |
355
|
|
|
' . $db->quote(json_encode($documentObject->bricks)) . ', |
356
|
|
|
' . $db->quote(json_encode($documentObject->dynamicBricks)) . ' |
357
|
|
|
) |
358
|
|
|
'); |
359
|
|
|
$result = $stmt->execute(); |
360
|
|
|
return $result; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Delete the document from the database |
365
|
|
|
* If it's a folder, also delete it's contents |
366
|
|
|
* |
367
|
|
|
* @param Repository $repository |
368
|
|
|
* @param $path |
369
|
|
|
*/ |
370
|
|
|
public function deleteDocumentByPath(Repository $repository, $path) |
371
|
|
|
{ |
372
|
|
|
$db = $this->getContentDbHandle(); |
373
|
|
|
$documentToDelete = $this->getDocumentByPath($repository, $path, 'unpublished'); |
374
|
|
|
if ($documentToDelete instanceof Document) { |
375
|
|
|
if ($documentToDelete->type == 'document') { |
376
|
|
|
$stmt = $this->getDbStatement(' |
377
|
|
|
DELETE FROM documents_unpublished |
378
|
|
|
WHERE path = ' . $db->quote($path) . ' |
379
|
|
|
'); |
380
|
|
|
$stmt->execute(); |
381
|
|
|
} elseif ($documentToDelete->type == 'folder') { |
382
|
|
|
$folderPathWithWildcard = $path . '%'; |
383
|
|
|
$stmt = $this->getDbStatement(' |
384
|
|
|
DELETE FROM documents_unpublished |
385
|
|
|
WHERE (path LIKE ' . $db->quote($folderPathWithWildcard) . ' |
386
|
|
|
AND substr(`path`, ' . (strlen($path) + 1) . ', 1) = "/") |
387
|
|
|
OR path = ' . $db->quote($path) . ' |
388
|
|
|
'); |
389
|
|
|
$stmt->execute(); |
390
|
|
|
} |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
|
396
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.