PluginProbe
Depicter — Popup & Slider Builder / 1.6.2
Depicter — Popup & Slider Builder v1.6.2
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.6.2, at app/src/Database/Repository/DocumentRepository.php

776 lines 17.2 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 * @param array $args
202 *
203 * @return array
204 * @throws Exception
205 */
206 public function getList( array $fields = [], $args = [] )
207 {
208 $columnsName = !empty( $fields ) ? $fields : ['id', 'name', 'slug', 'author', 'sections_count', 'created_at', 'modified_at', 'thumbnail', 'status'];
209 $numberOfPages = '';
210 if ( !empty( $args['page'] ) && !empty( $args['perPage'] ) ) {
211 $pager = $this->select( $fields )->paginate( $args['perPage'], $args['page'] );
212 if ( $pager ) {
213 $numberOfPages = $pager->getNumberOfPages();
214 $documents = $pager->getResults();
215 } else {
216 $documents = [];
217 }
218
219 } else {
220 $documents = $this->select( $fields )->findAll()->get();
221 }
222
223
224 $documents = $documents ? $documents->toArray() : [];
225
226 if ( $documents && empty( $fields ) ) {
227 $uploadDir = wp_upload_dir();
228 foreach ( $documents as $key => $document ) {
229 $documents[ $key ]['publishedAt'] = $this->getLastPublishedAt( $document );
230
231 if ( is_file( $uploadDir['basedir'] . '/depicter/preview-images/' . $document['id'] . '.png' ) ) {
232 $documents[ $key ]['previewImage'] = $uploadDir['baseurl'] . '/depicter/preview-images/' . $document['id'] . '.png';
233 } else {
234 $documents[ $key ]['previewImage'] = '';
235 }
236 }
237 }
238
239 if ( !empty( $numberOfPages ) ) {
240 return [
241 'page' => $args['page'],
242 'perPage' => $args['perPage'],
243 'numberOfPages' => $numberOfPages,
244 'documents' => $documents
245 ];
246 }
247
248 return $documents;
249 }
250
251 /**
252 * Queries records of documents with specified fields
253 *
254 * @param array $fields
255 *
256 * @return Document
257 * @throws Exception
258 */
259 public function select( array $fields = [] )
260 {
261 $columnsName = !empty( $fields ) ? $fields : ['id', 'name', 'slug', 'author', 'sections_count', 'created_at', 'modified_at', 'thumbnail', 'status'];
262 return $this->document()->reselect( $columnsName )
263 ->where('parent', '0');
264 }
265
266 /**
267 * Save changes directly to current document
268 *
269 * @param array $fields
270 *
271 * @return mixed
272 */
273 public function save( array $fields = [] )
274 {
275 $fields = $this->getMergedFields( $fields );
276 return $this->document->save( $fields);
277 }
278
279 /**
280 * Updates a document by ID
281 *
282 * @param int $id
283 * @param array $fields
284 * @param bool $parentOnly Only update the record if is parent document
285 *
286 * @return mixed
287 * @throws Exception
288 */
289 public function update( int $id = 0, array $fields = [], bool $parentOnly = false )
290 {
291 if( $document = $this->document->findById( $id ) ){
292 if( $parentOnly && $document->getFieldValue('parent') != 0 ){
293 throw new Exception('Updating revision is not allowed.');
294 }
295
296 $fields['modified_at'] = $document->getDateTime();
297 return $document->update( $fields );
298 }
299 return false;
300 }
301
302
303
304 /**
305 * Creates new document in database
306 *
307 * @return Document
308 * @throws Exception
309 */
310 public function create()
311 {
312 $documentId = $this->findOrCreate(0, [
313 'created_at' => $this->document->getDateTime(),
314 'modified_at' => $this->document->getDateTime(),
315 'slug' => $this->makeSlug()
316 ]);
317
318 $document = $this->document()->findById( $documentId );
319 $document->rename( $document->getFieldValue( 'name' ) . ' ' . $documentId );
320
321 return $document;
322 }
323
324 /**
325 * Renames a document.
326 *
327 * @param $id
328 * @param $name
329 *
330 * @return int
331 */
332 public function rename( $id, $name )
333 {
334 if( $document = $this->document->findById( $id ) ){
335 return $document->rename( $name );
336 }
337 return false;
338 }
339
340 /**
341 * Changes the document slug.
342 *
343 * @param $id
344 * @param $slug
345 *
346 * @return int
347 * @throws Exception
348 */
349 public function changeSlug( $id, $slug )
350 {
351 if( $this->checkSlug( $slug ) ){
352 throw new Exception("The slug is already in use.");
353 }
354 if( $document = $this->document->findById( $id ) ){
355 return $document->changeSlug( $slug );
356 }
357 return false;
358 }
359
360 /**
361 * Duplicates a document.
362 *
363 * @param int $id
364 *
365 * @param bool $returnNew
366 *
367 * @return int
368 * @throws Exception
369 */
370 public function duplicate( int $id, bool $returnNew = false )
371 {
372 if( $document = $this->document->findById( $id ) ){
373 $fields = $document->getProperties();
374
375 unset( $fields['id'] );
376
377 $fields['name'] = is_string( $fields['name'] ) ? $fields['name'] . ' copy' : $fields['name'];
378 $fields['slug'] = $this->makeSlug();
379 $fields['created_at'] = $this->document->getDateTime();
380
381 if( ! is_numeric( $fields['sections_count'] ) ){
382 unset( $fields['sections_count'] );
383 }
384 if( ! is_numeric( $fields['parent'] ) ){
385 unset( $fields['parent'] );
386 }
387 if( ! is_numeric( $fields['author'] ) ){
388 unset( $fields['author'] );
389 }
390 if( is_null( $fields['content'] ) ){
391 unset( $fields['content'] );
392 }
393 if( is_null( $fields['password'] ) ){
394 unset( $fields['password'] );
395 }
396 if( is_null( $fields['thumbnail'] ) ){
397 unset( $fields['thumbnail'] );
398 }
399
400 $newId = $this->findOrCreate( 0, $fields );
401
402 return $this->document()->findById( $newId );
403 }
404
405 return false;
406 }
407
408 /**
409 * Removes a document.
410 *
411 * @param $id
412 *
413 * @return int
414 * @throws Exception
415 */
416 public function delete( $id )
417 {
418 if( $document = $this->document()->findById( $id ) ){
419 return $document->delete();
420 }
421 return false;
422 }
423
424 /**
425 * Find a document by ID
426 *
427 * @param integer $id
428 *
429 * @return mixed
430 */
431 public function findById( int $id )
432 {
433 return $this->document()->findById( $id );
434 }
435
436 /**
437 * Finds or creates new document in database
438 *
439 * @param integer $id
440 * @param array $fields
441 *
442 * @return mixed returns Document if exists or id of created document
443 * @throws Exception
444 */
445 public function findOrCreate( int $id, array $fields = [] )
446 {
447 $fields = $this->getMergedFields( $fields );
448
449 return $this->document()->findOrCreate( $id, $fields );
450 }
451
452 /**
453 * Retrieves the lst document
454 *
455 * @return array|bool|false|int|object|Model|null
456 * @throws Exception
457 */
458 public function getLastDocument()
459 {
460 return $this->document()->findAll()->orderBy('id', 'DESC')->first();
461 }
462
463 /**
464 * Make a unique slug
465 *
466 * @param string $slug
467 * @param int $id
468 *
469 * @return int
470 * @throws Exception
471 */
472 public function makeSlug( string $slug = '', int $id = 0 )
473 {
474 if( ! $id && $document = $this->getLastDocument() ){
475 $id = $document->getID();
476 }
477
478 if( ! $slug ){
479 $slug = 'document';
480 }
481
482 $newID = $id + 1;
483 $newSlug = $slug . '-' . $newID;
484
485 while( $this->checkSlug( $newSlug ) ){
486 $newID++;
487 $newSlug = $slug . '-' . $newID;
488 }
489
490 return $newSlug;
491 }
492
493 /**
494 * Rename document
495 *
496 * @param string $slug
497 *
498 * @param int $ignoreID The document ID to ignore on check
499 *
500 * @return int
501 * @throws Exception
502 */
503 public function checkSlug( string $slug, int $ignoreID = 0 )
504 {
505 $document = $this->document();
506 $foundCount = $document->where('slug', $slug )
507 ->where('id', 'NOT LIKE', $ignoreID )
508 ->findAll()->count();
509
510 if( $foundCount > 0 ){
511 unset( $document );
512 return true;
513 }
514
515 return false;
516 }
517
518 /**
519 * Retrieves default fields
520 *
521 * @return array
522 */
523 public function draftFields()
524 {
525 return [
526 'name' => __('Untitled Slider'),
527 'status' => 'draft',
528 'author' => $this->getCurrentUserId()
529 ];
530 }
531
532 /**
533 * Retrieves current logged in user ID
534 *
535 * @return int
536 */
537 public function getCurrentUserId(){
538 return is_user_logged_in() ? get_current_user_id() : 0;
539 }
540
541 /**
542 * Retrieves default fields
543 *
544 * @return array
545 */
546 public function defaultFields()
547 {
548 return [
549 'name' => __('Untitled Slider'),
550 'slug' => '',
551 'author' => 0,
552 'parent' => 0,
553 'created_at' => $this->document->getDateTime(),
554 'sections_count' => 0,
555 'thumbnail' => '',
556 'content' => '',
557 'password' => '',
558 'status' => 'draft'
559 ];
560 }
561
562 /**
563 * Merge fields with default fields
564 *
565 * @param $fields
566 *
567 * @return array
568 */
569 public function getMergedFields( $fields )
570 {
571 return Arr::merge( $fields, $this->draftFields() );
572 }
573
574 /**
575 * Get a revision of a document
576 *
577 * @param int $id Document ID
578 * @param int $offset offset
579 *
580 * @return array|Model|object|Results|null
581 * @throws Exception
582 */
583 public function getRecentRevision( int $id, int $offset = 0 ) {
584 $offset = max( $offset, 0 );
585 return $this->document()->orderBy('id', 'DESC')->where( 'parent', $id )->take( 1, $offset )->get();
586 }
587
588 /**
589 * @param array $document Document object in array
590 *
591 * @return mixed|string|null
592 * @throws Exception
593 */
594 public function getLastPublishedAt( array $document ) {
595 if ( $document['status'] == 'publish' ) {
596 return $document['modified_at'];
597 }
598
599 $lastRevision = $this->getRecentRevision( $document['id'] );
600 return $lastRevision ? $lastRevision->modified_at : null;
601
602 }
603
604 /**
605 * Get a document entity
606 *
607 * @param null $documentId
608 * @param array $where
609 *
610 * @return array|bool|int|object|Results|Model|null
611 * @throws Exception
612 */
613 public function findOne( $documentId = null, array $where = [] ){
614
615 if( $documentId ){
616 $where['id'] = $documentId;
617 }
618
619 $documentEntity = $this->find( $where );
620
621 if ( ! $documentEntity->count() && !empty( $where['id'] ) ) {
622 $where['status'] = 'publish';
623 $where['parent'] = $where['id'];
624 unset( $where['id'] );
625
626 if ( ! $documentEntity = $this->find( $where )->orderBy('id', 'DESC') ) {
627 return false;
628 }
629 }
630
631 return $documentEntity->first();
632 }
633
634 /**
635 * Retrieves the content of document
636 *
637 * @param int $documentId
638 * @param array $where
639 *
640 * @return mixed
641 * @throws DocumentNotFoundException
642 */
643 public function geContent( int $documentId, array $where = [] ){
644 if( ! $documentEntity = $this->findOne( $documentId, $where ) ){
645 if ( isset( $where['status'] ) && is_array( $where['status'] ) ) {
646 unset( $where['status'][0] );
647 if( ! $documentEntity = $this->findOne( $documentId, $where ) ){
648 throw new DocumentNotFoundException( 'Document does not exist.', 404, $where );
649 }
650 } else {
651 throw new DocumentNotFoundException( 'Document does not exist.', 404, $where );
652 }
653 }
654 return $documentEntity->content();
655 }
656
657 /**
658 * Find based of where clauses
659 *
660 * @param array $where
661 *
662 * @return Document
663 * @throws Exception
664 */
665 public function find( array $where = [] ){
666 $documentEntity = $this->document();
667 foreach ( $where as $clause => $clauseValue ) {
668 $documentEntity->where( $clause, is_array( $clauseValue ) ? 'IN' : '=' , $clauseValue );
669 }
670
671 return $documentEntity;
672 }
673
674 /**
675 * Whether the document has been published so far or not
676 *
677 * @param int $documentId
678 *
679 * @return bool
680 */
681 public function isPublishedBefore( $documentId ){
682 try{
683 if( $this->getRecentRevision( $documentId ) ){
684 return true;
685 }
686 } catch ( \Exception $exception ) {
687 return false;
688 }
689
690 return false;
691 }
692
693 /**
694 * Whether the document has published status or not
695 *
696 * @param int $documentId
697 *
698 * @return bool|Document
699 * @throws Exception
700 */
701 public function isPublished( $documentId ){
702 try{
703 return $this->getStatus( $documentId ) === 'publish';
704 } catch ( \Exception $exception ) {
705 return false;
706 }
707 }
708
709 /**
710 * Get status of document
711 *
712 * @param int $documentId
713 *
714 * @return string
715 */
716 public function getStatus( $documentId ) {
717 if( $document = $this->findById( $documentId ) ){
718 $document = $document->toArray();
719 return $document['status'];
720 }
721
722 return '';
723 }
724
725 /**
726 * Retrieves the url to preview image of document if exists
727 *
728 * @param int $documentID
729 *
730 * @return string URL of image if exits, otherwise, empty string
731 */
732 public function getPreviewImageUrl( int $documentID ){
733 if( file_exists( $this->getPreviewImagePath( $documentID ) ) ){
734 return Depicter::storage()->uploads()->getBaseUrl() . $this->getPreviewRelativeImagePath( $documentID );
735 }
736 return '';
737 }
738
739 /**
740 * Retrieves the path to preview image of a document
741 *
742 * @param int $documentID
743 *
744 * @return string
745 */
746 public function getPreviewImagePath( int $documentID ){
747 return Depicter::storage()->uploads()->getBaseDirectory() . $this->getPreviewRelativeImagePath( $documentID );
748 }
749
750 /**
751 * Writes preview image to the disk
752 *
753 * @param int $documentID
754 * @param string $imageContent
755 *
756 * @return bool
757 */
758 public function savePreviewImage( int $documentID, string $imageContent ){
759 return Depicter::storage()->filesystem()->write(
760 $this->getPreviewImagePath( $documentID ),
761 (string) $imageContent
762 );
763 }
764
765 /**
766 * Retrieves the relative path to preview image of a document
767 *
768 * @param $documentID
769 *
770 * @return string
771 */
772 protected function getPreviewRelativeImagePath( $documentID ){
773 return '/depicter/preview-images/' . $documentID . '.png';
774 }
775 }
776