PluginProbe
Depicter — Popup & Slider Builder / 1.2.0
Depicter — Popup & Slider Builder v1.2.0
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / app / src / Database / Repository / DocumentRepository.php

DocumentRepository.php in Depicter — Popup & Slider Builder 1.2.0, at app/src/Database/Repository/DocumentRepository.php

708 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Depicter\Database\Repository;
3
4 use Averta\Core\Utility\Arr;
5 use Averta\Core\Utility\JSON;
6 use Depicter;
7 use Depicter\Database\Entity\Document;
8 use Depicter\Exception\DocumentNotFoundException;
9 use Exception;
10 use TypeRocket\Database\Results;
11 use TypeRocket\Models\Model;
12
13 class DocumentRepository
14 {
15 /**
16 * @var Document
17 */
18 private $document;
19
20
21 public function __construct(){
22 $this->document = New Document();
23 }
24
25 /**
26 * @return Document
27 *
28 * @throws Exception
29 */
30 public function document()
31 {
32 return new Document();
33 }
34
35
36 /**
37 * Save changes directly
38 *
39 * @param int $id
40 * @param array $properties
41 *
42 * @return array
43 * @throws Exception
44 */
45 public function saveEditorData( int $id = 0, array $properties = [] )
46 {
47 if( isset( $properties['editor'] ) ){
48 $editor = $properties['editor'];
49 unset( $properties['editor'] );
50
51 $parsedObject = $this->getParsedEditorContent( $editor );
52
53 $properties['sections_count' ] = $parsedObject->getSectionsCount();
54 $properties['content' ] = JSON::normalize( $editor );
55 }
56
57 // validate if the slug is not taken before
58 if( isset( $properties['slug' ] ) ){
59 $properties['slug' ] = trim( $properties['slug' ] );
60
61 if( empty( $properties['slug' ] ) ){
62 throw new Exception('Slug cannot be empty.');
63 }
64 if( $this->checkSlug( $properties['slug' ], $id ) ){
65 throw new Exception('The slug is already taken by another document.');
66 }
67 }
68
69 // name cannot be empty string
70 if( isset( $properties['name' ] ) ){
71 $properties['name' ] = trim( $properties['name' ] );
72
73 if( empty( $properties['name' ] ) ){
74 throw new Exception('Name cannot be empty.');
75 }
76 }
77
78 $result = $this->update( $id, $properties, true );
79
80 if( $properties['status'] == 'publish' ){
81 $this->addRevision( $id, $properties );
82 }
83
84 return [
85 'result' => $result,
86 'modifiedAt' => $this->document->getApiProperties()['modified_at'],
87 'publishedAt' => $this->document->getLastPublishedAt()
88 ];
89 }
90
91 /**
92 * @param $editorContent
93 *
94 * @return mixed
95 */
96 private function getParsedEditorContent( $editorContent ){
97 // Get editor data
98 $documentMapper = \Depicter::resolve('depicter.document.mapper');
99 return $documentMapper->hydrate( $editorContent )->get();
100 }
101
102 /**
103 * Creates new revision for a document
104 *
105 * @param int $id
106 * @param array $fields
107 *
108 * @return mixed
109 * @throws Exception
110 */
111 protected function addRevision( int $id = 0, array $fields = [] )
112 {
113 $fields['created_at'] = $this->document->getDateTime();
114 $fields['parent'] = $id;
115
116 // check if revisions limit exceed than 15 number then remove the oldest one
117 if( DEPICTER_REVISIONS ){
118 $this->checkRevisionsLimit( $id );
119 }
120
121 $documentId = $this->findOrCreate(0, $fields);
122
123 return $documentId;
124 }
125
126 /**
127 * Reverts to a previously published checkpoint
128 *
129 * @param $documentId
130 * @param string $revisionId
131 *
132 * @return array|bool|false|int|object|Model|null
133 * @throws Exception
134 */
135 public function revert( $documentId, string $revisionId = '~1' ){
136
137 if( ! $parentDocument = $this->findOne( $documentId ) ){
138 throw new Exception('Document does not exist.');
139 }
140 // Set a valid $revisionId
141 if( "" === $revisionId || is_null( $revisionId ) ){
142 $revisionId = "~1";
143 }
144
145 $lastRevision = null;
146
147 // Reverting the status back by step number, prefixed by `~`
148 if( false !== strpos( $revisionId, '~' ) ){
149 $revisionOffset = (int) ltrim( $revisionId, '~' );
150 $lastRevision = $this->getRecentRevision( $documentId, $revisionOffset - 1 );
151 // revert by revision document ID
152 } elseif( is_numeric( $revisionId ) ){
153 $lastRevision = $this->findOne( $revisionId );
154 }
155
156 if( ! empty( $lastRevision ) ){
157 // Throw error if revision does not belong to the document
158 if( $parentDocument->getID() != $lastRevision->parent() ){
159 throw new Exception('Revision ID does not belong to this document.');
160 }
161 // Set revision editor data for document
162 $parentDocument->save( ['content' => $lastRevision->content() ] );
163
164 } else {
165 throw new Exception("Couldn't revert to the revision. No revision found for this checkpoint.");
166 }
167
168 return $lastRevision;
169 }
170
171 /**
172 * Check for revisions limit number
173 *
174 * @param $id
175 *
176 * @throws Exception
177 */
178 protected function checkRevisionsLimit( $id ) {
179 $revisions = $this->document()->select('id')->where('parent', $id)->findAll();
180 if ( $revisions->count() >= DEPICTER_REVISIONS ) {
181 $this->document()->findById( $revisions->first()->id )->delete();
182 }
183 }
184
185 /**
186 * Save changes directly to current document
187 *
188 * @param array $fields
189 *
190 * @return array|Model|object|Results|null
191 */
192 public function all( array $fields = [] )
193 {
194 return $this->document->findAll()->get();
195 }
196
197 /**
198 * Save changes directly to current document
199 *
200 * @param array $fields
201 *
202 * @return array
203 * @throws Exception
204 */
205 public function getList( array $fields = [] )
206 {
207 $columnsName = !empty( $fields ) ? $fields : ['id', 'name', 'slug', 'author', 'sections_count', 'created_at', 'modified_at', 'thumbnail', 'status'];
208 $documents = $this->document()->reselect( $columnsName )
209 ->where('parent', '0')
210 ->findAll()->get();
211
212 $documents = $documents ? $documents->toArray() : [];
213
214 if ( $documents && empty( $fields ) ) {
215 $uploadDir = wp_upload_dir();
216 foreach ( $documents as $key => $document ) {
217 $documents[ $key ]['publishedAt'] = $this->getLastPublishedAt( $document );
218
219 if ( is_file( $uploadDir['basedir'] . '/depicter/preview-images/' . $document['id'] . '.png' ) ) {
220 $documents[ $key ]['previewImage'] = $uploadDir['baseurl'] . '/depicter/preview-images/' . $document['id'] . '.png';
221 } else {
222 $documents[ $key ]['previewImage'] = '';
223 }
224 }
225 }
226
227 return $documents;
228 }
229
230 /**
231 * Save changes directly to current document
232 *
233 * @param array $fields
234 *
235 * @return mixed
236 */
237 public function save( array $fields = [] )
238 {
239 $fields = $this->getMergedFields( $fields );
240 return $this->document->save( $fields);
241 }
242
243 /**
244 * Updates a document by ID
245 *
246 * @param int $id
247 * @param array $fields
248 * @param bool $parentOnly Only update the record if is parent document
249 *
250 * @return mixed
251 * @throws Exception
252 */
253 public function update( int $id = 0, array $fields = [], bool $parentOnly = false )
254 {
255 if( $document = $this->document->findById( $id ) ){
256 if( $parentOnly && $document->getFieldValue('parent') != 0 ){
257 throw new Exception('Updating revision is not allowed.');
258 }
259
260 $fields['modified_at'] = $document->getDateTime();
261 return $document->update( $fields );
262 }
263 return false;
264 }
265
266
267
268 /**
269 * Creates new document in database
270 *
271 * @return Document
272 * @throws Exception
273 */
274 public function create()
275 {
276 $documentId = $this->findOrCreate(0, [
277 'created_at' => $this->document->getDateTime(),
278 'modified_at' => $this->document->getDateTime(),
279 'slug' => $this->makeSlug()
280 ]);
281
282 $document = $this->document()->findById( $documentId );
283 $document->rename( $document->getFieldValue( 'name' ) . ' ' . $documentId );
284
285 return $document;
286 }
287
288 /**
289 * Renames a document.
290 *
291 * @param $id
292 * @param $name
293 *
294 * @return int
295 */
296 public function rename( $id, $name )
297 {
298 if( $document = $this->document->findById( $id ) ){
299 return $document->rename( $name );
300 }
301 return false;
302 }
303
304 /**
305 * Changes the document slug.
306 *
307 * @param $id
308 * @param $slug
309 *
310 * @return int
311 * @throws Exception
312 */
313 public function changeSlug( $id, $slug )
314 {
315 if( $this->checkSlug( $slug ) ){
316 throw new Exception("The slug is already in use.");
317 }
318 if( $document = $this->document->findById( $id ) ){
319 return $document->changeSlug( $slug );
320 }
321 return false;
322 }
323
324 /**
325 * Duplicates a document.
326 *
327 * @param int $id
328 *
329 * @param bool $returnNew
330 *
331 * @return int
332 * @throws Exception
333 */
334 public function duplicate( int $id, bool $returnNew = false )
335 {
336 if( $document = $this->document->findById( $id ) ){
337 $fields = $document->getProperties();
338
339 unset( $fields['id'] );
340
341 $fields['name'] = is_string( $fields['name'] ) ? $fields['name'] . ' copy' : $fields['name'];
342 $fields['slug'] = $this->makeSlug();
343 $fields['created_at'] = $this->document->getDateTime();
344
345 if( ! is_numeric( $fields['sections_count'] ) ){
346 unset( $fields['sections_count'] );
347 }
348 if( ! is_numeric( $fields['parent'] ) ){
349 unset( $fields['parent'] );
350 }
351 if( ! is_numeric( $fields['author'] ) ){
352 unset( $fields['author'] );
353 }
354 if( is_null( $fields['content'] ) ){
355 unset( $fields['content'] );
356 }
357 if( is_null( $fields['password'] ) ){
358 unset( $fields['password'] );
359 }
360 if( is_null( $fields['thumbnail'] ) ){
361 unset( $fields['thumbnail'] );
362 }
363
364 $newId = $this->findOrCreate( 0, $fields );
365
366 return $this->document()->findById( $newId );
367 }
368
369 return false;
370 }
371
372 /**
373 * Removes a document.
374 *
375 * @param $id
376 *
377 * @return int
378 * @throws Exception
379 */
380 public function delete( $id )
381 {
382 if( $document = $this->document()->findById( $id ) ){
383 return $document->delete();
384 }
385 return false;
386 }
387
388 /**
389 * Find a document by ID
390 *
391 * @param integer $id
392 *
393 * @return mixed
394 */
395 public function findById( int $id )
396 {
397 return $this->document->findById( $id );
398 }
399
400 /**
401 * Finds or creates new document in database
402 *
403 * @param integer $id
404 * @param array $fields
405 *
406 * @return mixed returns Document if exists or id of created document
407 * @throws Exception
408 */
409 public function findOrCreate( int $id, array $fields = [] )
410 {
411 $fields = $this->getMergedFields( $fields );
412
413 return $this->document()->findOrCreate( $id, $fields );
414 }
415
416 /**
417 * Retrieves the lst document
418 *
419 * @return array|bool|false|int|object|Model|null
420 * @throws Exception
421 */
422 public function getLastDocument()
423 {
424 return $this->document()->findAll()->orderBy('id', 'DESC')->first();
425 }
426
427 /**
428 * Make a unique slug
429 *
430 * @param string $slug
431 * @param int $id
432 *
433 * @return int
434 * @throws Exception
435 */
436 public function makeSlug( string $slug = '', int $id = 0 )
437 {
438 if( ! $id && $document = $this->getLastDocument() ){
439 $id = $document->getID();
440 }
441
442 if( ! $slug ){
443 $slug = 'document';
444 }
445
446 $newID = $id + 1;
447 $newSlug = $slug . '-' . $newID;
448
449 while( $this->checkSlug( $newSlug ) ){
450 $newID++;
451 $newSlug = $slug . '-' . $newID;
452 }
453
454 return $newSlug;
455 }
456
457 /**
458 * Rename document
459 *
460 * @param string $slug
461 *
462 * @param int $ignoreID The document ID to ignore on check
463 *
464 * @return int
465 * @throws Exception
466 */
467 public function checkSlug( string $slug, int $ignoreID = 0 )
468 {
469 $document = $this->document();
470 $foundCount = $document->where('slug', $slug )
471 ->where('id', 'NOT LIKE', $ignoreID )
472 ->findAll()->count();
473
474 if( $foundCount > 0 ){
475 unset( $document );
476 return true;
477 }
478
479 return false;
480 }
481
482 /**
483 * Retrieves default fields
484 *
485 * @return array
486 */
487 public function draftFields()
488 {
489 return [
490 'name' => __('Untitled Slider'),
491 'status' => 'draft',
492 'author' => $this->getCurrentUserId()
493 ];
494 }
495
496 /**
497 * Retrieves current logged in user ID
498 *
499 * @return int
500 */
501 public function getCurrentUserId(){
502 return is_user_logged_in() ? get_current_user_id() : 0;
503 }
504
505 /**
506 * Retrieves default fields
507 *
508 * @return array
509 */
510 public function defaultFields()
511 {
512 return [
513 'name' => __('Untitled Slider'),
514 'slug' => '',
515 'author' => 0,
516 'parent' => 0,
517 'created_at' => $this->document->getDateTime(),
518 'sections_count' => 0,
519 'thumbnail' => '',
520 'content' => '',
521 'password' => '',
522 'status' => 'draft'
523 ];
524 }
525
526 /**
527 * Merge fields with default fields
528 *
529 * @param $fields
530 *
531 * @return array
532 */
533 public function getMergedFields( $fields )
534 {
535 return Arr::merge( $fields, $this->draftFields() );
536 }
537
538 /**
539 * Get a revision of a document
540 *
541 * @param int $id Document ID
542 * @param int $offset offset
543 *
544 * @return array|Model|object|Results|null
545 * @throws Exception
546 */
547 public function getRecentRevision( int $id, int $offset = 0 ) {
548 $offset = max( $offset, 0 );
549 return $this->document()->orderBy('id', 'DESC')->where( 'parent', $id )->take( 1, $offset )->get();
550 }
551
552 /**
553 * @param array $document Document object in array
554 *
555 * @return mixed|string|null
556 * @throws Exception
557 */
558 public function getLastPublishedAt( array $document ) {
559 if ( $document['status'] == 'publish' ) {
560 return $document['modified_at'];
561 }
562
563 $lastRevision = $this->getRecentRevision( $document['id'] );
564 return $lastRevision ? $lastRevision->modified_at : null;
565
566 }
567
568 /**
569 * Get a document entity
570 *
571 * @param null $documentId
572 * @param array $where
573 *
574 * @return array|bool|int|object|Results|Model|null
575 * @throws Exception
576 */
577 public function findOne( $documentId = null, array $where = [] ){
578
579 if( $documentId ){
580 $where['id'] = $documentId;
581 }
582
583 $documentEntity = $this->find( $where );
584
585 if ( ! $documentEntity->count() && !empty( $where['id'] ) ) {
586 $where['status'] = 'publish';
587 $where['parent'] = $where['id'];
588 unset( $where['id'] );
589
590 if ( ! $documentEntity = $this->find( $where )->orderBy('id', 'DESC') ) {
591 return false;
592 }
593 }
594
595 return $documentEntity->first();
596 }
597
598 /**
599 * Retrieves the content of document
600 *
601 * @param int $documentId
602 * @param array $where
603 *
604 * @return mixed
605 * @throws DocumentNotFoundException
606 */
607 public function geContent( int $documentId, array $where = [] ){
608 if( ! $documentEntity = $this->findOne( $documentId, $where ) ){
609 if ( isset( $where['status'] ) && is_array( $where['status'] ) ) {
610 unset( $where['status'][0] );
611 if( ! $documentEntity = $this->findOne( $documentId, $where ) ){
612 throw new DocumentNotFoundException( 'Document does not exist.', 404, $where );
613 }
614 } else {
615 throw new DocumentNotFoundException( 'Document does not exist.', 404, $where );
616 }
617 }
618 return $documentEntity->content();
619 }
620
621 /**
622 * Find based of where clauses
623 *
624 * @param array $where
625 *
626 * @return Document
627 * @throws Exception
628 */
629 public function find( array $where = [] ){
630 $documentEntity = $this->document();
631 foreach ( $where as $clause => $clauseValue ) {
632 $documentEntity->where( $clause, is_array( $clauseValue ) ? 'IN' : '=' , $clauseValue );
633 }
634
635 return $documentEntity;
636 }
637
638 /**
639 * Whether the document exists and is not published or not
640 *
641 * @param int $documentId
642 * @param array $where
643 *
644 * @return bool
645 * @throws Exception
646 */
647 public function isNotPublished( $documentId, $where = [] ){
648 if( ! $documentEntity = $this->findOne( $documentId, $where ) ){
649 if( $documentEntity = $this->findOne( $documentId, [ 'status' => 'draft' ] ) ){
650 return true;
651 }
652 }
653
654 return false;
655 }
656
657 /**
658 * Retrieves the url to preview image of document if exists
659 *
660 * @param int $documentID
661 *
662 * @return string URL of image if exits, otherwise, empty string
663 */
664 public function getPreviewImageUrl( int $documentID ){
665 if( file_exists( $this->getPreviewImagePath( $documentID ) ) ){
666 return Depicter::storage()->uploads()->getBaseUrl() . $this->getPreviewRelativeImagePath( $documentID );
667 }
668 return '';
669 }
670
671 /**
672 * Retrieves the path to preview image of a document
673 *
674 * @param int $documentID
675 *
676 * @return string
677 */
678 public function getPreviewImagePath( int $documentID ){
679 return Depicter::storage()->uploads()->getBaseDirectory() . $this->getPreviewRelativeImagePath( $documentID );
680 }
681
682 /**
683 * Writes preview image to the disk
684 *
685 * @param int $documentID
686 * @param string $imageContent
687 *
688 * @return bool
689 */
690 public function savePreviewImage( int $documentID, string $imageContent ){
691 return Depicter::storage()->filesystem()->write(
692 $this->getPreviewImagePath( $documentID ),
693 (string) $imageContent
694 );
695 }
696
697 /**
698 * Retrieves the relative path to preview image of a document
699 *
700 * @param $documentID
701 *
702 * @return string
703 */
704 protected function getPreviewRelativeImagePath( $documentID ){
705 return '/depicter/preview-images/' . $documentID . '.png';
706 }
707 }
708