PluginProbe
Depicter — Popup & Slider Builder / 1.9.2
Depicter — Popup & Slider Builder v1.9.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 / Document / Models / Document.php

Document.php in Depicter — Popup & Slider Builder 1.9.2, at app/src/Document/Models/Document.php

698 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Depicter\Document\Models;
3
4
5 use Averta\Core\Hydrate\HydratableInterface;
6 use Averta\Core\Utility\Arr;
7 use Averta\Core\Utility\Data;
8 use Averta\WordPress\Utility\Sanitize;
9 use Depicter\Document\CSS\Selector;
10 use Depicter\Document\Models\Options\Loading;
11 use Depicter\Document\Models\Options\Script;
12 use Depicter\Document\Models\Traits\UnPublishedNoticeTrait;
13 use Depicter\Html\Html;
14 use Depicter\Services\StyleGeneratorService;
15
16 class Document implements HydratableInterface
17 {
18 use UnPublishedNoticeTrait;
19
20
21 /**
22 * @var array
23 */
24 public $sectionsList;
25
26 /**
27 * @var Section[]
28 */
29 public $sections;
30
31 /**
32 * @var Element[]
33 */
34 public $elements;
35
36 /**
37 * @var array
38 */
39 public $foregroundElements;
40
41 /**
42 * @var array
43 */
44 public $foregroundElementObjects = [];
45
46 /**
47 * @var Options\All
48 */
49 public $options;
50
51 /**
52 * @var array
53 */
54 public $meta;
55
56 /**
57 * @var array
58 */
59 public $env = [];
60
61 /**
62 * Start from which section
63 *
64 * @var int
65 */
66 public $startSection = 0;
67
68 /**
69 * Document markup
70 *
71 * @var string
72 */
73 protected $html;
74
75 /**
76 * Style generator class instance
77 *
78 * @var StyleGeneratorService
79 */
80 protected $styleGenerator;
81
82 /**
83 * Collected styles in a list
84 *
85 * @var array
86 */
87 protected $stylesList = [];
88
89 /**
90 * Collected fonts from belonging elements
91 *
92 * @var array
93 */
94 private $fontsList = [];
95
96 /**
97 * Link to download all used fonts in this document
98 *
99 * @var string|null
100 */
101 private $fontLink;
102
103 /**
104 * Extra document args
105 *
106 * @var array
107 */
108 private $args = [];
109
110 /**
111 * Check if document is ai generated or not
112 *
113 * @var boolean
114 */
115 public $isBuildWithAI = false;
116
117 /**
118 * Extract values for this class
119 *
120 * @return array
121 */
122 public function getProperties()
123 {
124 return [
125 'name' => $this->getName(),
126 'slug' => $this->getSlug(),
127 'sections_count' => $this->getSectionsCount(),
128 'content' => $this->get(),
129 'status' => 'published'
130 ];
131 }
132
133 /**
134 * Extract values for this class
135 *
136 * @return array
137 */
138 public function extract()
139 {
140 // TODO: Implement extract() method.
141 }
142
143 /**
144 * Hydrate the class with the provided $data.
145 *
146 * @param array|object $data
147 */
148 public function hydrate($data)
149 {
150 // TODO: Implement hydrate() method.
151 }
152
153 /**
154 * Prepare document for generating markup
155 *
156 * @return $this
157 */
158 public function prepare()
159 {
160 $this->reorderSections();
161 $this->setElementsForObjects();
162 $this->setForegroundElementObjects();
163
164 return $this;
165 }
166
167 public function render()
168 {
169 $this->html = Html::div([
170 'id' => $this->getCssId(),
171 'class' => $this->getClassNames()
172 ]);
173
174 $this->renderNotice();
175 $this->renderLoadingSymbol();
176 $this->renderForegroundElements();
177 $this->renderSectionsAndElements();
178 $this->collectAndSetFontsData();
179 $this->getSelectorAndCssList();
180 $this->renderSymbols();
181
182 return $this->html . $this->getInitScriptTag();
183 }
184
185 /**
186 * Render markup for possible notices
187 *
188 * @return void
189 */
190 protected function renderNotice(){
191 $notice = $this->getUnpublishedChangesNotice();
192 $this->html->nest( "\n" . $notice );
193 }
194
195 /**
196 * Render markup for loading symbols
197 */
198 protected function renderLoadingSymbol() {
199 if ( empty( $this->options->loading ) ) {
200 $this->options->loading = new Loading();
201 }
202 $this->html->nest( "\n" . $this->options->loading->render() );
203 }
204
205 /**
206 * Render symbols markup
207 */
208 protected function renderSymbols() {
209 $symbolsContent = \Depicter::symbolsProvider()->render();
210 if ( !empty( $symbolsContent ) ) {
211 $this->html->nest( "\n" . $symbolsContent );
212 }
213 }
214
215 /**
216 * Render markup and collect styles of foreground elements.
217 */
218 protected function renderForegroundElements(){
219
220 $foregroundAttributes = [ 'class' => Selector::prefixify('overlay-layers') ];
221 $elementsMarkup = "";
222
223 foreach ( $this->foregroundElementObjects as $element ) {
224 $this->stylesList = array_merge( $this->stylesList, $element->prepare()->getSelectorAndCssList() );
225 $elementsMarkup .= $element->prepare()->render() . "\n";
226 }
227
228 if( $elementsMarkup ){
229 $foregroundDiv = Html::div( $foregroundAttributes, "\n" . $elementsMarkup );
230 $this->html->nest( "\n\n" . $foregroundDiv . "\n" );
231 }
232 }
233
234 /**
235 * Get the font link for loading document fonts
236 *
237 * @return string|null
238 */
239 public function getFontsLink()
240 {
241 if( is_null( $this->fontLink ) ){
242 $this->collectAndSetFontsData();
243 }
244 return $this->fontLink;
245 }
246
247 /**
248 * Render markup and collect styles of sections and nested elements.
249 */
250 protected function renderSectionsAndElements(){
251 foreach ( $this->sections as $section ) {
252 $this->html->nest( $section->render() . "\n" );
253 $this->stylesList = array_merge( $this->stylesList, $section->getCss() );
254 }
255 }
256
257 /**
258 * Collects fonts and generates a link for loading document fonts
259 */
260 protected function collectAndSetFontsData(){
261 if( !empty( $this->env['additionalFonts'] ) ){
262 // convert to array recursively
263 $this->env['additionalFonts'] = Data::cast( $this->env['additionalFonts'], 'array' );
264
265 if( is_array( $this->env['additionalFonts'] ) ){
266 foreach( $this->env['additionalFonts'] as $localFontName => $localFontInfo ){
267 \Depicter::documentFonts()->addLocalFont( $this->getDocumentID(), $localFontName, $localFontInfo['variants'] );
268 }
269 }
270 }
271
272 foreach ( $this->sections as $section ) {
273 // this method adds element fonts to documentFonts service
274 $section->collectElementFonts();
275 }
276 $this->fontLink = \Depicter::documentFonts()->getFontsLink( $this->getDocumentID() );
277 }
278
279 /**
280 * Render document custom styles
281 */
282 /**
283 * Get list of selector and CSS for section
284 *
285 * @return array
286 */
287 protected function getSelectorAndCssList(){
288 if( ! isset( $this->stylesList[ '.'. $this->getSelector() ] ) ){
289 $this->stylesList[ '.'. $this->getSelector() ] = [];
290 }
291
292 $documentStyles = [
293 '.'. $this->getStyleSelector() . ' .depicter-section' => $this->options->getSectionGeneralStyles(),
294 '.'. $this->getStyleSelector() . ' .depicter-layers-wrapper' => $this->options->getLayersWrapperStyles()
295 ];
296 // prepend document styles at the beginning of the styles
297 $this->stylesList = $documentStyles + $this->stylesList;
298
299 $this->stylesList[ '.'. $this->getStyleSelector() ]['customStyle'] = $this->getCustomStyles();
300
301 // add before init styles separately to style list as well
302 $this->stylesList[ '.'. $this->getStyleSelector() ]['beforeInitStyle'] = [
303 '.'. $this->getStyleSelector() => $this->options->getStyles(),
304 '.'. $this->getStyleSelector( true ) . ':not(.depicter-ready)' => $this->options->getBeforeInitStyles(), // styles to prevent FOUC. It should not have depicter-revert class in selector
305 ];
306
307 return $this->stylesList;
308 }
309
310 /**
311 * Retrieves StyleGeneratorService
312 *
313 * @param bool $args
314 *
315 * @return StyleGeneratorService
316 */
317 public function styleGenerator( $args = [] ){
318 $args = Arr::merge( $args, [
319 'forceRegenerateStyles' => false
320 ]);
321
322 if( ! $this->styleGenerator ){
323 $this->styleGenerator = new StyleGeneratorService( $this->stylesList, $this->getDocumentID(), $args );
324 }
325 if( $args['forceRegenerateStyles'] ){
326 $this->styleGenerator->setStylesList( $this->stylesList );
327 }
328
329 return $this->styleGenerator;
330 }
331
332 /**
333 * Saves generated css in file
334 *
335 * @param bool $forceRegenerateStyles Whether regenerate styles or not
336 */
337 public function saveCss( $forceRegenerateStyles = false ){
338 $this->styleGenerator( ['forceRegenerateStyles' => $forceRegenerateStyles] )->saveCss( $forceRegenerateStyles = false );
339 }
340
341 /**
342 * Retrieves generated css
343 *
344 * @param bool $forceRegenerateStyles Whether regenerate styles or not
345 *
346 * @return string css styles
347 */
348 public function getCss( $forceRegenerateStyles = false ){
349 return $this->styleGenerator( ['forceRegenerateStyles' => $forceRegenerateStyles] )->getCss( $forceRegenerateStyles = false );
350 }
351
352 /**
353 * Retrieves before init CSS
354 *
355 * @param bool $forceRegenerateStyles Whether regenerate before init styles or not
356 *
357 * @return string css styles
358 */
359 public function getBeforeInitCssAndTag( $forceRegenerateStyles = false ){
360 return $this->styleGenerator( ['forceRegenerateStyles' => $forceRegenerateStyles] )->getBeforeInitCssAndTag( $forceRegenerateStyles = false );
361 }
362
363 /**
364 * Retrieves generated css wrapper in a style tag
365 *
366 * @param bool $forceRegenerateStyles Whether regenerate styles or not
367 *
368 * @return string css styles and style tag
369 */
370 public function getInlineCssTag( $forceRegenerateStyles = false ){
371 return $this->styleGenerator( ['forceRegenerateStyles' => $forceRegenerateStyles] )->getCssAndTag( $forceRegenerateStyles = false );
372 }
373
374 /**
375 * Retrieves custom css file of a document if exists
376 *
377 * @param bool $forceRegenerateStyles Whether regenerate styles or not
378 *
379 * @return bool|string
380 */
381 public function getCssFileUrl( $forceRegenerateStyles = false ){
382 if( $forceRegenerateStyles ){
383 $this->saveCss( $forceRegenerateStyles );
384 }
385 if( $cssFile = $this->styleGenerator()->getCssFileUrl() ){
386 return $cssFile;
387 }
388
389 return false;
390 }
391
392 /**
393 * Generates all CSS classes of document wrapper tag
394 *
395 * @return string
396 */
397 protected function getClassNames() {
398 $classes = [ Selector::PREFIX_NAME, Selector::prefixify( Selector::DOCUMENT_PREFIX ), Selector::prefixify( 'revert' ) ];
399 $classes[] = $this->getSelector();
400
401 if ( $this->options->sectionLayout == 'fullscreen' ) {
402 $classes[] = 'depicter-layout-fullscreen';
403 }
404
405 if ( $this->options->sectionLayout == 'fullwidth' ) {
406 $classes[] = 'depicter-layout-fullwidth';
407 }
408
409 if ( $this->getCustomClassName() ) {
410 $classes[] = $this->getCustomClassName();
411 }
412
413 if ( isset( $this->options->general->visible->default ) && $this->options->general->visible->default === false ) {
414 $classes[] = 'depicter-hide-on-desktop';
415 }
416
417 if ( isset( $this->options->general->visible->tablet ) && $this->options->general->visible->tablet === false ) {
418 $classes[] = 'depicter-hide-on-tablet';
419 }
420
421 if ( isset( $this->options->general->visible->mobile ) && $this->options->general->visible->mobile === false ) {
422 $classes[] = 'depicter-hide-on-mobile';
423 }
424
425 return implode( ' ', $classes );
426 }
427
428 protected function getCssId(){
429 return Selector::getFullSelectorPath( $this->getDocumentID() );
430 }
431
432 /**
433 * Retrieves custom class name of document
434 *
435 * @return string
436 */
437 protected function getCustomClassName(){
438 if ( ! empty( $this->options->advanced->className ) ) {
439 return $this->options->advanced->className;
440 }
441 return '';
442 }
443
444 /**
445 * Retrieves custom styles of document
446 *
447 * @return string
448 */
449 protected function getCustomStyles(){
450 if ( ! empty( $this->options->advanced->customStyle ) ) {
451 $customStyles = $this->options->advanced->customStyle;
452 // replace "selector" with unique selector of document
453 return str_replace('selector', '.'.$this->getStyleSelector(), $customStyles );
454 }
455 return '';
456 }
457
458 /**
459 * Retrieves list of all generated custom css files
460 *
461 * @param array|string $fileKeysToInclude Array of file keys or 'all', '*' to retrieve all
462 *
463 * @return array
464 */
465 public function getCustomCssFiles( $fileKeysToInclude = 'all' )
466 {
467 $documentCustomStyles = [];
468
469 if( is_string( $fileKeysToInclude ) && in_array( $fileKeysToInclude, [ 'all', '*'] ) ){
470 $fileKeysToInclude = ['google-font', 'custom'];
471 }
472
473 if( in_array('google-font', $fileKeysToInclude) && $fontLink = $this->getFontsLink() ){
474 $documentCustomStyles[ "depicter-{$this->getDocumentID()}-google-font" ] = $fontLink;
475 }
476 if( in_array('custom', $fileKeysToInclude) && $customCssFileUrl = $this->getCssFileUrl() ){
477 $documentCustomStyles[ "depicter--{$this->getDocumentID()}-custom" ] = $customCssFileUrl;
478 }
479
480 return $documentCustomStyles;
481 }
482
483 /**
484 * Retrieves unique selector of document
485 *
486 * @return string
487 */
488 public function getSelector(){
489 return Selector::getUniqueSelector( $this->getDocumentID() );
490 }
491
492 /**
493 * Get style selector
494 *
495 * @param bool $excludePrefix Whether to exclude selector prefix or not
496 *
497 * @return string
498 */
499 public function getStyleSelector( $excludePrefix = false ) {
500 return ( $excludePrefix ? '' : Selector::PREFIX_CSS . "." ) . $this->getSelector();
501 }
502
503 /**
504 * Order sections based on sectionsList
505 */
506 protected function reorderSections(){
507 $ordered_sections = [];
508 foreach ( $this->sectionsList as $section_id ) {
509 if( isset( $this->sections[ $section_id ] ) ){
510 $ordered_sections[ $section_id ] = $this->sections[ $section_id ];
511 }
512 }
513 $this->sections = $ordered_sections;
514 }
515
516 /**
517 * Assigns belonging elements of all section
518 * Here Objects can be element or section
519 */
520 protected function setElementsForObjects(){
521 foreach ( $this->sections as $section ){
522 // Assign document ID to all sections
523 $section->setDocumentID( $this->getDocumentID() );
524 $this->setElementsForOneObjects( $section );
525 }
526
527 foreach ( $this->elements as $element ){
528 // Assign document ID to all elements
529 $element->setDocumentID( $this->getDocumentID() );
530 $this->setElementsForOneObjects( $element );
531 }
532 }
533
534 /**
535 * Assigns belonging elements of a section
536 *
537 * @param $object
538 */
539 protected function setElementsForOneObjects( &$object ){
540 if ( $elementIds = $object->getElementIds() ) {
541 $elements = $this->sortElementsByDepth( $elementIds );
542 $object->setElementObjects( $elements );
543 }
544 }
545
546 /**
547 * Assigns belonging elements of a section
548 */
549 protected function setForegroundElementObjects(){
550 if ( $elementIds = $this->foregroundElements ) {
551 $this->foregroundElementObjects = $this->sortElementsByDepth( $elementIds );
552 }
553 }
554
555 /**
556 * Sorts elements by depth
557 *
558 * @param array $elementIds
559 *
560 * @return array|Element[]
561 */
562 protected function sortElementsByDepth( $elementIds ){
563 if( empty( $elementIds ) ){
564 return [];
565 }
566
567 $elementsByDepth = [];
568 $elements = [];
569
570 // sort this group of elements in depth
571 foreach ( $elementIds as $elementId ) {
572 $element = $this->elements[ $elementId ];
573 $elementsByDepth[ $element->depth ][] = $element;
574 }
575
576 // sort by ascending depth
577 ksort( $elementsByDepth );
578
579 // collect these elements by depth
580 foreach ( $elementsByDepth as $elementsInADepth ){
581 foreach ( $elementsInADepth as $element ){
582 $elements[] = $element;
583 }
584 }
585
586 return $elements;
587 }
588
589 /**
590 * @return array
591 */
592 public function getMeta()
593 {
594 return $this->meta ?? [];
595 }
596
597 /**
598 * @return Options
599 */
600 public function getOptions()
601 {
602 return $this->options ?? [];
603 }
604
605 /**
606 * Get teh name of document
607 *
608 * @return string
609 */
610 public function getName()
611 {
612 return isset( $this->meta['name'] ) ? $this->meta['name'] : '';
613 }
614
615 /**
616 * Get document slug
617 *
618 * @return mixed|string
619 */
620 public function getSlug()
621 {
622 return isset( $this->meta['slug'] ) ? $this->meta['slug'] : '';
623 }
624
625 /**
626 * Get sections count
627 *
628 * @return int|void
629 */
630 public function getSectionsCount()
631 {
632 return is_array( $this->sectionsList ) ? count( $this->sectionsList ) : 0;
633 }
634
635 /**
636 * Gel list of section objects
637 *
638 * @return Section[]
639 */
640 public function getSections()
641 {
642 return $this->sections ?? [];
643 }
644
645 /**
646 * Get a section object by section ID
647 *
648 * @param string $sectionId The section ID
649 *
650 * @return Section|null
651 */
652 public function getSectionById( $sectionId )
653 {
654 return $this->sections[ $sectionId ] ?? null;
655 }
656
657 /**
658 * Get a section by section number. starts from 1
659 *
660 * @param int $sectionNumber The section number
661 *
662 * @return Section|null
663 */
664 public function getSectionNth( $sectionNumber )
665 {
666 if( ! $this->getSections() ){
667 return null;
668 }
669
670 $sectionIndex = $sectionNumber > 0 ? $sectionNumber - 1 : 1;
671 return array_values( $this->getSections() )[ $sectionIndex ] ?? null;
672 }
673
674 /**
675 * Get init script
676 *
677 * @return string
678 */
679 public function getInitScript() {
680 return (new Script())->getDocumentInitScript( $this );
681 }
682
683 /**
684 * Get init script tag
685 */
686 public function getInitScriptTag() {
687 return "\n" . Html::script( [], $this->getInitScript() );
688 }
689
690 /**
691 * Print init script tag
692 */
693 public function printInitScriptTag() {
694 echo Sanitize::html( $this->getInitScriptTag() );
695 }
696
697 }
698