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

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