PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 2.1.8
Page Builder: Pagelayer – Drag and Drop website builder v2.1.8
2.2.1 2.2.0 2.1.9 2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 trunk 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 1.0.0 1.0.2 1.0.3 1.0.4 1.0.5 All 129 releases
pagelayer / main / functions.php

functions.php in Page Builder: Pagelayer – Drag and Drop website builder 2.1.8, at main/functions.php

4,439 lines 110.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 //////////////////////////////////////////////////////////////
4 //===========================================================
5 // class.php
6 //===========================================================
7 // PAGELAYER
8 // Inspired by the DESIRE to be the BEST OF ALL
9 // ----------------------------------------------------------
10 // Started by: Pulkit Gupta
11 // Date: 23rd Jan 2017
12 // Time: 23:00 hrs
13 // Site: http://pagelayer.com/wordpress (PAGELAYER)
14 // ----------------------------------------------------------
15 // Please Read the Terms of use at http://pagelayer.com/tos
16 // ----------------------------------------------------------
17 //===========================================================
18 // (c)Pagelayer Team
19 //===========================================================
20 //////////////////////////////////////////////////////////////
21
22 // Are we being accessed directly ?
23 if(!defined('PAGELAYER_VERSION')) {
24 exit('Hacking Attempt !');
25 }
26
27 // Get the client IP
28 function _pagelayer_getip(){
29 if(isset($_SERVER["REMOTE_ADDR"])){
30 return $_SERVER["REMOTE_ADDR"];
31 }elseif(isset($_SERVER["HTTP_X_FORWARDED_FOR"])){
32 return $_SERVER["HTTP_X_FORWARDED_FOR"];
33 }elseif(isset($_SERVER["HTTP_CLIENT_IP"])){
34 return $_SERVER["HTTP_CLIENT_IP"];
35 }
36 }
37
38 // Get the client IP
39 function pagelayer_getip(){
40
41 global $pagelayer;
42
43 // Just so that we have something
44 $ip = _pagelayer_getip();
45 $method = @$pagelayer->ip_method;
46
47 $pagelayer->ip_method = (int) $method;
48
49 if(isset($_SERVER["REMOTE_ADDR"])){
50 $ip = $_SERVER["REMOTE_ADDR"];
51 }
52
53 if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && $method == 1){
54 $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
55 }
56
57 if(isset($_SERVER["HTTP_CLIENT_IP"]) && $method == 2){
58 $ip = $_SERVER["HTTP_CLIENT_IP"];
59 }
60
61 // Hacking fix for X-Forwarded-For
62 if(!pagelayer_valid_ip($ip)){
63 return '';
64 }
65
66 return $ip;
67
68 }
69
70 // Execute a select query and return an array
71 function pagelayer_selectquery($query, $array = 0){
72 global $wpdb;
73
74 $result = $wpdb->get_results($query, 'ARRAY_A');
75
76 if(empty($array)){
77 return current($result);
78 }else{
79 return $result;
80 }
81 }
82
83 // Check if an IP is valid
84 function pagelayer_valid_ip($ip){
85
86 // IPv6
87 if(pagelayer_valid_ipv6($ip)){
88 return true;
89 }
90
91 // IPv4
92 if(!ip2long($ip)){
93 return false;
94 }
95
96 return true;
97 }
98
99 function pagelayer_valid_ipv6($ip){
100
101 $pattern = '/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/';
102
103 if(!preg_match($pattern, $ip)){
104 return false;
105 }
106
107 return true;
108
109 }
110
111 // Check if a field is posted via POST else return default value
112 function pagelayer_optpost($name, $default = ''){
113
114 if(!empty($_POST[$name])){
115 return pagelayer_inputsec(pagelayer_htmlizer(trim($_POST[$name])));
116 }
117
118 return $default;
119 }
120
121 // Check if a field is posted via GET else return default value
122 function pagelayer_optget($name, $default = ''){
123
124 if(!empty($_GET[$name])){
125 return pagelayer_inputsec(pagelayer_htmlizer(trim($_GET[$name])));
126 }
127
128 return $default;
129 }
130
131 // Check if a field is posted via GET or POST else return default value
132 function pagelayer_optreq($name, $default = ''){
133
134 if(!empty($_REQUEST[$name])){
135 return pagelayer_inputsec(pagelayer_htmlizer(trim($_REQUEST[$name])));
136 }
137
138 return $default;
139 }
140
141 // For filling in posted values
142 function pagelayer_POSTval($name, $default = ''){
143
144 return (!empty($_POST) ? (!isset($_POST[$name]) ? '' : esc_html($_POST[$name])) : $default);
145
146 }
147
148 function pagelayer_POSTchecked($name, $default = false){
149
150 return (!empty($_POST) ? (isset($_POST[$name]) ? 'checked="checked"' : '') : (!empty($default) ? 'checked="checked"' : ''));
151
152 }
153
154 // For check isset value
155 function pagelayer_isset($var, $name, $default = ''){
156
157 return isset($var[$name]) ? $var[$name] : $default;
158
159 }
160
161 function pagelayer_POSTselect($name, $value, $default = false){
162
163 if(empty($_POST)){
164 if(!empty($default)){
165 return 'selected="selected"';
166 }
167 }else{
168 if(isset($_POST[$name])){
169 if(trim($_POST[$name]) == $value){
170 return 'selected="selected"';
171 }
172 }
173 }
174
175 }
176
177 function pagelayer_inputsec($string){
178
179 $string = addslashes($string);
180
181 // This is to replace ` which can cause the command to be executed in exec()
182 $string = str_replace('`', '\`', $string);
183
184 return $string;
185
186 }
187
188 function pagelayer_htmlizer($string){
189
190 $string = htmlentities($string, ENT_QUOTES, 'UTF-8');
191
192 preg_match_all('/(&amp;#(\d{1,7}|x[0-9a-fA-F]{1,6});)/', $string, $matches);//r_print($matches);
193
194 foreach($matches[1] as $mk => $mv){
195 $tmp_m = pagelayer_entity_check($matches[2][$mk]);
196 $string = str_replace($matches[1][$mk], $tmp_m, $string);
197 }
198
199 return $string;
200
201 }
202
203 function pagelayer_entity_check($string){
204
205 //Convert Hexadecimal to Decimal
206 $num = ((substr($string, 0, 1) === 'x') ? hexdec(substr($string, 1)) : (int) $string);
207
208 //Squares and Spaces - return nothing
209 $string = (($num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num < 0x20) ? '' : '&#'.$num.';');
210
211 return $string;
212
213 }
214
215 // Check if a checkbox is selected
216 function pagelayer_is_checked($post){
217
218 if(!empty($_POST[$post])){
219 return true;
220 }
221 return false;
222 }
223
224 // Report an error
225 function pagelayer_report_error($error = array()){
226
227 if(empty($error)){
228 return true;
229 }
230
231 $error_string = '<b>Please fix the below error(s) :</b> <br />';
232
233 foreach($error as $ek => $ev){
234 $error_string .= '* '.$ev.'<br />';
235 }
236
237 echo '<div id="message" class="error"><p>'
238 . __pl($error_string)
239 . '</p></div>';
240 }
241
242 // Report a notice
243 function pagelayer_report_notice($notice = array()){
244
245 global $wp_version;
246
247 if(empty($notice)){
248 return true;
249 }
250
251 // Which class do we have to use ?
252 if(version_compare($wp_version, '3.8', '<')){
253 $notice_class = 'updated';
254 }else{
255 $notice_class = 'updated';
256 }
257
258 $notice_string = '<b>Please check the below notice(s) :</b> <br />';
259
260 foreach($notice as $ek => $ev){
261 $notice_string .= '* '.$ev.'<br />';
262 }
263
264 echo '<div id="message" class="'.$notice_class.'"><p>'
265 . __pl($notice_string)
266 . '</p></div>';
267 }
268
269 // Convert an objext to array
270 function pagelayer_objectToArray($d){
271
272 if(is_object($d)){
273 $d = get_object_vars($d);
274 }
275
276 if(is_array($d)){
277 return array_map(__FUNCTION__, $d); // recursive
278 }elseif(is_object($d)){
279 return pagelayer_objectToArray($d);
280 }else{
281 return $d;
282 }
283 }
284
285 // Sanitize variables
286 function pagelayer_sanitize_variables($variables = array()){
287
288 if(is_array($variables)){
289 foreach($variables as $k => $v){
290 $variables[$k] = trim($v);
291 $variables[$k] = escapeshellcmd($v);
292 }
293 }else{
294 $variables = escapeshellcmd(trim($variables));
295 }
296
297 return $variables;
298 }
299
300 // Is multisite ?
301 function pagelayer_is_multisite() {
302
303 if(function_exists('get_site_option') && function_exists('is_multisite') && is_multisite()){
304 return true;
305 }
306
307 return false;
308 }
309
310 // Generate a random string
311 function pagelayer_RandomString($length = 10){
312 $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
313 $charactersLength = strlen($characters);
314 $randomString = '';
315 for($i = 0; $i < $length; $i++){
316 $randomString .= $characters[rand(0, $charactersLength - 1)];
317 }
318 return $randomString;
319 }
320
321 function pagelayer_print($array){
322
323 echo '<pre>';
324 print_r($array);
325 echo '</pre>';
326
327 }
328
329 function pagelayer_cleanpath($path){
330 $path = str_replace('\\\\', '/', $path);
331 $path = str_replace('\\', '/', $path);
332 $path = str_replace('//', '/', $path);
333 return rtrim($path, '/');
334 }
335
336 // Returns the Numeric Value of results Per Page
337 function pagelayer_get_page($get = 'page', $resperpage = 50){
338
339 $resperpage = (!empty($_REQUEST['reslen']) && is_numeric($_REQUEST['reslen']) ? (int) pagelayer_optreq('reslen') : $resperpage);
340
341 if(pagelayer_optget($get)){
342 $pg = (int) pagelayer_optget($get);
343 $pg = $pg - 1;
344 $page = ($pg * $resperpage);
345 $page = ($page <= 0 ? 0 : $page);
346 }else{
347 $page = 0;
348 }
349 return $page;
350 }
351
352 // Are we editing from the Admin panel ?
353 function pagelayer_is_editing($force = false){
354
355 global $post, $pagelayer;
356
357 if(!empty($force)){
358 return true;
359 }
360
361 if(!is_admin()){
362 return false;
363 }
364
365 $current_file = basename($_SERVER['PHP_SELF']);
366
367 $type = get_post_type();
368 //echo $type;return false;
369 //$page = pagelayer_optreq('page');
370
371 // Are we in the live editor mode OR is this a post which is supported
372 if((pagelayer_supported_type($type) && in_array($current_file, array('post.php', 'post-new.php'))) || pagelayer_is_live()){
373 return true;
374 }else{
375 return false;
376 }
377
378 }
379
380 // Is the given post type editable by us ?
381 function pagelayer_supported_type($type){
382
383 global $pagelayer;
384
385 $type = trim($type);
386
387 if(in_array($type, $pagelayer->settings['post_types'])){
388 return true;
389 }
390
391 if($type == $pagelayer->builder['name']){
392 return true;
393 }
394
395 return false;
396
397 }
398
399 function pagelayer_shortlink($id){
400
401 $post = get_post( $id );
402 if ( ! empty( $post->ID ) ) {
403 $post_id = $post->ID;
404 }
405
406 $post_type = get_post_type_object( $post->post_type );
407
408 if ( 'page' === $post->post_type && get_option( 'page_on_front' ) == $post->ID && 'page' === get_option( 'show_on_front' ) ) {
409 $link = home_url( '/' );
410 } elseif ( $post_type->public ) {
411 $link = home_url( '?p=' . $post_id );
412 }
413
414 if(function_exists('is_post_status_viewable') && !is_post_status_viewable($post_id)){
415 $link = get_permalink( $post->ID );
416 }
417
418 $link .= substr_count($link, '?') > 0 ? '' : '?';
419 return $link;
420 }
421
422 // Pagelayer live link
423 function pagelayer_livelink($id){
424 return pagelayer_shortlink($id).'&pagelayer-live=1';
425 }
426
427 // Are we in live mode ?
428 function pagelayer_is_live(&$error = array()){
429
430 global $post;
431
432 // Are we seeing the post ?
433 if(!isset($post) || !isset($post->ID) || empty($post->ID)){
434 $error[] = 'Post ID is missing or blank - '.@$post->ID;
435 return false;
436 }
437
438 $parID = $post->ID;
439
440 // Is revision?
441 if(wp_is_post_revision($post->ID) ){
442 $parID = wp_get_post_parent_id($post->ID);
443 }
444
445 // Are you allowed to edit ?
446 if(!pagelayer_user_can_edit($parID)){
447 $error[] = 'You dont have editing rights for this page - '.$parID;
448 return false;
449 }
450
451 // Is it the live mode ?
452 if(pagelayer_optreq('pagelayer-live')){
453 $error[] = 'pagelayer-live is missing';
454 return true;
455 }
456
457 return false;
458
459 }
460
461 // Are we in live IFRAME mode ?
462 function pagelayer_is_live_iframe(&$error = array()){
463
464 // Are we seeing the post ?
465 if(!pagelayer_is_live($error)){
466 return false;
467 }
468
469 // Is it the live mode ?
470 if(pagelayer_optreq('pagelayer-iframe')){
471 return true;
472 }
473
474 $error[] = 'pagelayer-iframe missing in GET';
475
476 return false;
477
478 }
479
480 // Are we editing a live template
481 function pagelayer_is_live_template($post = []){
482
483 // Are we seeing the post ?
484 if(!pagelayer_is_live()){
485 return false;
486 }
487
488 if(!$post){
489 $post = $GLOBALS['post'];
490 }
491
492 if($post->post_type == 'pagelayer-template'){
493 return true;
494 }
495
496 return false;
497
498 }
499
500 function pagelayer_has_blocks($post = null) {
501 if ( ! has_blocks( $post ) ) {
502 return false;
503 }
504
505 if ( ! is_string( $post ) ) {
506 $wp_post = get_post( $post );
507 if ( $wp_post instanceof WP_Post ) {
508 $post = $wp_post->post_content;
509 }
510 }
511
512 return false !== strpos( $post, '<!-- wp:pagelayer/' );
513 }
514
515 // Can the current user edit the post ?
516 function pagelayer_user_can_edit($post = NULL){
517
518 global $wp_the_query, $current_user, $pagelayer;
519
520 $post = get_post($post);
521
522 if(empty($post)){
523 return false;
524 }
525
526 // No trash editing
527 if(get_post_status($post->ID) === 'trash'){
528 return false;
529 }
530
531 // Is pagelayer supposed to edit this ?
532 if(!pagelayer_supported_type($post->post_type)){
533 return false;
534 }
535
536 // Get the post type object
537 $object = get_post_type_object($post->post_type);
538
539 // Is this type editable by the user ?
540 if(!current_user_can($object->cap->edit_posts)){
541 return false;
542 }
543
544 // Is this type editable ?
545 if(!isset($object->cap->edit_post)){
546 return false;
547 }
548
549 // Can this user edit the post type ?
550 if(!current_user_can($object->cap->edit_post, $post->ID)){
551 return false;
552 }
553
554 // Page for blogs not allowed
555 if(get_option('page_for_posts') === $post->ID){
556 return false;
557 }
558
559 return true;
560
561 }
562
563 // Language sting function
564 function __pl($key){
565
566 global $pagelayer;
567
568 if(!empty($pagelayer->l[$key])){
569 return $pagelayer->l[$key];
570 }
571
572 return $key;
573
574 }
575
576 // Give the list of icon sources
577 function pagelayer_icon_sources(){
578 return array();
579 }
580
581 // Sets the memory limit
582 function pagelayer_memory_limit($mb){
583
584 $bytes = ($mb * 1024 * 1024);
585 $mb_str = (string) $mb.'M';
586
587 // Some servers might have ini_get disabled
588 if(function_exists('ini_get')){
589 $memory_limit = @ini_get('memory_limit');
590 }
591
592 if(empty($memory_limit)){
593 return;
594 }
595
596 $memory_limit_bytes = (strpos($memory_limit, 'M') ? (intval($memory_limit) * 1024 * 1024) : intval($memory_limit));
597
598 //$memory_limit_bytes > 0 is for memory limit = -1
599 if($memory_limit_bytes <= $bytes && $memory_limit_bytes > 0){
600
601 // Some servers might have ini_set disabled
602 if(function_exists('ini_set')){
603 @ini_set('memory_limit', $mb_str);
604 }
605 }
606
607 }
608
609 // Pagelayer the content
610 function pagelayer_the_content($content, $dump = false){
611 global $pagelayer;
612
613 $content = pagelayer_sanitize_content( $content );
614 $content = do_blocks( $content );
615 $content = do_shortcode( $content );
616
617 if($dump){
618 preg_match_all('/<div ([^>]*)pagelayer-id="([^"]*)"([^>]*)>/', $content, $matches);
619
620 foreach($matches[0] as $k => $div){
621 $id = $matches[2][$k];
622
623 if(empty($pagelayer->data_attr[$id])){
624 continue;
625 }
626
627 $data_attr = '<!-- '.pagelayer_serialize_attributes($pagelayer->data_attr[$id]).' -->';
628 $content = str_replace($div, $div.$data_attr, $content);
629 }
630 }
631
632 return $content;
633 }
634
635 function pagelayer_create_id(){
636 return pagelayer_RandomString(3).rand(1000, 9999);
637 }
638
639 // Loads the shortcodes
640 function pagelayer_load_shortcodes(){
641 global $pagelayer, $post;
642
643 if(!empty($pagelayer->shortcode_loaded)){
644 return;
645 }
646
647 pagelayer_memory_limit(128);
648
649 // We have loaded
650 $pagelayer->shortcode_loaded = 1;
651
652 do_action('pagelayer_before_load_shortcodes');
653
654 // pQuery
655 include_once(PAGELAYER_DIR.'/lib/pquery/IQuery.php');
656 include_once(PAGELAYER_DIR.'/lib/pquery/gan_formatter.php');
657 include_once(PAGELAYER_DIR.'/lib/pquery/gan_node_html.php');
658 include_once(PAGELAYER_DIR.'/lib/pquery/gan_tokenizer.php');
659 include_once(PAGELAYER_DIR.'/lib/pquery/gan_parser_html.php');
660 include_once(PAGELAYER_DIR.'/lib/pquery/gan_selector_html.php');
661 include_once(PAGELAYER_DIR.'/lib/pquery/gan_xml2array.php');
662 include_once(PAGELAYER_DIR.'/lib/pquery/pQuery.php');
663
664 include_once(PAGELAYER_DIR.'/main/shortcode_functions.php');
665
666 // Apply filter to load custom widgets functions
667 do_action('pagelayer_load_shortcode_functions');
668
669 include_once(PAGELAYER_DIR.'/main/shortcodes.php');
670
671 // Apply filter to load custom widgets
672 do_action('pagelayer_load_custom_widgets');
673
674 // Render Pagelayer element by blocks
675 add_action('pre_render_block', 'pagelayer_render_blocks', 10, 2);
676
677 // Add global widget data
678 if(defined('PAGELAYER_PREMIUM') && !pagelayer_is_gutenberg_editor()){
679
680 // Get global widget templates id by type
681 $args = [
682 'post_type' => $pagelayer->builder['name'],
683 'status' => 'publish',
684 'meta_key' => 'pagelayer_template_type',
685 'meta_value' => array('global_widget', 'section', 'global_section'),
686 'posts_per_page' => -1
687 ];
688
689 $query = new WP_Query($args);
690
691 $tmp_list = [];
692 $global_widgets = array();
693 $global_widgets['global_widget'] = array();
694 $global_widgets['section'] = array();
695 $global_widgets['global_section'] = array();
696
697 foreach($query->posts as $template){
698
699 // The type
700 $pagelayer_template_type = get_post_meta($template->ID, 'pagelayer_template_type', true);
701
702 $global_data = [];
703 $global_data['post_id'] = $template->ID;
704 $global_data['title'] = $template->post_title;
705 $global_data['$'] = pagelayer_the_content($template->post_content, true);
706 $global_widgets[$pagelayer_template_type][$template->ID] = $global_data;
707
708 }
709
710 $pagelayer->global_widgets = $global_widgets['global_widget'];
711 $pagelayer->saved_sections = $global_widgets['section'];
712 $pagelayer->global_sections = $global_widgets['global_section'];
713 }
714
715 do_action('pagelayer_after_load_shortcodes');
716 }
717
718 // Add the shortcodes to the pagelayer list
719 function pagelayer_add_shortcode($tag, $params = array()){
720
721 global $pagelayer, $post;
722
723 if($tag == 'pl_row'){
724 $inner_tag = 'pl_inner_row';
725 add_shortcode($inner_tag, 'pagelayer_render_shortcode');
726 }
727
728 if($tag == 'pl_col'){
729 $inner_tag = 'pl_inner_col';
730 add_shortcode($inner_tag, 'pagelayer_render_shortcode');
731 }
732
733 add_shortcode($tag, 'pagelayer_render_shortcode');//$params['func']);
734 //unset($params['func']);
735
736 // Is there a group ?
737 if(empty($params['group'])){
738 $params['group'] = 'misc';
739 }
740
741 // Add the advanced styling group
742 $params['options'] = [
743 'ele_bg_styles' => __pl('ele_bg_styles'),
744 'ele_styles' => __pl('ele_styles'),
745 'border_styles' => __pl('border_styles'),
746 'font_style' => __pl('font_style'),
747 'position_styles' => __pl('position_styles'),
748 'animation_styles' => __pl('animation_styles'),
749 'motion_effects' => __pl('Motion Effects'),
750 'responsive_styles' => __pl('responsive_styles'),
751 'attributes' => __pl('attributes'),
752 'custom_styles' => __pl('custom_styles'),
753 ];
754
755 if(!empty($params['skip_props_cat'])){
756 foreach($params['skip_props_cat'] as $k => $v){
757 unset($params['options'][$v]);
758 }
759 }
760
761 // Are the settings there which hold the params ?
762 if(empty($params['settings'])){
763 $params['settings'] = [
764 'params' => $params['name'],
765 ];
766 }
767
768 // Disable the style options
769 if(!empty($params['styles'])){
770 $params['settings'] = array_merge($params['settings'], $params['styles']);
771 unset($params['styles']);
772 }
773
774 /*// The following is for testing only
775 $r = [];
776 foreach($pagelayer->styles as $k => $v){
777 foreach($v as $kk => $vv){
778 $r[$kk] = $kk;
779 }
780 }
781 //print_r($r);die();
782
783 foreach($params['settings'] as $k => $v){
784 if(empty($params[$k])) continue;
785 foreach($params[$k] as $kk => $vv){
786 if(!empty($r[$kk])){
787 echo 'Duplicate KEY '.$kk.' in Shortcode '.$tag."<br>";
788 }
789 }
790 }
791 //die();*/
792
793 $params = apply_filters( 'pagelayer_shortcode_params', $params, $tag );
794
795 // Insert the shortcode
796 $pagelayer->shortcodes[$tag] = $params;
797 $pagelayer->groups[$params['group']][] = $tag;
798
799 // Export the default values
800 foreach($pagelayer->tabs as $tab){
801
802 if(empty($pagelayer->shortcodes[$tag][$tab])){
803 continue;
804 }
805
806 foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
807
808 $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
809
810 //echo $tab.' - '.$section.' - <br>';
811
812 if(empty($props)){
813 continue;
814 }
815
816 // Save customizer params
817 if( $tag == 'pl_customizer' ){
818 $pagelayer->customizer_params = array_merge($pagelayer->customizer_params, $props);
819 }
820
821 foreach($props as $prop => $param){
822
823 // Set default values to export for JS
824 if(isset($param['export-def']) && isset($param['default']) && $param['export-def'] == 1){
825 $pagelayer->default_params[$tag][$prop] = $param['default'];
826 }
827
828 }
829 }
830 }
831 }
832
833 // Add a freemium shortcode i.e. available for render, but not to drag or edit
834 function pagelayer_freemium_shortcode($tag, $params = array()){
835
836 // If we are the free version, we just allow render and some edits
837 if(!defined('PAGELAYER_PREMIUM')){
838
839 $params['not_visible'] = 1;
840 $params['freemium'] = 1;
841
842 $cats = empty($params['styles']) ? array() : $params['styles'];
843
844 if(!empty($params['settings'])){
845 $cats = array_merge($cats, $params['settings']);
846 }
847
848 $cats['params'] = $params['name'];
849 //pagelayer_print($cats);
850
851 foreach($cats as $k => $v){
852 if(empty($params[$k])) continue;
853
854 foreach($params[$k] as $kk => $vv){
855
856 if(empty($params[$k][$kk]['np'])){
857 $params[$k][$kk]['pro'] = 1;
858 }
859
860 }
861
862 }
863
864 }
865
866 return pagelayer_add_shortcode($tag, $params);
867 }
868
869 // Returns the permalink values
870 function pagelayer_permalink($id){
871
872 if(is_numeric($id)){
873 $id = (int) @$id;
874 $perma = get_permalink($id);
875
876 if(!empty($perma)){
877 $id = $perma;
878 }
879 }
880
881 $id = apply_filters('pagelayer_permalink', $id);
882
883 return $id;
884 }
885
886 // Returns the Image values
887 function pagelayer_image($id = 0){
888
889 global $pagelayer;
890
891 $ret = [];
892
893 if(!empty($id) && is_array($id)){
894
895 foreach($id as $key => $image){
896 $attachment = pagelayer_image(@$image);
897 if(!empty($attachment)){
898 foreach($attachment as $k => $v){
899 if($key == 'retina'){
900 $ret['retina-'.$k] = $v;
901 }else if($key == 'retina_mobile'){
902 $ret['retina-mobile-'.$k] = $v;
903 }else{
904 $ret[$k] = $v;
905 }
906 }
907 }
908 }
909
910 return $ret;
911 }
912
913 // External image ?
914 if(pagelayer_is_external_img($id)){
915
916 $ret['url'] = $id;
917
918 // Attachment
919 }elseif(!empty($id)){
920
921 $id = (int) @$id;
922
923 $image = get_post($id);
924
925 // Is there an attachment which is an image ?
926 if(!empty($image) && $image->post_type == 'attachment' && wp_attachment_is_image($id)){
927
928 // Need to export necessary media
929 if(!empty($pagelayer->export_mode)){
930 $pagelayer->media_to_export[] = $id;
931 }
932
933 $sizes = get_intermediate_image_sizes();
934 array_unshift($sizes, 'full');
935
936 foreach($sizes as $size){
937 $src = wp_get_attachment_image_src($id, $size);
938 $ret[$size.'-url'] = $src[0];
939 }
940
941 // Title and Alt
942 $title = esc_attr($image->post_title);
943 $alt = get_post_meta($id, '_wp_attachment_image_alt', true);
944 $alt = empty($alt) ? $image->post_excerpt : $alt;
945 $alt = empty($alt) ? $image->post_title : $alt;
946 $alt = empty($alt) ? '' : esc_attr(trim(strip_tags($alt)));
947 $link = get_attachment_link($id);
948 $caption = wp_get_attachment_caption($id);
949 $caption = !empty($caption) ? esc_attr($caption) : '';
950
951 }
952
953 }
954
955 // First preference to full url
956 if(!empty($ret['full-url'])){
957 $ret['url'] = $ret['full-url'];
958 }
959
960 // No image
961 if(empty($ret['url'])){
962 $ret['url'] = PAGELAYER_URL.'/images/default-image.png';
963 }
964
965 // Extract filename without extension
966 $filename = pathinfo($ret['url'], PATHINFO_FILENAME);
967
968 // Skip title if same as filename
969 $title = (!empty($title) && $title != $filename) ? $title : '';
970
971 $ret['alt'] = @$alt;
972 $ret['title'] = @$title;
973 $ret['link'] = @$link;
974 $ret['caption'] = @$caption;
975
976 $ret = apply_filters('pagelayer_image', $ret);
977
978 if(pagelayer_is_default_img($ret['url'])){
979 $ret['no-image-set'] = 1;
980 }
981
982 return $ret;
983
984 }
985
986 // Checks if the given parameter is an external link or a wp attachment id
987 function pagelayer_is_external_img($img = ''){
988
989 if(empty($img)){
990 return false;
991 }
992
993 if(preg_match('#http://#is', $img) || preg_match('#https://#is', $img) || preg_match('#^{{#is', $img)){
994 return true;
995 }
996
997 return false;
998
999 }
1000
1001 // Checks if the given parameter is the default image
1002 function pagelayer_is_default_img($img){
1003
1004 if($img == PAGELAYER_URL.'/images/default-image.png'){
1005 return true;
1006 }
1007
1008 return false;
1009
1010 }
1011
1012 // Returns the attachment url
1013 function pagelayer_attachment($id){
1014
1015 $ret = [];
1016
1017 // External url ?
1018 if(pagelayer_is_external_img($id)){
1019
1020 $ret['url'] = $id;
1021
1022 // Attachment
1023 }elseif(!empty($id)){
1024
1025 // Need to export necessary media
1026 if(!empty($pagelayer->export_mode)){
1027 $pagelayer->media_to_export[] = $id;
1028 }
1029
1030 $ret['url'] = wp_get_attachment_url($id);
1031
1032 }
1033
1034 $ret = apply_filters('pagelayer_attachment', $ret);
1035
1036 return $ret;
1037
1038 }
1039
1040 // Convert the regular URL of a Video to a Embed URL
1041 // Todo : Check
1042 function pagelayer_video_url($source, $no_url = false){
1043
1044 global $pagelayer;
1045
1046 if (!empty($source)) {
1047
1048 $source = esc_url( $source );
1049 $source = str_replace('&amp;', '&', $source);
1050 $url = parse_url($source);
1051 $videoSite ='';
1052 $videoId ='';
1053 $vid_atts = [];
1054
1055 $youtubeRegExp = '/youtube\.com|youtu\.be/is';
1056 $vimeoRegExp = '/vimeo\.com/is';
1057
1058 if (preg_match($youtubeRegExp, $source)) {
1059 $videoSite = 'youtube';
1060 } else if (preg_match($vimeoRegExp, $source)) {
1061 $videoSite = 'vimeo';
1062 }
1063
1064 switch ($videoSite) {
1065 case 'youtube':
1066
1067 $pagelayer->append_yt_api = true;
1068
1069 if (preg_match('/youtube\.com/is', $source)) {
1070
1071 if (preg_match('/watch/is', $source)) {
1072 parse_str($url['query'], $parameters);
1073
1074 if (isset($parameters['v']) && !empty($parameters['v'])) {
1075 $videoId = $parameters['v'];
1076 }
1077
1078 } else if (preg_match('/embed/is', $url['path'])) {
1079 $path = explode('/', $url['path']);
1080 if (isset($path[2]) && !empty($path[2])) {
1081 $videoId = $path[2];
1082 }
1083 }
1084
1085 } else if (preg_match('/youtu\.be/is', $url['host'])) {
1086 $path = explode('/', $url['path']);
1087
1088 if (isset($path[1]) && !empty($path[1])) {
1089 $videoId = $path[1];
1090 }
1091
1092 }
1093
1094 $vid_atts['type'] = 'youtube';
1095 $vid_atts['src'] = '//www.youtube.com/embed/'.$videoId;
1096 $vid_atts['id'] = $videoId;
1097
1098 break;
1099 case 'vimeo':
1100
1101 if (preg_match('/player\.vimeo\.com/is', $url['host']) && preg_match('/video/is', $url['path'])) {
1102 $path = explode('video/', $source);
1103 } else if (preg_match('/vimeo\.com/is', $url['host'])) {
1104 $path = explode('.com/', $source);
1105 }
1106
1107 if(isset($path[1]) && !empty($path[1])) {
1108 $videoId = $path[1];
1109 }
1110
1111 $vid_atts['type'] = 'vimeo';
1112 $vid_atts['src'] = '//player.vimeo.com/video/'.$videoId;
1113 $vid_atts['id'] = $videoId;
1114
1115 break;
1116 default:
1117
1118 $vid_atts['type'] = 'local';
1119 $vid_atts['src'] = $source;
1120 $vid_atts['id'] = $videoId;
1121
1122 }
1123
1124 if(!$no_url){
1125 return $vid_atts['src'];
1126 }
1127
1128 return $vid_atts;
1129
1130 }
1131 }
1132
1133
1134 // As per the JS specification
1135 function pagelayer_escapeHTML($str){
1136
1137 if(is_null($str)){
1138 return '';
1139 }
1140
1141 $replace = [
1142 ']' => '&#93;',
1143 '[' => '&#91;',
1144 //'=' => '&#61;',
1145 '<' => '&lt;',
1146 '>' => '&gt;',
1147 '"' => '&quot;',
1148 //'&' => '&amp;',
1149 '\'' => '&#39;',
1150 '\\' => '&#92;'
1151 ];
1152
1153 $str = str_replace(array_keys($replace), array_values($replace), $str);
1154
1155 return $str;
1156 }
1157
1158 // As per the JS specification
1159 function pagelayer_unescapeHTML($str){
1160
1161 if(is_null($str)){
1162 return '';
1163 }
1164
1165 $replace = [
1166 '#93' => ']',
1167 '#91' => '[',
1168 //'#61' => '=',
1169 'lt' => '<',
1170 'gt' => '>',
1171 'quot' => '"',
1172 //'amp' => '&',
1173 '#39' => '\'',
1174 '#92' => '\\'
1175 ];
1176
1177 foreach($replace as $k => $v){
1178 $str = str_replace('&'.$k.';', $v, $str);
1179 }
1180 return $str;
1181 }
1182
1183 // Converts a Unicode code point to its UTF-8 encoded string.
1184 function pagelayer_codepoint_to_utf8($num) {
1185 if ($num < 0x80) {
1186 return chr($num);
1187 } elseif ($num < 0x800) {
1188 return chr(0xC0 | ($num >> 6))
1189 . chr(0x80 | ($num & 0x3F));
1190 } elseif ($num < 0x10000) {
1191 return chr(0xE0 | ($num >> 12))
1192 . chr(0x80 | (($num >> 6) & 0x3F))
1193 . chr(0x80 | ($num & 0x3F));
1194 } elseif ($num < 0x110000) {
1195 return chr(0xF0 | ($num >> 18))
1196 . chr(0x80 | (($num >> 12) & 0x3F))
1197 . chr(0x80 | (($num >> 6) & 0x3F))
1198 . chr(0x80 | ($num & 0x3F));
1199 }
1200 return '';
1201 }
1202
1203 // To make decode entities faster
1204 function pagelayer_optimized_decode_entities($string, $req = true) {
1205
1206 // Fast replace common HTML entities
1207 $common_entities_map = [
1208 '&#93;' => ']', '&#91;' => '[', '&lt;' => '<', '&gt;' => '>', '&amp;' => '&', '&quot;' => '"', '&#39;' => "'",
1209 '&copy;' => '©', '&reg;' => '®', '&ndash;' => '', '&mdash;' => '', '&bull;' => '',
1210 '&hellip;' => '', '&lsquo;' => '', '&rsquo;' => '', '&ldquo;' => '', '&rdquo;' => ''
1211 ];
1212
1213 // Replace common entities first for performance
1214 $string = str_replace(array_keys($common_entities_map), array_values($common_entities_map), $string);
1215
1216 // Return early if no encoded entities exist
1217 if(!preg_match('/\\\\u[0-9a-fA-F]{4}|&#x[0-9a-fA-F]+;|&#\d+;/', $string)) {
1218 return $string;
1219 }
1220
1221 $string = preg_replace_callback(
1222 '/\\\\u([0-9a-fA-F]{4})|&#x([0-9a-fA-F]+);|&#([0-9]+);/',
1223 function ($matches) {
1224 if (!empty($matches[1])) {
1225 // Decode \uXXXX Unicode sequences
1226 return pagelayer_codepoint_to_utf8( hexdec($matches[1]) );
1227 }elseif (!empty($matches[2])) {
1228 // Decode hexadecimal HTML entities (&#x6A; → j)
1229 return pagelayer_codepoint_to_utf8( hexdec($matches[2]) );
1230 } elseif (!empty($matches[3])) {
1231 // Decode decimal HTML entities (&#106; → j)
1232 return pagelayer_codepoint_to_utf8( (int)$matches[3] );
1233 }
1234 return $matches[0];
1235 },
1236 $string
1237 );
1238
1239 // Additional decoding to cover remaining cases
1240 if($req){
1241 $string = pagelayer_optimized_decode_entities($string, false);
1242 }
1243
1244 return $string;
1245 }
1246
1247 // Return true if user can add js content
1248 function pagelayer_user_can_add_js_content(){
1249
1250 // Unfiltered_html cap needs to be checked for multisite
1251 if(current_user_can('unfiltered_html')){
1252 return true;
1253 }
1254
1255 $pagelayer_js_permission = get_option('pagelayer_js_permission');
1256 $current_user = wp_get_current_user();
1257
1258 // If not allowed any role by admin
1259 if(empty($pagelayer_js_permission) || empty($current_user->roles)){
1260 return false;
1261 }
1262
1263 foreach($current_user->roles as $role){
1264 if(in_array($role, $pagelayer_js_permission)){
1265 return true;
1266 }
1267 }
1268
1269 return false;
1270 }
1271
1272 // Check for XSS codes in our shortcodes submitted
1273 function pagelayer_xss_content($data){
1274
1275 // Keep a whitespace-preserved copy for the on* event-handler scan.
1276 $orig = $data;
1277
1278 $data = pagelayer_optimized_decode_entities($data);
1279
1280 // Collapse all whitespace for pattern matching
1281 $data = preg_split('/\s/', $data);
1282 $data = implode('', $data);
1283 //echo $data;
1284
1285 if(preg_match('/["\']javascript\:/is', $data)){
1286 return 'javascript';
1287 }
1288
1289 if(preg_match('/["\']vbscript\:/is', $data)){
1290 return 'vbscript';
1291 }
1292
1293 if(preg_match('/\-moz\-binding\:/is', $data)){
1294 return '-moz-binding';
1295 }
1296
1297 if(preg_match('/expression\(/is', $data)){
1298 return 'expression';
1299 }
1300
1301 if(preg_match('/\<(iframe|frame|script|style|link|applet|embed|xml|svg|object|layer|ilayer|meta)/is', $data, $matches)){
1302 return $matches[1];
1303 }
1304
1305 // Reject ANY on* event handler attribute. Per the HTML spec every attribute
1306 // name beginning with "on" (optionally followed by a letter and word chars)
1307 if(preg_match('/\bon(?:[a-z0-9-]*)?\s*=/i', $orig, $matches)){
1308 return $matches[0];
1309 }
1310
1311 return;
1312
1313 }
1314
1315 // Check for XSS codes in our blocks array
1316 function pagelayer_sanitize_blocks_save_pre($block){
1317
1318 foreach($block as $k => $v){
1319
1320 // Recurse on arrays
1321 if(is_array($v)){
1322 $block[$k] = pagelayer_sanitize_blocks_save_pre($v);
1323
1324 // We dont support objects !
1325 }elseif(is_object($v)){
1326 $block[$k] = null;
1327 // Strings
1328 }else{
1329
1330 if(is_string($v)){
1331
1332 $v = wp_filter_post_kses($v);
1333
1334 while(true){
1335 $str = '"'.($v);
1336 $found = pagelayer_xss_content($str);
1337 //echo (string)$v.'--'.$found."\n";
1338
1339 if(strlen($found) > 0){
1340 // There is htmlentities?
1341 if(strpos($v, $found) === false){
1342 $v = '';
1343 break;
1344 }
1345 $v = str_replace($found, '', $v);
1346 }else{
1347 break;
1348 }
1349
1350 }
1351
1352 }
1353
1354 $block[$k] = $v;
1355 }
1356
1357 }
1358
1359 return $block;
1360 }
1361
1362 // Check for XSS codes in our shortcode attributes
1363 function pagelayer_sanitize_shortcode_atts($content){
1364
1365 // Do we have something suspicious ?
1366 $tmp_check = pagelayer_xss_content($content);
1367 if(empty($tmp_check)){
1368 return $content;
1369 }
1370
1371 pagelayer_load_shortcodes();
1372
1373 preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
1374
1375 $prefixes = ['pl_'];
1376
1377 $prefixes = apply_filters( 'pagelayer_valid_shortcode_tag', $prefixes);
1378
1379 foreach ($matches as $shortcode) {
1380
1381 $shortcode_name = $shortcode[2];
1382
1383 $vailid = false;
1384
1385 foreach($prefixes as $prefix) {
1386 if (strpos($shortcode_name, $prefix) === 0) {
1387 $vailid = true;
1388 break;
1389 }
1390 }
1391
1392 if(!$vailid){
1393 continue;
1394 }
1395
1396 $attrs = shortcode_parse_atts( $shortcode[3] );
1397 $atts = ' ';
1398
1399 foreach($attrs as $key => $value){
1400
1401 // Skip if key contains XSS
1402 if (!is_numeric($key) && strlen(pagelayer_xss_content($key . '=')) > 0) continue;
1403
1404 $value = wp_filter_post_kses($value);
1405
1406 // Skip if value contains XSS
1407 if (strlen(pagelayer_xss_content('"' . $value)) > 0) continue;
1408
1409 $atts .= is_numeric($key) ? $value . ' ' : $key . '="' . $value . '" ';
1410
1411 }
1412
1413 $new_shortcode = '[' . $shortcode_name . $atts . ']';
1414
1415 if(!empty($shortcode[5])){
1416 // Recurse into the inner content so nested shortcodes
1417 $inner = pagelayer_sanitize_shortcode_atts($shortcode[5]);
1418 $new_shortcode .= $inner.'[/' . $shortcode_name .']';
1419 }
1420
1421 // Replace the original shortcode with sanitized attributes
1422 $content = str_replace($shortcode[0], $new_shortcode, $content);
1423 }
1424
1425 return $content;
1426 }
1427
1428 // Scan post content for XSS payloads and return a structured report.
1429 function pagelayer_xss_scan_post($post_id = 0){
1430 global $post;
1431
1432 $report = ['found' => false, 'items' => []];
1433
1434 if(empty($post_id)){
1435 if(!empty($post->ID)){
1436 $post_id = $post->ID;
1437 }else{
1438 return $report;
1439 }
1440 }
1441
1442 $content = get_post_field('post_content', $post_id);
1443 if(empty($content)){
1444 return $report;
1445 }
1446
1447 // Scan the raw content (shortcodes are not yet expanded here, so we
1448 // catch on*= attributes inside [pl_* ele_attributes="onclick=..."])
1449 $found = pagelayer_xss_content($content);
1450 if(strlen($found) > 0){
1451 $report['found'] = true;
1452 $report['items'][] = $found;
1453 }
1454
1455 return $report;
1456 }
1457
1458 // Determine whether the XSS warning should be shown for the current post/user.
1459 function pagelayer_should_show_xss_warning($post_id = 0){
1460 global $post;
1461
1462 if(empty($post_id)){
1463 if(!empty($post->ID)){
1464 $post_id = $post->ID;
1465 }else{
1466 return false;
1467 }
1468 }
1469
1470 // Rule 1: only warn users who can edit posts
1471 if(!current_user_can('edit_posts')){
1472 return false;
1473 }
1474
1475 // Rule 5: user already acknowledged — don't block again
1476 $view_token = get_transient('pagelayer_xss_view_'.$post_id.'_'.get_current_user_id());
1477 if(!empty($view_token) && isset($_GET['pl_xss_view']) && $_GET['pl_xss_view'] === $view_token){
1478 return false;
1479 }
1480
1481 // Rule 2: the post must contain XSS
1482 $scan = pagelayer_xss_scan_post($post_id);
1483 if(empty($scan['found'])){
1484 return false;
1485 }
1486
1487 // Rule 3: skip if the current user is the post author
1488 $author_id = (int) get_post_field('post_author', $post_id);
1489 $current_user_id = (int) get_current_user_id();
1490 if($author_id > 0 && $author_id === $current_user_id){
1491 return false;
1492 }
1493
1494 // Rule 4: skip if the post author has the JS/unfiltered_html capability.
1495 // We check this by temporarily switching to the author's context.
1496 $author_can_js = false;
1497 if($author_id > 0){
1498 $author_user = get_userdata($author_id);
1499 if(!empty($author_user)){
1500 $author_can_js = user_can($author_user, 'unfiltered_html');
1501 if(!$author_can_js){
1502 $author_can_js = pagelayer_user_can_add_js_content_for_user($author_user);
1503 }
1504 }
1505 }
1506 if($author_can_js){
1507 return false;
1508 }
1509
1510 return true;
1511 }
1512
1513 // Check if a specific user (not the current user) can add JS content.
1514 function pagelayer_user_can_add_js_content_for_user($user){
1515 if(empty($user) || !($user instanceof WP_User)){
1516 return false;
1517 }
1518
1519 if(user_can($user, 'unfiltered_html')){
1520 return true;
1521 }
1522
1523 $pagelayer_js_permission = get_option('pagelayer_js_permission');
1524 if(empty($pagelayer_js_permission) || empty($user->roles)){
1525 return false;
1526 }
1527
1528 foreach($user->roles as $role){
1529 if(in_array($role, $pagelayer_js_permission)){
1530 return true;
1531 }
1532 }
1533
1534 return false;
1535 }
1536
1537 // Render the XSS warning page that completely blocks content rendering.
1538 function pagelayer_render_xss_warning_block($context = 'frontend'){
1539 global $post;
1540
1541 if(empty($post->ID)){
1542 return;
1543 }
1544
1545 if(!pagelayer_should_show_xss_warning($post->ID)){
1546 return;
1547 }
1548
1549 // Re-scan to get the items for display
1550 $scan = pagelayer_xss_scan_post($post->ID);
1551 if(empty($scan['found'])){
1552 return;
1553 }
1554
1555 $items_html = '';
1556 foreach($scan['items'] as $item){
1557 $items_html .= '<div class="pagelayer-xss-item">'.htmlspecialchars($item, ENT_QUOTES, 'UTF-8').'</div>';
1558 }
1559
1560 // Generate a one-time view token so the admin can explicitly consent
1561 $view_token = wp_generate_password(32, false);
1562 set_transient('pagelayer_xss_view_'.$post->ID.'_'.get_current_user_id(), $view_token, 3600);
1563
1564 // Build the "view page" URL with the consent token
1565 $current_url = $_SERVER['REQUEST_URI'] ?? '/';
1566 $separator = strpos($current_url, '?') !== false ? '&' : '?';
1567 $view_url = htmlspecialchars($current_url.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1568
1569 // Build the edit link
1570 $edit_link = pagelayer_livelink($post->ID);
1571 $edit_link = htmlspecialchars($edit_link.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1572
1573 // Get author info for the warning message
1574 $author_id = (int) get_post_field('post_author', $post->ID);
1575 $author_name = '';
1576 if($author_id > 0){
1577 $author_data = get_userdata($author_id);
1578 if(!empty($author_data)){
1579 $author_name = $author_data->display_name;
1580 }
1581 }
1582
1583 $warning_title = __pl('xss_warning_title');
1584 $warning_body = $author_name
1585 ? sprintf(__pl('xss_warning_body_author'), htmlspecialchars($author_name, ENT_QUOTES, 'UTF-8'))
1586 : __pl('xss_warning_body');
1587 $warning_items_label = __pl('xss_warning_items');
1588 $view_btn = __pl('xss_warning_view_page');
1589 $edit_btn = __pl('xss_warning_edit_post');
1590 $warning_blocked = __pl('xss_warning_blocked');
1591
1592 $dashboard_link = admin_url();
1593 $go_dashboard = __pl('xss_warning_go_dashboard');
1594
1595 // Output the warning page and die() — the malicious post content is
1596 // NEVER sent to the browser, so inline <script>, <img onerror>, and
1597 // <iframe onload> payloads cannot execute.
1598 $status_code = ($context === 'editor') ? 200 : 403;
1599 if(!headers_sent()){
1600 status_header($status_code);
1601 header('Content-Type: text/html; charset=utf-8');
1602 }
1603
1604 echo <<<HTML
1605 <!DOCTYPE html>
1606 <html lang="en">
1607 <head>
1608 <meta charset="utf-8">
1609 <meta name="viewport" content="width=device-width, initial-scale=1">
1610 <title>{$warning_title}</title>
1611 <style>
1612 *{ margin:0; padding:0; box-sizing:border-box; }
1613 body{
1614 background:#f0f0f1;
1615 font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
1616 display:flex;
1617 align-items:center;
1618 justify-content:center;
1619 min-height:100vh;
1620 padding:20px;
1621 }
1622 #pagelayer-xss-block-dialog{
1623 background:#fff;
1624 border-radius:8px;
1625 max-width:640px;
1626 width:100%;
1627 max-height:90vh;
1628 overflow-y:auto;
1629 box-shadow:0 4px 24px rgba(0,0,0,0.15);
1630 }
1631 .pagelayer-xss-block-header{
1632 background:#d63638;
1633 color:#fff;
1634 padding:20px 24px;
1635 border-radius:8px 8px 0 0;
1636 font-size:20px;
1637 font-weight:700;
1638 display:flex;
1639 align-items:center;
1640 gap:12px;
1641 }
1642 .pagelayer-xss-block-body{
1643 padding:24px;
1644 color:#1d2327;
1645 font-size:14px;
1646 line-height:1.6;
1647 }
1648 .pagelayer-xss-block-body p{
1649 margin:0 0 14px 0;
1650 }
1651 .pagelayer-xss-blocked-tag{
1652 display:inline-block;
1653 background:#fff4f4;
1654 border:1px solid #d63638;
1655 color:#d63638;
1656 padding:3px 10px;
1657 border-radius:3px;
1658 font-size:12px;
1659 font-weight:600;
1660 margin-bottom:14px;
1661 }
1662 .pagelayer-xss-items{
1663 background:#fff4f4;
1664 border:1px solid #d63638;
1665 border-radius:4px;
1666 padding:14px;
1667 margin:14px 0;
1668 max-height:220px;
1669 overflow-y:auto;
1670 }
1671 .pagelayer-xss-item{
1672 font-family:monospace;
1673 font-size:12px;
1674 padding:6px 8px;
1675 border-bottom:1px solid #f5cccc;
1676 word-break:break-all;
1677 color:#b32d2e;
1678 }
1679 .pagelayer-xss-item:last-child{
1680 border-bottom:none;
1681 }
1682 .pagelayer-xss-block-actions{
1683 padding:18px 24px;
1684 border-top:1px solid #dcdcde;
1685 display:flex;
1686 gap:12px;
1687 flex-wrap:wrap;
1688 border-radius:0 0 8px 8px;
1689 }
1690 .pagelayer-xss-btn-view{
1691 background:#2271b1;
1692 color:#fff;
1693 border:none;
1694 border-radius:4px;
1695 padding:10px 24px;
1696 font-size:14px;
1697 cursor:pointer;
1698 font-weight:600;
1699 text-decoration:none;
1700 display:inline-block;
1701 }
1702 .pagelayer-xss-btn-view:hover{
1703 background:#135e96;
1704 }
1705 .pagelayer-xss-btn-edit{
1706 background:#fff;
1707 color:#2271b1;
1708 border:1px solid #2271b1;
1709 border-radius:4px;
1710 padding:10px 24px;
1711 font-size:14px;
1712 cursor:pointer;
1713 font-weight:600;
1714 text-decoration:none;
1715 display:inline-block;
1716 }
1717 .pagelayer-xss-btn-edit:hover{
1718 background:#f0f6fc;
1719 }
1720 .pagelayer-xss-btn-dashboard{
1721 background:transparent;
1722 color:#646970;
1723 border:none;
1724 border-radius:4px;
1725 padding:10px 16px;
1726 font-size:13px;
1727 cursor:pointer;
1728 text-decoration:none;
1729 display:inline-block;
1730 margin-left:auto;
1731 }
1732 .pagelayer-xss-btn-dashboard:hover{
1733 color:#1d2327;
1734 }
1735 </style>
1736 </head>
1737 <body>
1738 <div id="pagelayer-xss-block-dialog">
1739 <div class="pagelayer-xss-block-header">
1740 <span style="font-size:28px;">&#9888;</span>
1741 {$warning_title}
1742 </div>
1743 <div class="pagelayer-xss-block-body">
1744 <span class="pagelayer-xss-blocked-tag">{$warning_blocked}</span>
1745 <p>{$warning_body}</p>
1746 <p style="font-weight:600; margin-bottom:6px;">{$warning_items_label}:</p>
1747 <div class="pagelayer-xss-items">{$items_html}</div>
1748 </div>
1749 <div class="pagelayer-xss-block-actions">
1750 <a href="{$view_url}" class="pagelayer-xss-btn-view">{$view_btn}</a>
1751 <a href="{$edit_link}" class="pagelayer-xss-btn-edit">{$edit_btn}</a>
1752 <a href="{$dashboard_link}" class="pagelayer-xss-btn-dashboard">{$go_dashboard}</a>
1753 </div>
1754 </div>
1755 </body>
1756 </html>
1757 HTML;
1758
1759 // CRITICAL: die() here ensures the malicious post content (the_content,
1760 // do_shortcode output, block rendering) is NEVER sent to the browser.
1761 // Without die(), WordPress would continue rendering the template and
1762 // output the post body — including any <script>/<img onerror>/<iframe
1763 // onload> payloads — which would execute even if hidden with CSS.
1764 die();
1765 }
1766
1767 function pagelayer_getting_started_notice(){
1768
1769 // Is Sitepad setup done?
1770 $setup_done = get_option('sp_setup_done');
1771
1772 if(defined('SITEPAD') && empty($setup_done)){
1773 return;
1774 }
1775
1776 // If SitePad used custom BRAND SM
1777 if(defined('BRAND_SM_CUSTOM')){
1778 return;
1779 }
1780
1781 echo '
1782 <script type="application/javascript">
1783 jQuery(document).ready(function(){
1784 jQuery("#pagelayer-getting-started-notice").click(function(e){
1785
1786 if(jQuery(e.target).hasClass("notice-dismiss")){
1787 var data;
1788 jQuery("#pagelayer-getting-started-notice").hide();
1789 // Save this preference
1790 jQuery.post("'.admin_url('?pagelayer-getting-started=0&pagelayer_nonce='.wp_create_nonce("pagelayer_getting_started_nonce") ).'", data, function(response) {
1791 //alert(response);
1792 });
1793 return false;
1794 }
1795
1796 });
1797 });
1798 </script>
1799
1800 <div id="pagelayer-getting-started-notice" class="notice notice-success is-dismissible">
1801 <p style="font-size: 14px; font-weight: 600">';
1802 if(defined('SITEPAD')){
1803
1804 echo '<a href="'.BRAND_SM_URL.'"><img src="'.BRAND_SM_LOGO .'" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Thanks for choosing '.BRAND_SM .'. We recommend that you see the short and sweet <a href="'.admin_url('admin.php?page=pagelayer_getting_started').'">Getting Started Video</a> to know the basics of '.BRAND_SM.'.');
1805
1806 }else{
1807
1808 echo '<a href="'.PAGELAYER_WWW_URL.'"><img src="'.PAGELAYER_URL.'/images/pagelayer-logo-256.png" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Thanks for choosing Pagelayer. We recommend that you see the short and sweet <a href="'.admin_url('admin.php?page=pagelayer_getting_started').'">Getting Started Video</a> to know the basics of Pagelayer.', 'pagelayer');
1809
1810 }
1811
1812 echo '</p>
1813 </div>';
1814
1815 }
1816
1817 // Show Changelog promo
1818 function pagelayer_show_changelog_notice(){
1819
1820 // Is Sitepad setup done?
1821 if(defined('SITEPAD')){
1822 return;
1823 }
1824
1825 echo '
1826 <script type="application/javascript">
1827 jQuery(document).ready(function(){
1828 jQuery("#pagelayer-changelog-notice").click(function(e){
1829
1830 if(jQuery(e.target).hasClass("notice-dismiss")){
1831 var data;
1832 jQuery("#pagelayer-changelog-notice").hide();
1833 // Save this preference
1834 jQuery.post("'.admin_url('?pagelayer-changelog-notice=0&pagelayer_nonce='.wp_create_nonce("pagelayer_changelog_nonce") ).'", data, function(response) {
1835 //alert(response);
1836 });
1837 return false;
1838 }
1839
1840 });
1841 });
1842 </script>
1843
1844 <div id="pagelayer-changelog-notice" class="notice notice-info is-dismissible">
1845 <p style="font-size: 14px; font-weight: 600">
1846 <a href="'.PAGELAYER_WWW_URL.'"><img src="'.PAGELAYER_URL.'/images/pagelayer-logo-256.png" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Empower Your Designs: Pagelayer 1.8.1 - Unleashing Seamless Integration with Gutenberg for Enhanced Website Creation! <a href="'.PAGELAYER_WWW_URL.'/blog/reinventing-pagelayer-for-gutenberg-a-seamless-fusion-of-powerful-page-building-and-wordpress-editor/">Read More</a>.', 'pagelayer') .'
1847 </p>
1848 </div>';
1849
1850 }
1851
1852 // Show promo notice on dashboard
1853 function pagelayer_show_promo(){
1854
1855 global $pagelayer_promo_opts;
1856 $opts = $pagelayer_promo_opts;
1857
1858 echo '<style>
1859 #pagelayer_promo a.pagelayer_promo_button {
1860 background-color: #4CAF50 !important;
1861 border: none !important;
1862 color: white !important;
1863 padding: 6px 10px !important;
1864 text-align: center !important;
1865 text-decoration: none !important;
1866 display: inline-block !important;
1867 font-size: 13px !important;
1868 margin: 4px 2px !important;
1869 -webkit-transition-duration: 0.4s;
1870 transition-duration: 0.4s;
1871 cursor: pointer !important;
1872 }
1873 #pagelayer_promo a.pagelayer_promo_button:focus,
1874 #pagelayer_promo a.pagelayer_promo_button:hover{
1875 border: none !important;
1876 color: white !important;
1877 box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19) !important;
1878 }
1879 #pagelayer_promo a.pagelayer_promo_buy {
1880 color: white !important;
1881 padding: 8px 12px !important;
1882 font-size: 14px !important;
1883 background-color: #4CAF50 !important;
1884 }
1885 #pagelayer_promo a.pagelayer_promo_button1 {
1886 color: white !important;
1887 background-color: #4CAF50 !important;
1888 border:3px solid #4CAF50 !important;
1889 }
1890 #pagelayer_promo a.pagelayer_promo_button1:hover {
1891 border:3px solid #4CAF50 !important;
1892 }
1893 #pagelayer_promo a.pagelayer_promo_button2 {
1894 color: white !important;
1895 background-color: #0085ba !important;
1896 }
1897 #pagelayer_promo a.pagelayer_promo_button3 {
1898 color: white !important;
1899 background-color: #365899 !important;
1900 }
1901 #pagelayer_promo a.pagelayer_promo_button4 {
1902 color: white !important;
1903 background-color: rgb(66, 184, 221) !important;
1904 }
1905 #pagelayer_promo a.pagelayer_promo-close{
1906 float:right !important;
1907 text-decoration:none !important;
1908 margin: 5px 10px 0px 0px !important;
1909 color: #666 !important;
1910 }
1911 #pagelayer_promo a.pagelayer_promo-close:hover{
1912 color: red !important;
1913 }
1914 #pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close) {
1915 color: #4CAF50 !important;
1916 text-decoration: underline !important;
1917 }
1918 #pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close):hover {
1919 color: #3e8e41 !important;
1920 }
1921 </style>
1922 <script type="application/javascript">
1923 jQuery(document).ready(function(){
1924 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
1925 var data;
1926 jQuery("#pagelayer_promo").hide();
1927 // Save this preference
1928 jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
1929 //alert(response);
1930 });
1931 });
1932 });
1933 </script>
1934
1935 <div class="notice notice-success" id="pagelayer_promo" style="min-height:90px">
1936 <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
1937 <span class="dashicons dashicons-dismiss"></span> Dismiss
1938 </a>';
1939
1940 if(!empty($opts['image'])){
1941 echo '<a href="'.$opts['website'].'"><img src="'.$opts['image'].'" style="float:left; margin:10px 20px 10px 10px" width="67" /></a>';
1942 }
1943
1944 echo '
1945 <p style="font-size:13px">We are glad you like <a href="'.$opts['website'].'"><b>Pagelayer</b></a> and have been using it since the past few days. It is time to take the next step !</p>
1946 <p>
1947 '.(empty($opts['pro_url']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_buy" target="_blank" href="'.$opts['pro_url'].'">Buy Pagelayer Pro</a>').'
1948 '.(empty($opts['rating']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button2" target="_blank" href="'.$opts['rating'].'">Rate it 5�
1949 \'s</a>').'
1950 '.(empty($opts['facebook']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button3" target="_blank" href="'.$opts['facebook'].'"><span class="dashicons dashicons-thumbs-up"></span> Facebook</a>').'
1951 '.(empty($opts['twitter']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['twitter'].'"><span class="dashicons dashicons-twitter"></span> Tweet</a>').'
1952 '.(empty($opts['website']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['website'].'">Visit our website</a>').'
1953 </p>
1954 <p style="font-size:13px"><a href="'.$opts['pro_url'].'"><b>Pagelayer Pro</b></a> has many more features like 60+ widgets, 400+ sections, Theme Builder, WooCommerce Builder, Theme Creator and Exporter, Form Builder, Popup Builder, etc.';
1955
1956 if(date('Ymd') <= 20200331){
1957 echo '<br><span style="font-size: 14px"><b>Promotional Offer</b></span> : If you buy <a href="'.$opts['pro_url'].'"><b>Pagelayer Pro</b></a> before <b>31st March, 2020</b> then you will get an additional year free and your license will expire on <b>31st March, 2022</b>.';
1958 }
1959
1960 echo '</p></div>';
1961
1962 }
1963
1964 // Are we to show a promo ?
1965 function pagelayer_maybe_promo($opts){
1966
1967 global $pagelayer_promo_opts;
1968
1969 // There must be an interval
1970 if(!current_user_can('activate_plugins')){
1971 return false;
1972 }
1973
1974 // There must be an interval
1975 if(empty($opts['interval'])){
1976 return false;
1977 }
1978
1979 // Are we to show a promo
1980 $opt_name = 'pagelayer_promo_time';
1981 $promo_time = get_option($opt_name);
1982
1983 // First time access
1984 if(empty($promo_time)){
1985 update_option($opt_name, time() + (!empty($opts['after']) ? $opts['after'] * 86400 : 0));
1986 $promo_time = get_option($opt_name);
1987 }
1988
1989 // Is there interval elapsed
1990 if(time() > $promo_time){
1991 $pagelayer_promo_opts = $opts;
1992 add_action('admin_notices', 'pagelayer_show_promo');
1993 }
1994
1995 // Are we to disable the promo
1996 if(isset($_GET['pagelayer_promo']) && (int)$_GET['pagelayer_promo'] == 0){
1997 update_option($opt_name, time() + ($opts['interval'] * 86400));
1998 die('DONE');
1999 }
2000
2001 }
2002
2003 // Show the Pro notice
2004 function pagelayer_show_pro_notice(){
2005
2006 if(defined('PAGELAYER_PREMIUM')){
2007 return;
2008 }
2009
2010 echo '<div class="pagelayer-notice pagelayer-notice-info">'.__('This feature is a part of <a href="'.PAGELAYER_PRO_PRICE_URL.'" target="_blank">Pagelayer Pro</a>. You will need to purchase <a href="'.PAGELAYER_PRO_PRICE_URL.'" target="_blank">Pagelayer Pro</a> to use this feature.').'</div>';
2011
2012 }
2013
2014 // Show the Pro Div
2015 function pagelayer_show_pro_div($head = '', $message = '', $admin_css = 1){
2016
2017 if(defined('PAGELAYER_PREMIUM')){
2018 return;
2019 }
2020
2021 if(basename(get_template_directory()) == 'popularfx'){
2022 $pro_url = 'https://popularfx.com/pricing?from=pagelayer-plugin';
2023 $pro_txt = 'PopularFX Pro';
2024 }else{
2025 $pro_url = PAGELAYER_PRO_PRICE_URL;
2026 $pro_txt = 'Pagelayer Pro';
2027 }
2028
2029 if(!empty($admin_css)){
2030 wp_enqueue_style( 'pagelayer-admin', PAGELAYER_CSS.'/pagelayer-admin.css', array(), PAGELAYER_VERSION);
2031 }
2032
2033 echo '<div class="pagelayer-pro-div">';
2034
2035 if(!empty($head)){
2036 echo '<h1 class="pagelayer-pro-head">'.$head.'</h1>';
2037 }
2038
2039 echo '<div class="pagelayer-pro-message">';
2040
2041 if(empty($message)){
2042
2043 echo __('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>. You will need to purchase <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a> to use this feature.');
2044
2045 }else{
2046
2047 echo $message;
2048 echo ' '.__('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>.');
2049
2050 }
2051
2052 echo '</div>
2053 <center><a class="button-pagelayer" href="'.$pro_url.'" target="_blank" style="text-decoration: none;">'.sprintf(__('Upgrade to %s', 'pagelayer'), $pro_txt).'</a></center>
2054 </div>';
2055
2056 }
2057
2058 // Bread Crumbs with links
2059 function pagelayer_get_breadcrumb(){
2060 global $post;
2061
2062 // Home page
2063 $ret = '<a href="'.home_url().'" rel="nofollow">{{home}}</a>';
2064
2065 // Is Front page
2066 if(is_front_page()){
2067 return $ret;
2068
2069 // Is search query
2070 }elseif(is_search()){
2071 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><b class="pagelayer-breadcrumb-prefix" if="{{search_prefix}}">{{search_prefix}}</b>';
2072 $ret .= '<em>';
2073 $ret .= '<span>'.get_search_query().'</span>';
2074 $ret .= '</em>';
2075
2076 //Is category or single post
2077 }elseif(is_category() || is_single()){
2078
2079 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2080 $categories = get_the_category();
2081 $separator = ' ';
2082 $output = '';
2083 if(!empty($categories)){
2084 $ret .= get_category_parents($categories[0], true, ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ');
2085 }
2086 if(is_single()) {
2087 /* if (empty($categories)){
2088 $ret .= ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ';
2089 } */
2090 $ret .= '<span>'.get_the_title().'</span>';
2091 }
2092
2093 // Is page
2094 }elseif(is_page() ){
2095 if(!empty($post->post_parent)){
2096 $ancestors = array_reverse(get_post_ancestors( $post->ID ));
2097 $page_on_front = get_option('page_on_front');
2098
2099 foreach( $ancestors as $ancestor ){
2100
2101 if($page_on_front == $ancestor){
2102 continue;
2103 }
2104
2105 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><a href="'.get_permalink($ancestor).'">'.get_the_title($ancestor).'</a>';
2106 }
2107
2108 $ret .=' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><span>'.get_the_title().'</span>';
2109
2110 }else{
2111 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2112 $ret .= '<span>'.get_the_title().'</span>';
2113 }
2114 }else{
2115 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2116 $ret .= '<span>'.wp_title('', false ).'</span>';
2117 }
2118
2119 // wooCommerce Integration left
2120
2121 return $ret;
2122 }
2123
2124 // Portfolio Posts
2125 function pagelayer_widget_posts($args){
2126
2127 $r = new \WP_Query($args);
2128
2129 $ret = '';
2130 if ( $r->have_posts() ){
2131
2132 if($args['filter_by'] != 'none' && $args['post_type'] == 'post'){
2133 $ret .= '<div class="pagelayer-postfolio-filter">';
2134
2135 $data_filter = ( $args['filter_by'] == 'category' ? get_categories() : get_tags() );
2136
2137 if(!empty($data_filter)){
2138
2139 $ret .= '<div class="pagelayer-postfolio-btn" data-filter="all">'.__pl('All').'</div>';
2140
2141 foreach($data_filter as $filter) {
2142 $ret .= '<div class="pagelayer-postfolio-btn" data-filter="'.$filter->name.'">' . $filter->name . '</div>';
2143 }
2144
2145 }
2146
2147 $ret .= '</div>';
2148 }
2149
2150 $ret .= '<div class="pagelayer-postfolio-container">';
2151
2152 while ( $r->have_posts() ) : $r->the_post();
2153 $post_meta = ( $args['filter_by'] == 'category' ? get_the_category() : get_the_tags() );
2154 $meta_attr = '';
2155 if($post_meta){
2156
2157 $meta_array = array();
2158 foreach( $post_meta as $meta ){
2159 $meta_array[] = $meta->name;
2160 }
2161 $meta_attr .= ' data-category="'.implode(' ', $meta_array).'"';
2162 }
2163
2164 $ret .= '<div class="pagelayer-postfolio-thumb"'.(has_post_thumbnail() ? ' style="background:url('.get_the_post_thumbnail_url().')"' : ''). $meta_attr .'>';
2165 $ret .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark">';
2166 $ret .= '<div class="pagelayer-postfolio-content">';
2167 $ret .= '<h4 class="pagelayer-entry-title">'.get_the_title().'</h4>';
2168 $ret .= '</div></a></div>';
2169
2170 endwhile;
2171
2172 $ret .= '</div>';
2173
2174 }
2175
2176 return $ret;
2177 }
2178
2179 // List all available sizes of images registered on WordPress
2180 function pagelayer_image_sizes(){
2181
2182 $sizes = array();
2183 $sizes = get_intermediate_image_sizes();
2184 $ret = array();
2185 foreach($sizes as $size){
2186 $ret[$size] = __pl($size);
2187 }
2188
2189 return $ret;
2190 }
2191
2192 function pagelayer_remove_excerpt_more($more){
2193 return '';
2194 }
2195
2196 function pagelayer_posts($params, $args = []){
2197 global $post, $wp_query;
2198
2199 if(isset($params['exc_length'])){
2200 $exc_length = (int) $params['exc_length'];
2201 add_filter( 'excerpt_length', function($length) use($exc_length){
2202 return $exc_length;
2203 }, 999 );
2204 }
2205
2206 // add_filter('excerpt_more', 'pagelayer_remove_excerpt_more', 999);
2207
2208 // If args is empty
2209 if(empty($args)){
2210 $args = array(
2211 'post_type' => $params['post_type'],
2212 'posts_per_page' => $params['posts_per_page'],
2213 'order' => $params['order']
2214 );
2215
2216 if(!empty($params['paged'])){
2217 $args['paged'] = $params['paged'];
2218 }
2219
2220 if (!empty($params['term']) || !empty($params['exc_term'])) {
2221 $tax_query = [];
2222
2223 if (!empty($params['term'])) {
2224 $terms = explode(',', $params['term']);
2225 $include = array_reduce($terms, function ($carry, $term) {
2226 list($taxonomy, $slug, $id) = explode(':', $term);
2227 $carry[$taxonomy][] = $slug;
2228 return $carry;
2229 }, []);
2230
2231 $filters = array_filter([
2232 isset($include['category']) ? [
2233 'taxonomy' => 'category',
2234 'field' => 'slug',
2235 'terms' => $include['category'],
2236 'include_children' => false,
2237 ] : null,
2238 isset($include['post_tag']) ? [
2239 'taxonomy' => 'post_tag',
2240 'field' => 'slug',
2241 'terms' => $include['post_tag'],
2242 'include_children' => false,
2243 ] : null,
2244 ]);
2245
2246 if(!empty($filters)){
2247 $tax_query[] = array_merge(['relation' => 'OR'], $filters);
2248 }
2249 }
2250
2251 // Handle exclusions
2252 if (!empty($params['exc_term'])) {
2253 $terms = explode(',', $params['exc_term']);
2254 $include = array_reduce($terms, function ($carry, $term) {
2255 list($taxonomy, $slug, $id) = explode(':', $term);
2256 $carry[$taxonomy][] = $slug;
2257 return $carry;
2258 }, []);
2259
2260 $filters = array_filter([
2261 isset($include['category']) ? [
2262 'taxonomy' => 'category',
2263 'field' => 'slug',
2264 'terms' => $include['category'],
2265 'operator' => 'NOT IN',
2266 'include_children' => false,
2267 ] : null,
2268 isset($include['post_tag']) ? [
2269 'taxonomy' => 'post_tag',
2270 'field' => 'slug',
2271 'terms' => $include['post_tag'],
2272 'operator' => 'NOT IN',
2273 'include_children' => false,
2274 ] : null,
2275 ]);
2276
2277 if(!empty($filters)){
2278 $tax_query[] = array_merge(['relation' => 'AND'], $filters);
2279 }
2280 }
2281
2282 // Apply the combined tax_query
2283 if(!empty($tax_query)){
2284 $args['tax_query'] = array_merge(['relation' => 'AND'], $tax_query);
2285 }
2286 }
2287
2288 if(!empty($params['author_name'])){
2289 $author_name = explode(':', $params['author_name']);
2290 $args['author_name'] = $author_name[0];
2291 }
2292
2293 if(!empty($params['exc_author'])){
2294 $exc_author = explode(':', $params['exc_author']);
2295 $args['author'] = '-'.$exc_author[1];
2296 }
2297
2298 if (!empty($params['offset'])) {
2299 $args['offset'] = (int) $params['offset'];
2300
2301 // Make Compatible for Infinite load
2302 if(!empty($params['infinite_types']) && !empty($params['paged']) && $params['paged'] > 1 ){
2303 $args['offset'] = $args['offset'] + (($params['paged'] - 1) * $params['posts_per_page']);
2304 }
2305 }
2306
2307 if(!empty($params['ignore_sticky'])){
2308 $args['ignore_sticky_posts'] = $params['ignore_sticky'];
2309 }
2310
2311 if(!empty($params['orderby'])){
2312 $args['orderby'] = $params['orderby'];
2313 }
2314
2315 if(!empty($params['by_period'])){
2316 $date_arg = array();
2317 switch($params['by_period']){
2318 case 'last_day':
2319 $date_arg['day'] = date('j')-1;
2320 break;
2321 case 'last_week':
2322 $date_arg['week'] = date('W')-1;
2323 break;
2324 case 'last_month':
2325 $date_arg['month'] = date('n')-1;
2326 break;
2327 case 'last_year':
2328 $date_arg['year'] = date('Y')-1;
2329 break;
2330 case 'custom':
2331 $date_arg['before'] = $params['before_date'];
2332 $date_arg['after'] = $params['after_date'];
2333 break;
2334 }
2335 $args['date_query'] = array(
2336 $date_arg
2337 );
2338 }
2339 }
2340 //pagelayer_print($args);
2341
2342 // Only see published posts
2343 $args['post_status'] = 'publish';
2344
2345 $postsquery = new WP_Query($args);
2346 $data = '';
2347
2348 if(!wp_doing_ajax() && !empty($params['infinite_types'])){
2349 $data.='<div class="pagelayer-post-max" data-max="'.$postsquery->max_num_pages.'"></div>';
2350 }
2351
2352 if(!$postsquery->have_posts()){
2353 return '<h3>No posts found!</h3>';
2354 }
2355
2356 // To reset the post when the $wp_query->post is empty
2357 $orig_post = $post;
2358
2359 while($postsquery->have_posts()) : $postsquery->the_post();
2360 $data .= '<div class="pagelayer-wposts-col">
2361 <div class="pagelayer-wposts-post">
2362 <div class="pagelayer-wposts-featured">';
2363 $data .= '<a href="'. get_the_permalink() .'">';
2364
2365 if(!empty($params['show_thumb'])){
2366 $data .= '<div class="pagelayer-wposts-thumb" style="background:url('.(has_post_thumbnail($postsquery->ID) ? get_the_post_thumbnail_url($postsquery->ID, $params['thumb_size']) : ( empty($params['thumb_img_type']) || empty($params['def_thumb_img']) ? PAGELAYER_URL.'/images/no_screenshot.png' : $params['def_thumb_img']) ).')"></div>';
2367 }
2368 /* if($params['show_thumb'] && has_post_thumbnail( $postsquery->ID )){
2369 $data .= get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']);
2370 } */
2371 $data .= '</a></div>
2372 <div class="pagelayer-wposts-content">';
2373
2374 if(!empty($params['show_title'])){
2375 $data .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark"><div class="pagelayer-wposts-title">'. get_the_title().'</div></a>';
2376 }
2377
2378 $data .= '<div class="pagelayer-wposts-meta">';
2379 $sep = '';
2380 if(!empty($params['meta_sep'])){
2381 $sep = ' <b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b> ';
2382 }
2383 if(!empty($params['author'])){
2384 $data .= '<span class="pagelayer-wposts-author">By <a class="pagelayer-wposts-author-url" href="'.esc_url(get_author_posts_url(get_the_author_meta('ID'))).'">'.esc_html(get_the_author()).'</a></span>'.$sep;
2385 }
2386 if(!empty($params['date'])){
2387 $data .= '<span class="pagelayer-wposts-date"><time class="pagelayer-wposts-entry-date published updated" datetime="'.get_the_date('c').'"><span class="date-d">'.get_the_date('j').'</span> <span class="date-my">'.get_the_date('M, y').'</span></time></span>'.$sep;
2388 }
2389
2390 if(!empty($params['category'])){
2391 $category = get_the_category();
2392 $singlecategory = '';
2393 foreach( $category as $cat ){
2394 $singlecategory .= '<a href="' . get_tag_link( $cat->term_id ) . '">'. $cat->name .'</a>';
2395 }
2396
2397 if(!empty($singlecategory)){
2398 $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2399 }
2400
2401 }
2402
2403 if(!empty($params['tags'])){
2404 $tags = get_the_tags();
2405 $singletag = '';
2406 if(!empty($tags)){
2407 foreach( $tags as $tag ){
2408 $singletag .= '<a href="' . get_tag_link( $tag->term_id ) . '">'. $tag->name .'</a>';
2409 }
2410 if(!empty($singletag)){
2411 $data .= '<span class="pagelayer-wposts-tags">'.$singletag.'</span>'.$sep;
2412 }
2413 }
2414
2415 }
2416
2417 if(!empty($params['comments']) && comments_open($postsquery->ID)){
2418 $data .= '<span class="pagelayer-wposts-comments"><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()).' '.__pl('comments').'</a></span>'.$sep;
2419 }
2420
2421 $data .= '</div>';
2422
2423 if(!empty($params['show_content'])){
2424 $data .= '<div class="pagelayer-wposts-excerpt">';
2425
2426 if($params['show_content'] == 'excerpt'){
2427 $post_excerpt = get_the_excerpt();
2428 $trimmed_excerpt = !empty($params['exc_length']) ? wp_trim_words($post_excerpt, (int) $params['exc_length']) : $post_excerpt;
2429 $data .= pagelayer_the_content($trimmed_excerpt);
2430 } elseif ($params['show_content'] == 'full') {
2431 $data .= pagelayer_the_content(get_the_content());
2432 }
2433 $data .= '</div>';
2434 }
2435
2436 if(!empty($params['show_more'])){
2437 $data .= '<div class="pagelayer-wposts-mdiv"><a class="pagelayer-wposts-more pagelayer-btn-holder pagelayer-ele-link '.$params['btn_type'].' '.$params['size'].' '.$params['icon_position'].'" href="'. get_the_permalink().'">';
2438
2439 if($params['icon']){
2440 $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2441 }
2442
2443 if($params['more']){
2444 $data .= '<span class="pagelayer-btn-text">'.$params['more'].'</span>';
2445 }
2446
2447 if($params['icon']){
2448 $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2449 }
2450
2451 $data .= '</a></div>';
2452
2453 //$data .= '<a class="pagelayer-wposts-more" href="'. get_the_permalink().'">'.$params['more'].'</a>';
2454 }
2455
2456 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
2457 $data .= '</div></div></div>';
2458 endwhile;
2459
2460 // In the Gutenberg while adding new page the $wp_query->post was empty
2461 if ( !isset( $wp_query ) || empty($wp_query->post) ) {
2462 $GLOBALS['post'] = $orig_post;
2463 }else{
2464 wp_reset_postdata();
2465 }
2466
2467 return $data;
2468 }
2469
2470 // Get Post Revision
2471 function pagelayer_get_post_revision_by_id($postID){
2472
2473 // Insert the post revision into the database
2474 $post_revisions = array();
2475 $reviews = wp_get_post_revisions($postID);
2476
2477 foreach($reviews as $values){
2478
2479 $date_format = date_i18n('j-M @ H:i', strtotime( $values->post_modified ) );
2480 $user_meta = get_userdata($values->post_author);
2481
2482 if ( false !== strpos( $values->post_name, 'autosave' ) ) {
2483 $type = 'autosave';
2484 } else {
2485 $type = 'revision';
2486 }
2487
2488 $post_tmp_data = array(
2489 'ID' => $values->ID,
2490 'post_author_name' => $user_meta->data->display_name,
2491 'post_author_url' => get_avatar_url($values->post_author),
2492 'post_date' => $date_format,
2493 'post_date_ago' => human_time_diff(strtotime($values->post_modified), current_time( 'timestamp' )) . ' ago ',
2494 'post_type' => $type,
2495 );
2496
2497 $post_revisions[] = $post_tmp_data;
2498 }
2499
2500 return $post_revisions;
2501 }
2502
2503 // Gets author data
2504 function pagelayer_author_data($postID){
2505
2506 $authorID = get_post_field('post_author', $postID);
2507 $data['display_name'] = get_the_author_meta( 'display_name' , $authorID );
2508 $data['description'] = get_the_author_meta( 'description' , $authorID );
2509 $data['user_url'] = get_author_posts_url( $authorID , '' );
2510 $data['avatar'] = get_avatar_url( $authorID );
2511
2512 return $data;
2513 }
2514
2515 // Posts Slider
2516 function pagelayer_posts_slider($params){
2517
2518 $args = array();
2519
2520 if(isset($params['post']['post_type'])){
2521 $args['post_type'] = $params['post']['post_type'];
2522 }
2523
2524 if(isset($params['post']['category'])){
2525 $args['category_name'] = $params['post']['category'];
2526 }
2527
2528 if(isset($params['post']['tags'])){
2529 $args['tag'] = $params['post']['tags'];
2530 }
2531
2532 if(isset($params['post']['order_by'])){
2533 $args['orderby'] = $params['post']['order_by'];
2534 }
2535
2536 if(isset($params['post']['sort_order'])){
2537 $args['order'] = $params['post']['sort_order'];
2538 }
2539
2540 if(isset($params['post']['post_count'])){
2541 $args['posts_per_page'] = $params['post']['post_count'];
2542 }
2543
2544 $data = '';
2545 $postsquery = new WP_Query($args);
2546 if ( $postsquery->have_posts() ){
2547 while ( $postsquery->have_posts() ) : $postsquery->the_post();
2548 $data .= '<li class="pagelayer-posts-slider-col">
2549 <div class="pagelayer-posts-slider-post">
2550 <div class="pagelayer-posts-slider-featured-img">';
2551 $data .= '<a href="'. get_the_permalink() .'">';
2552 if(has_post_thumbnail( $postsquery->ID ) ){
2553 if($params['post']['image_size'] == "full" || $params['post']['image_size'] == "custom" ){
2554 $image = wp_get_attachment_image_src( get_post_thumbnail_id( $postsquery->ID ), 'single-post-thumbnail' );
2555 $data .= '<img class="pagelayer-img" src="'.$image[0].'" class="pagelayer-posts-slider-img">';
2556 }else if($params['post']['image_size'] == "thumbnail"){
2557 $data .= get_the_post_thumbnail();
2558 }
2559 }
2560 $data .= '</a></div>
2561 <div class="pagelayer-posts-slider-content">';
2562 $data .= '<p class="pagelayer-posts-slider-title">'. get_the_title().'</p>';
2563 $data .= '<div class="pagelayer-posts-slider-excerpt">';
2564 if($params['post']['show_excerpt'] == "true"){
2565 if(has_excerpt()){
2566 $excerpt = get_the_excerpt();
2567 $data .= pagelayer_the_content($excerpt);
2568 }
2569 }
2570 $data .= '</div>';
2571 $data .= '<a class="pagelayer-posts-slider-link" href="'. get_the_permalink().'">Read More</a>';
2572 $data .= '<div class="pagelayer-post-slider-date"><p>'.get_the_date().'</p></div>';
2573 $data .= '</div></div></li>';
2574 endwhile;
2575 }
2576 return $data;
2577 }
2578
2579 // Gets the site logo URLs
2580 function pagelayer_site_logo(){
2581
2582 if(get_theme_mod('custom_logo')){
2583 $logo_id = get_theme_mod('custom_logo');
2584 return pagelayer_image($logo_id);
2585 }
2586
2587 return NULL;
2588 }
2589
2590 // Create select options
2591 function pagelayer_create_sel_options( $opt_array , $selected = ''){
2592 $options = '';
2593 foreach($opt_array as $x => $val){
2594
2595 // Single item
2596 if(is_string($opt_array[$x])){
2597 $options .= pagelayer_sel_option($x, $val, $selected);
2598
2599 // Groups
2600 }else{
2601
2602 if(array_key_exists('hide_drop', $opt_array[$x]) && !empty($opt_array[$x]['hide_drop'])){
2603 continue;
2604 }
2605
2606 // If Label is there, then its a normal option
2607 if(array_key_exists('label', $opt_array[$x])){
2608 $options .= pagelayer_sel_option($x, $opt_array[$x]['label'], $selected);
2609
2610 // Optgroups
2611 } else{
2612 $options .= '<optgroup label="'. $x .'">';
2613 $options .= pagelayer_create_sel_options($opt_array[$x], $selected);
2614 $options .= '</optgroup>';
2615 }
2616 }
2617 }
2618
2619 return $options;
2620 }
2621
2622 // Create option HTML
2623 function pagelayer_sel_option($val, $lang, $selected){
2624 return '<option value="'. $val .'" '. (($val != $selected) ? '' : 'selected="selected"') .' >'. $lang .'</option>';
2625 }
2626
2627 // Get values from multi-dimensional array by key
2628 function pagelayer_multi_array_search(&$array, $key){
2629
2630 if(!is_array($array)){
2631 return false;
2632 }
2633
2634 foreach ($array as $k => $v) {
2635
2636 if($k == $key){
2637 return $v;
2638 }
2639
2640 if (is_array($v)) {
2641 $found = pagelayer_multi_array_search($v, $key);
2642 if(!empty($found)){
2643 return $found;
2644 }
2645 }
2646 }
2647
2648 return false;
2649 }
2650
2651 function pagelayer_get_post_term(){
2652
2653 $args = [
2654 'taxonomy' => array('category','post_tag'),
2655 'hide_empty' => false,
2656 ];
2657
2658 $terms = get_terms( $args );
2659
2660 $ret = array();
2661 foreach ( $terms as $term ) {
2662 $ret[$term->taxonomy.':'.$term->slug.':'.$term->term_taxonomy_id] = $term->taxonomy .': '. $term->name;
2663 }
2664 //pagelayer_print($terms);die();
2665 return $ret;
2666 }
2667
2668 function pagelayer_get_post_author(){
2669
2670 $args = [
2671 'capability' => array( 'edit_posts' ),
2672 'fields' => [
2673 'ID',
2674 'display_name',
2675 'user_nicename',
2676 ]
2677 ];
2678
2679 // Capability queries were only introduced in WP 5.9.
2680 if( version_compare( $GLOBALS['wp_version'], '5.9-alpha', '<' ) ){
2681 $args['who'] = 'authors';
2682 unset( $args['capability'] );
2683 }
2684
2685 $authors = new \WP_User_Query( $args );
2686
2687 $ret = array();
2688 foreach ( $authors->get_results() as $author ) {
2689 $ret[$author->user_nicename.':'.$author->ID] = $author->display_name;
2690 }
2691 //pagelayer_print($authors->get_results());die();
2692 return $ret;
2693 }
2694
2695 // Gets the registered post types
2696 function pagelayer_get_public_post_types( $args = [] ) {
2697
2698 global $pagelayer;
2699
2700 $post_type_args = [
2701 'public' => true,
2702 ];
2703
2704 $post_type_args = wp_parse_args( $post_type_args, $args );
2705
2706 $_post_types = get_post_types( $post_type_args, 'objects' );
2707
2708 $post_types = array();
2709
2710 foreach ( $_post_types as $post_type => $object ) {
2711
2712 if($post_type == $pagelayer->builder['name']){
2713 continue;
2714 }
2715
2716 $post_types[ $post_type ] = $object->label;
2717 }
2718 //print_r($post_types);
2719
2720 return $post_types;
2721 }
2722
2723 // Simply echo and dir
2724 function pagelayer_json_output(&$done){
2725
2726 echo json_encode($done);
2727 wp_die();
2728
2729 }
2730
2731 // Get the current query for render the product
2732 function pagelayer_shortcode_current_query($query_args, $atts, $type){
2733 global $wp_query;
2734
2735 if($type == 'pagelayer_current_query'){
2736
2737 if ( ! is_page( wc_get_page_id( 'shop' ) ) ) {
2738 $query_args = $wp_query->query_vars;
2739 }
2740
2741 add_action( "woocommerce_shortcode_before_{$type}_loop", function () {
2742 wc_set_loop_prop( 'is_shortcode', false );
2743 } );
2744
2745 if(!empty($atts['paginate'])){
2746 $page = get_query_var( 'paged', 1 );
2747
2748 if( 1 < $page ) {
2749 $query_args['paged'] = $page;
2750 }
2751 }
2752
2753 // Always query only IDs
2754 $query_args['fields'] = 'ids';
2755
2756 }
2757
2758 return $query_args;
2759 }
2760
2761 // Loads the tags which have parameters of a particular type
2762 function pagelayer_get_prop_type($types){
2763
2764 global $pagelayer;
2765
2766 if(!is_array($types)){
2767 $types = [$types];
2768 }
2769
2770 $ret = [];
2771
2772 // Loop thru all shortcodes
2773 foreach($pagelayer->shortcodes as $tag => $vvv){
2774
2775 // Lets create the CSS, Classes, Attr. Also clean the dependent atts
2776 foreach($pagelayer->tabs as $tab){
2777
2778 if(empty($pagelayer->shortcodes[$tag][$tab])){
2779 continue;
2780 }
2781
2782 foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
2783
2784 $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
2785
2786 //echo $tab.' - '.$section.' - <br>';
2787
2788 if(empty($props)){
2789 continue;
2790 }
2791
2792 // Loop all props
2793 foreach($props as $prop => $param){
2794
2795 // Load any attachment values
2796 if(!in_array($param['type'], $types)){
2797 continue;
2798 }
2799
2800 $ret[$tag][$prop] = $param['type'];
2801
2802 }
2803
2804 }
2805
2806 }
2807
2808 }
2809
2810 //r_print($ret);die();
2811
2812 return $ret;
2813
2814 }
2815
2816 function pagelayer_export_content($content){
2817
2818 global $pagelayer;
2819
2820 // Just call do_shortcode so we can get list of media files to export
2821 //do_shortcode($content);
2822
2823 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
2824
2825 $content = preg_replace('/http(s?):\/\/'.preg_quote($theme_url, '/').'/is', '{{theme_url}}', $content);
2826
2827 // Remove unnecessary spaces
2828 $content = preg_replace('/--\>\s*(?!<!--)/is', '-->', $content);
2829
2830 // Replace links of the theme for images
2831 $content = pagelayer_export_theme_links($content, get_stylesheet_directory_uri());
2832
2833 ////////////////////
2834 // Handle Link IDs
2835 ////////////////////
2836
2837 if(empty($pagelayer->e['link_tags'])){
2838 $pagelayer->e['link_tags'] = pagelayer_get_prop_type('link');
2839 //r_print($pagelayer->e['link_tags']);die();
2840 }
2841
2842 if(empty($pagelayer->e['media_tags'])){
2843 $pagelayer->e['media_tags'] = pagelayer_get_prop_type(['image', 'video', 'audio', 'media', 'multi_image']);
2844 //r_print($pagelayer->e['media_tags']);die();
2845 }
2846
2847 $parsed = parse_blocks($content);
2848 //r_print($parsed);//die();
2849
2850 $parsed = pagelayer_export_link_ids($parsed);
2851
2852 // Export the media as well
2853 $parsed = pagelayer_export_media($parsed);
2854
2855 // Reserialize
2856 $content = serialize_blocks($parsed);
2857
2858 // We store in SP format
2859 $content = str_replace('<!-- wp:pagelayer', '<!-- sp:pagelayer', $content);
2860 $content = str_replace('<!-- /wp:pagelayer', '<!-- /sp:pagelayer', $content);
2861
2862 // Just pass the content to
2863 $content = pagelayer_export_extract_menu_ids($content);
2864
2865 // Apply a filter
2866 $content = apply_filters('pagelayer_export_content', $content);
2867
2868 return $content;
2869
2870 }
2871
2872 // Export the menu IDs
2873 function pagelayer_export_extract_menu_ids($content){
2874
2875 global $pagelayer;
2876
2877 // Any Menu ?
2878 preg_match_all('/\[pl_wp_menu ([^\]]*)nav_list="(\d*)"([^\]]*)\]/is', $content, $matches);
2879 //r_print($matches);
2880
2881 if(!empty($matches[2])){
2882
2883 foreach($matches[2] as $k => $v){
2884 $v = (int)$v;
2885 $pagelayer->export_menus[$v] = $v;
2886 }
2887 //r_print($pagelayer->export_menus);
2888
2889 }
2890
2891 // Also for block format
2892 preg_match_all('/<!--\s+(?P<closer>\/)?sp:pagelayer\/pl_wp_menu\s+(?P<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?}\s+)?(?P<void>\/)?-->/s', $content, $matches);
2893
2894 if(!empty($matches['attrs'])){
2895
2896 foreach($matches['attrs'] as $k => $v){
2897 preg_match('/nav_list"\s*:\s*"(\d*)"/is', $v, $vmatch);
2898
2899 if(!empty($vmatch[1])){
2900 $vmatch[1] = (int)$vmatch[1];
2901 $pagelayer->export_menus[$vmatch[1]] = $vmatch[1];
2902 //r_print($pagelayer->export_menus);
2903 }
2904 }
2905 }
2906
2907 return $content;
2908
2909 }
2910
2911 function pagelayer_export_link_ids($parsed){
2912
2913 global $pagelayer;
2914
2915 foreach($parsed as $k => $v){
2916
2917 if(!empty($v['innerBlocks'])){
2918 $parsed[$k]['innerBlocks'] = pagelayer_export_link_ids($v['innerBlocks']);
2919 }
2920
2921 if(!preg_match('/pagelayer/is', $v['blockName'])){
2922 continue;
2923 }
2924
2925 $tag = str_replace('pagelayer/', '', $v['blockName']);
2926 //echo $tag;//die();
2927
2928 // Is there a tag having a link prop
2929 if(empty($pagelayer->e['link_tags'][$tag])){
2930 continue;
2931 }
2932 //echo $tag;die();
2933
2934 foreach($pagelayer->e['link_tags'][$tag] as $kk => $vv){
2935 if(!empty($v['attrs'][$kk]) && is_numeric($v['attrs'][$kk])){
2936 $linked = get_post($v['attrs'][$kk]);
2937 if(!empty($linked->post_name)){
2938 $parsed[$k]['attrs'][$kk] = '||link_id|'.$linked->post_type.'|'.$linked->post_name.'||';
2939 }
2940 //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
2941 }
2942 }
2943
2944 }
2945
2946 return $parsed;
2947
2948 }
2949
2950 // Replace theme links with theme_url
2951 function pagelayer_export_theme_links($content, $link){
2952
2953 global $sitepad;
2954
2955 // Theme URL correction code
2956 $theme_url = preg_replace('/http(s?):\/\//is', '', $link);
2957
2958 $theme_urls[0] = 'http://'.$theme_url;
2959 $theme_urls[1] = 'https://'.$theme_url;
2960 $theme_urls[2] = $theme_url;
2961 $theme_urls[3] = str_replace('/', '\\/', $theme_urls[0]);
2962 $theme_urls[4] = str_replace('/', '\\/', $theme_urls[1]);
2963 $theme_urls[5] = str_replace('/', '\\/', $theme_url);
2964
2965 foreach($theme_urls as $k => $v){
2966 $content = preg_replace_callback('/'.preg_quote($v, '/').'([^"]*)/is', 'pagelayer_export_theme_links_replacer', $content);
2967 }
2968
2969 $content = str_ireplace($theme_urls, '{{theme_url}}', $content);
2970
2971 return $content;
2972 }
2973
2974 // This function is a part of pagelayer_export_theme_links
2975 function pagelayer_export_theme_links_replacer($matches){
2976 return '{{theme_url}}'.str_replace('\\/', '/', $matches[1]);
2977 }
2978
2979 // Export media
2980 function pagelayer_export_media($parsed){
2981
2982 global $pagelayer;
2983
2984 foreach($parsed as $k => $v){
2985
2986 if(!empty($v['innerBlocks'])){
2987 $parsed[$k]['innerBlocks'] = pagelayer_export_media($v['innerBlocks']);
2988 }
2989
2990 if(!preg_match('/pagelayer/is', $v['blockName'])){
2991 continue;
2992 }
2993
2994 $tag = str_replace('pagelayer/', '', $v['blockName']);
2995 //echo $tag;//die();
2996
2997 // Is there a tag having a link prop
2998 if(empty($pagelayer->e['media_tags'][$tag])){
2999 continue;
3000 }
3001 //echo $tag;die();
3002
3003 foreach($pagelayer->e['media_tags'][$tag] as $kk => $vv){
3004
3005 if(empty($v['attrs'][$kk])){
3006 continue;
3007 }
3008
3009 $is_array = 0;
3010
3011 // Is it an array
3012 if(is_array($v['attrs'][$kk])){
3013
3014 $ids = $v['attrs'][$kk];
3015 $is_array = 1;
3016
3017 // Is it of the format 1,2,3
3018 }elseif(preg_match('/^((\d*)(,?))*$/is', $v['attrs'][$kk])){
3019
3020 $ids = pagelayer_maybe_explode(',', $v['attrs'][$kk]);
3021
3022 // Its a number or string
3023 }else{
3024
3025 // Is it our default image ?
3026 if(preg_match('/pagelayer-pro\/images\/default-image.png$/is', $v['attrs'][$kk])){
3027 $parsed[$k]['attrs'][$kk] = '';
3028 continue;
3029 }
3030
3031 // It should be a number
3032 if(!is_numeric($v['attrs'][$kk])){
3033 continue;
3034 }
3035
3036 $ids = [$v['attrs'][$kk]];
3037 }
3038
3039 $ret = [];
3040
3041 foreach($ids as $id){
3042
3043 $file = pagelayer_export_media_files($id, $exp_img_url);
3044
3045 // Did it export ?
3046 if(empty($file) || !file_exists($file)){
3047 continue;
3048 }
3049
3050 $ret[] = $exp_img_url;
3051
3052 }
3053
3054 $parsed[$k]['attrs'][$kk] = $is_array ? $ret : implode(',', $ret);
3055 //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
3056 }
3057
3058 }
3059
3060 return $parsed;
3061
3062 }
3063
3064 // Exports the media to the images folder of the current theme
3065 function pagelayer_export_media_files($id, &$url = ''){
3066
3067 $theme_dir = get_stylesheet_directory();
3068 $image_dir = $theme_dir.'/images/';
3069 @mkdir($image_dir);
3070
3071 // Get the file path
3072 $file = get_attached_file($id);
3073
3074 if(empty($file) || !file_exists($file)){
3075 return false;
3076 }
3077
3078 $dest_file = $image_dir.basename($file);
3079
3080 // Copy the file
3081 copy($file, $dest_file);
3082
3083 // Replace the text
3084 $url = '{{theme_url}}/images/'.basename($file);
3085
3086 return $dest_file;
3087
3088 //echo $content;
3089 }
3090
3091 // Insert a post which is a Pagelayer Post
3092 function pagelayer_sanitize_content($content){
3093
3094 if(empty($content)){
3095 return $content;
3096 }
3097
3098 // Replace Vars
3099 $template_vars = pagelayer_template_vars();
3100
3101 foreach($template_vars as $key => $val){
3102 $content = str_replace($key, $val, $content);
3103 }
3104
3105 if(defined('PAGELAYER_BLOCK_PREFIX') && PAGELAYER_BLOCK_PREFIX == 'wp'){
3106 $content = str_replace('<!-- sp:pagelayer', '<!-- wp:pagelayer', $content);
3107 $content = str_replace('<!-- /sp:pagelayer', '<!-- /wp:pagelayer', $content);
3108 }
3109
3110 return $content;
3111 }
3112
3113 // Insert a post which is a Pagelayer Post
3114 function pagelayer_insert_content($post, &$ret){
3115
3116 $post = apply_filters('pagelayer_start_insert_content', $post);
3117
3118 $post['post_content'] = pagelayer_sanitize_content($post['post_content']);
3119 //pagelayer_print($post);die();
3120
3121 // Add slashes for safe insert
3122 $post['post_content'] = wp_slash($post['post_content']);
3123
3124 $post = apply_filters('pagelayer_pre_insert_content', $post);
3125
3126 // Now insert / update the post
3127 $ret = wp_insert_post($post);
3128
3129 // Did we save the post ?
3130 if(empty($ret) || is_wp_error($ret)){
3131 return false;
3132 }
3133
3134 // Convert to pagelayer accessed post
3135 if(!add_post_meta($ret, 'pagelayer-data', time(), true)){
3136 update_post_meta($ret, 'pagelayer-data', time());
3137 }
3138
3139 return $ret;
3140
3141 }
3142
3143 // Gets the list of enabled fonts
3144 function pagelayer_enabled_icons(){
3145
3146 $stored_icons = get_option('pagelayer_icons_set');
3147 if(empty($stored_icons)){
3148 update_option('pagelayer_icons_set', ['font-awesome5']);
3149 $stored_icons = get_option('pagelayer_icons_set');
3150 }
3151
3152 return $stored_icons;
3153
3154 }
3155
3156 // Prevent pro activate text for installer
3157 function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
3158
3159 if($plugin_file == PAGELAYER_PREMIUM_BASE){
3160 return array();
3161 }
3162
3163 return $install_actions;
3164 }
3165
3166 // Handle hexa to rgba and also remove alpha which is ff
3167 function pagelayer_hex8_to_rgba($val){
3168
3169 // If opacity is ff then discard ff
3170 if(preg_match('/^#([a-f0-9]{6})ff$/is', $val)){
3171 return substr($val, 0, 7);
3172 }
3173
3174 // Lets handle the RGB+opacity
3175 if(preg_match('/#([a-f0-9]{8})$/is', $val)){
3176 $val = pagelayer_hex2rgba($val);
3177 }
3178
3179 return $val;
3180
3181 }
3182
3183 // Convert to RGBA from HEX
3184 function pagelayer_hex2rgba($color){
3185
3186 //Return if no color provided
3187 if(empty($color)){
3188 return;
3189 }
3190
3191 //Sanitize $color if "#" is provided
3192 if ($color[0] == '#'){
3193 $color = substr( $color, 1 );
3194 }
3195
3196 //Check if color has 6 or 3 characters and get values
3197 if (strlen($color) == 8) {
3198 $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3199 $alpha = $color[6] . $color[7];
3200 //$alpha = '';
3201 } elseif (strlen($color) == 6) {
3202 $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3203 } elseif ( strlen( $color ) == 3 ) {
3204 $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
3205 } else {
3206 return;
3207 }
3208
3209 //Convert hexadec to rgb
3210 $rgb = array_map('hexdec', $hex);
3211
3212 //Check if opacity is set(rgba or rgb)
3213 if($alpha){
3214
3215 $alpha = number_format((float)hexdec($alpha) / 255, 2, '.', '');
3216 //print_r($alpha);
3217 if(abs($alpha) > 1){
3218 $alpha = 1.0;
3219 }
3220 $output = 'rgba('.implode(",",$rgb).','.$alpha.')';
3221 } else {
3222 $output = 'rgb('.implode(",",$rgb).')';
3223 }
3224
3225 //Return rgb(a) color string
3226 return $output;
3227 }
3228
3229 // Get social URLs
3230 function pagelayer_get_social_urls(){
3231
3232 $urls = array();
3233
3234 $urls['facebook'] = get_option('pagelayer-facebook-url');
3235 $urls['twitter'] = get_option('pagelayer-twitter-url');
3236 $urls['instagram'] = get_option('pagelayer-instagram-url');
3237 $urls['linkedin'] = get_option('pagelayer-linkedin-url');
3238 $urls['youtube'] = get_option('pagelayer-youtube-url');
3239 $urls['google'] = get_option('pagelayer-gplus-url');
3240
3241 foreach($urls as $k => $v){
3242
3243 if(empty($v)){
3244 unset($urls[$k]);
3245 continue;
3246 }
3247
3248 $urls[$k] = esc_url($v);
3249 }
3250
3251 return $urls;
3252 }
3253
3254 function pagelayer_get_option($opt){
3255 $ret = get_option($opt);
3256
3257 $opts = array(
3258 'pagelayer-address' => '1, My Address, My Street, New York City, NY, USA',
3259 'pagelayer-phone' => '+1234567890',
3260 'pagelayer-copyright' => '© '.date('Y').' '.get_option('blogname'),
3261 'pagelayer_cf_to_email' => 'contact@domain.com',
3262 'pagelayer_cf_success' => __pl('cf_success'),
3263 'pagelayer_cf_failed' => __pl('cf_failed'),
3264 );
3265
3266 if(empty($ret)){
3267 return $opts[$opt];
3268 }
3269
3270 return wp_kses_post($ret);
3271 }
3272
3273 // Uploads an image / media
3274 function pagelayer_upload_media($filename, $blob){
3275
3276 if(empty($blob)){
3277 return false;
3278 }
3279
3280 $md5 = md5($blob);
3281
3282 // Do we have this image
3283 $args = array(
3284 'post_type' => 'attachment',
3285 'post_status' => 'inherit',
3286 'meta_query' => array(
3287 array(
3288 'key' => 'pagelayer_image_md5',
3289 'value' => $md5,
3290 )
3291 )
3292 );
3293
3294 $query = new WP_Query($args);
3295
3296 // If we found the image, return
3297 foreach($query->posts as $ck => $cv){
3298 return $cv->ID;
3299 }
3300
3301 $upload = wp_upload_bits($filename, null, $blob);
3302
3303 if( !empty( $upload['error'] ) ) {
3304 return false;
3305 }
3306
3307 $file_path = $upload['file'];
3308 $file_name = basename( $file_path );
3309 $file_type = wp_check_filetype( $file_name, null );
3310 $attachment_title = sanitize_file_name( pathinfo( $file_name, PATHINFO_FILENAME ) );
3311 $wp_upload_dir = wp_upload_dir();
3312
3313 $post_info = array(
3314 'guid' => $wp_upload_dir['url'] . '/' . $file_name,
3315 'post_mime_type'=> $file_type['type'],
3316 'post_title' => $attachment_title,
3317 'post_content' => '',
3318 'post_status' => 'inherit',
3319 );
3320
3321 $attach_id = wp_insert_attachment( $post_info, $file_path, $parent_post_id );
3322 update_post_meta($attach_id, 'pagelayer_image_md5', $md5);
3323
3324 $lib = ABSPATH . 'site-admin/includes/image.php';
3325 $media = ABSPATH . 'site-admin/includes/media.php';
3326 $media = file_exists($media) ? $media : ABSPATH . 'wp-admin/includes/media.php';
3327 $lib = file_exists($lib) ? $lib : ABSPATH . 'wp-admin/includes/image.php';
3328
3329 require_once($media);
3330 require_once($lib);
3331
3332 $attach_data = wp_generate_attachment_metadata( $attach_id, $file_path );
3333 wp_update_attachment_metadata( $attach_id, $attach_data );
3334
3335 return $attach_id;
3336
3337 }
3338
3339 // Show the notice of importing the active themes content
3340 function pagelayer_theme_import_notices($return = false){
3341
3342 $theme = wp_get_theme();
3343
3344 $imported = get_option('pagelayer_theme_'.get_template().'_imported');
3345 $show = 0;
3346
3347 // We need to import the content
3348 if(empty($imported) && is_admin() && current_user_can('switch_themes')){
3349 $show = 1;
3350 }
3351
3352 $dismissed = get_option('pagelayer_theme_'.get_template().'_dismissed');
3353
3354 // Is this dismissed
3355 if($dismissed){
3356 $show = 0;
3357 }
3358
3359 //$show = 1;
3360
3361 // Is it the importer page ?
3362 if(!empty($_REQUEST['page']) && $_REQUEST['page'] == 'pagelayer_import'){
3363 $show = 0;
3364 }
3365
3366 // Show the message
3367 if(!empty($show)){
3368 $str = '
3369
3370 <style>
3371 .pagelayer_promo_button {
3372 background-color: #4CAF50; /* Green */
3373 border: none;
3374 color: white;
3375 padding: 6px 10px;
3376 text-align: center;
3377 text-decoration: none;
3378 display: inline-block;
3379 font-size: 13px;
3380 margin: 4px 2px;
3381 -webkit-transition-duration: 0.4s; /* Safari */
3382 transition-duration: 0.4s;
3383 cursor: pointer;
3384 }
3385 .pagelayer_promo_button:focus,
3386 .pagelayer_promo_button:hover{
3387 border: none;
3388 color: white;
3389 box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
3390 color: white;
3391 }
3392 .pagelayer_promo_buy {
3393 color: white;
3394 padding: 8px 12px;
3395 font-size: 14px;
3396 }
3397 .pagelayer_promo_button1 {
3398 color: white;
3399 background-color: #4CAF50;
3400 border:3px solid #4CAF50;
3401 }
3402 .pagelayer_promo_button1:hover {
3403 border:3px solid #4CAF50;
3404 }
3405 .pagelayer_promo_button2 {
3406 color: white;
3407 background-color: #0085ba;
3408 }
3409 .pagelayer_promo_button3 {
3410 color: white;
3411 background-color: #365899;
3412 }
3413 .pagelayer_promo_button4 {
3414 color: white;
3415 background-color: rgb(66, 184, 221);
3416 }
3417 .pagelayer_promo-close{
3418 float:right;
3419 text-decoration:none;
3420 margin: 5px 10px 0px 0px;
3421 }
3422 .pagelayer_promo-close:hover{
3423 color: red;
3424 }
3425 </style>
3426
3427 <script type="application/javascript">
3428 jQuery(document).ready(function(){
3429 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
3430 var data;
3431 jQuery("#pagelayer_promo").hide();
3432 // Save this preference
3433 jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
3434 //alert(response);
3435 });
3436 });
3437 });
3438 </script>
3439
3440 <div class="notice notice-success" id="pagelayer_promo" style="min-height:30px">
3441 <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
3442 <span class="dashicons dashicons-dismiss"></span> Dismiss
3443 </a>';
3444
3445 $str .= apply_filters('pagelayer_theme_import_notice_content', '<p>Please click <a href="'.admin_url('admin.php?page=pagelayer_import').'">here</a> to import your themes content</p>').'
3446
3447 </div>';
3448
3449 // Print it
3450 if(empty($return)){
3451 echo $str;
3452 }else{
3453 return $str;
3454 }
3455 }
3456
3457 }
3458
3459 // Parse the array variables
3460 function pagelayer_replace_vars( $string, $array, $pre = '', $suf = ''){
3461
3462 $array = (array) $array;
3463
3464 foreach($array as $kk => $vv){
3465
3466 if(is_array($vv)){
3467 $vv = pagelayer_flat_join($vv);
3468 }
3469
3470 $string = str_replace($pre.$kk.$suf, $vv, $string);
3471 }
3472 return $string;
3473 }
3474
3475 // Add Attachment to the contact the email
3476 function pagelayer_cf_email_attachment( &$phpmailer) {
3477
3478 if ( ! empty( $_FILES ) ) {
3479 foreach ( $_FILES as $attachment ) {
3480
3481 if(empty($attachment['name'])){
3482 continue;
3483 }
3484
3485 try {
3486 $phpmailer->addAttachment($attachment['tmp_name'], $attachment['name'], $encoding = 'base64', $attachment['type'], $disposition = 'attachment');
3487 } catch ( \Exception $e ) {
3488 continue;
3489 }
3490 }
3491 }
3492
3493 }
3494
3495 function pagelayer_array_flatten( $input ) {
3496 if ( ! is_array( $input ) ) {
3497 return array( $input );
3498 }
3499
3500 $output = array();
3501
3502 foreach ( $input as $value ) {
3503 $output = array_merge( $output, pagelayer_array_flatten( $value ) );
3504 }
3505
3506 return $output;
3507 }
3508
3509 function pagelayer_flat_join( $array ) {
3510 $array = pagelayer_array_flatten( $array );
3511 $output = array();
3512
3513 foreach ( (array) $array as $value ) {
3514 $output[] = trim( (string) $value );
3515 }
3516
3517 return implode( ', ', $output );
3518 }
3519
3520 // Verifies the Google Captcha
3521 function pagelayer_captcha_verify(){
3522
3523 // If secret key is not there, return
3524 $captcha_secret = get_option('pagelayer_google_captcha_secret');
3525
3526 if(empty($captcha_secret)){
3527 return true;
3528 }
3529
3530 $response = !empty($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : '';
3531
3532 $ip = pagelayer_getip();
3533
3534 // Is the IP or response not there ?
3535 if(empty($response) || empty($ip)){
3536 return false;
3537 }
3538
3539 $url = 'https://www.google.com/recaptcha/api/siteverify';
3540
3541 // Verify the post
3542 $req = wp_remote_post($url, array(
3543 'timeout' => 20,
3544 'body' => array(
3545 'secret' => $captcha_secret,
3546 'response' => $response,
3547 'remoteip' => $ip
3548 )
3549 )
3550 );
3551
3552 // Was there an error posting ?
3553 if(is_wp_error($req)){
3554 return false;
3555 }
3556
3557 // Process the post response
3558 $resp = wp_remote_retrieve_body($req);
3559
3560 // Is the body valid
3561 if(empty($resp)){
3562 return false;
3563 }
3564
3565 $json = json_decode($resp, true);
3566
3567 if(!empty($json['success']) && // for v2 and v3
3568 (!isset($json['score']) || $json['score'] >= 0.5 && $json['action'] === 'submit') // For v3
3569 ){
3570 return true;
3571 }
3572
3573 return false;
3574 }
3575
3576 // Pagelayer serialize attributes
3577 function pagelayer_serialize_attributes( $block_attributes ) {
3578 $encoded_attributes = json_encode( $block_attributes, JSON_FORCE_OBJECT);
3579 $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
3580 $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
3581 $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
3582 // Regex: /\\"/
3583 $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
3584
3585 return $encoded_attributes;
3586 }
3587
3588 function pagelayer_enable_giver(){
3589 global $pagelayer;
3590 return !empty($pagelayer->settings['enable_giver']) && $pagelayer->settings['enable_giver'] == 1;
3591 }
3592
3593 function pagelayer_load_font_options(){
3594 include_once(PAGELAYER_DIR.'/main/font-options.php');
3595 }
3596
3597 function pagelayer_is_utf8($str) {
3598 return (bool) preg_match('//u', $str);
3599 }
3600
3601 // Create blank images
3602 function pagelayer_create_blank_image($width, $height){
3603
3604 $im = @imagecreate($width, $height);
3605
3606 if(empty($im)){
3607 return false;
3608 }
3609
3610 $background_color = imagecolorallocate($im, 220, 220, 220);
3611 $text_color = imagecolorallocate($im, 110, 110, 110);
3612 $string = $width.' x '.$height;
3613 $font_size = 30;
3614 $sx = ($width / 2) - (strlen($string) * $font_size / 2);
3615 $sy = ($height / 2) - ($font_size / 2);
3616 $font = realpath(PAGELAYER_DIR.'/fonts/OpenSans-BoldItalic.ttf');
3617 imagettftext($im, $font_size, 0, $sx, $sy, $text_color, $font, $string);
3618 ob_start();
3619 imagepng($im);
3620 imagedestroy($im);
3621
3622 return ob_get_clean();
3623
3624 }
3625
3626 // Get category name by post type
3627 function pagelayer_post_type_category($post_type){
3628
3629 $taxonomies = get_object_taxonomies( $post_type, 'objects');
3630
3631 foreach( $taxonomies as $key => $tax){
3632 if(!empty($tax->hierarchical)){
3633 return $key;
3634 }
3635 }
3636
3637 return false;
3638
3639 }
3640
3641 // Get tag name by post type
3642 function pagelayer_post_type_tag($post_type){
3643
3644 $taxonomies = get_object_taxonomies( $post_type, 'objects');
3645
3646 foreach( $taxonomies as $key => $tax){
3647 if(!empty($tax->show_tagcloud) && empty($tax->hierarchical)){
3648 return $key;
3649 }
3650 }
3651 return false;
3652
3653 }
3654
3655 // Get checkboxes of post categories.
3656 function pagelayer_post_cats($post, &$cat_name = ''){
3657
3658 $ret = [];
3659
3660 $cat_name = pagelayer_post_type_category($post->post_type);
3661
3662 if(empty($cat_name)){
3663 return $ret;
3664 }
3665
3666 include_once(ABSPATH.PAGELAYER_CMS_DIR_PREFIX.'-admin/includes/template.php');
3667
3668 $args1 = array(
3669 'taxonomy' => $cat_name,
3670 'checked_ontop' => false,
3671 'echo' => false,
3672 );
3673
3674 $ret['with_checkbox'] = '<ul class="pagelayer-post-category" >'.wp_terms_checklist($post->ID, $args1 ).'</ul>';
3675
3676 $args2 = array(
3677 'taxonomy' => $cat_name,
3678 'hierarchical' => true,
3679 'echo' => 0,
3680 'class' => 'pagelayer-add-cat',
3681 'name' => 'pagelayer_cat_parent',
3682 'style' => 'list',
3683 'hide_empty' => 0,
3684 );
3685
3686 $ret['without_checkbox'] = wp_dropdown_categories( $args2 );
3687
3688 return $ret;
3689
3690 }
3691
3692 // Get checkboxes of post tags.
3693 function pagelayer_post_tags($post, &$tag_name = ''){
3694
3695 $ret = [];
3696
3697 $tag_name = pagelayer_post_type_tag($post->post_type);
3698
3699 if(empty($tag_name)){
3700 return $ret;
3701 }
3702
3703 $ret['postTags'] = wp_get_post_terms( $post->ID, $tag_name );
3704
3705 $ret['allTags'] = get_tags(array( 'hide_empty' => false, 'taxonomy' => $tag_name ));
3706
3707 return $ret;
3708
3709 }
3710
3711 // Get users who can edit a particular post type
3712 function pagelayer_post_authors_by_type($post_type){
3713
3714 global $wp_post_types;
3715
3716 if(empty($wp_post_types[$post_type])){
3717 $users = get_users( array( 'role__in' => array( 'administrator' ) ) );
3718 }else{
3719 $req_role = $wp_post_types[$post_type]->cap->edit_posts;
3720 $roles__in = [];
3721 foreach( wp_roles()->roles as $role_slug => $role ){
3722 if( ! empty( $role['capabilities'][$req_role] ) )
3723 $roles__in[] = $role_slug;
3724 }
3725 $users = get_users( array( 'role__in' => $roles__in ) );
3726 }
3727
3728 $user_array = [];
3729
3730 foreach ( $users as $user ) {
3731 $user_array[$user->ID] = $user->display_name;
3732 }
3733
3734 return $user_array;
3735 }
3736
3737 function pagelayer_parent_post_prop($post){
3738 global $wp_post_types;
3739
3740 if(!empty($wp_post_types[$post->post_type]->hierarchical)){
3741 return pagelayer_list_parents(0, 0, $post, []);
3742 }else{
3743 return [];
3744 }
3745 }
3746
3747 function pagelayer_list_parents( $parent = 0, $level = 0, $post = null, $values=array('')){
3748
3749 global $wpdb;
3750
3751 $post = get_post( $post );
3752
3753 $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_title FROM $wpdb->posts
3754 WHERE post_parent = %d
3755 AND post_type = '".$post->post_type."'
3756 AND post_status = 'publish'
3757 ORDER BY menu_order", $parent ) );
3758
3759 if ( !empty($items) ) {
3760 foreach ( $items as $item ) {
3761
3762 // A page cannot be its own parent.
3763 if ( $post && $post->ID && (int) $item->ID === $post->ID ) {
3764 continue;
3765 }
3766
3767 $pad = str_repeat( '-', $level );
3768
3769 if(count($values)==0){
3770 $values[] = '';
3771 }
3772
3773 $values[' '.$item->ID] = $pad.esc_html( $item->post_title );
3774
3775 $values = pagelayer_list_parents($item->ID, $level + 1, $post, $values );
3776 }
3777 }
3778
3779 return $values;
3780 }
3781
3782 // Maybe explode
3783 function pagelayer_maybe_explode( $separator = ',', $string = '', $limit = PHP_INT_MAX ){
3784
3785 if(is_array($string)){
3786 return $string;
3787 }
3788
3789 return explode($separator, $string, $limit);
3790 }
3791
3792 // Maybe implode
3793 function pagelayer_maybe_implode($array, $separator = ','){
3794
3795 if(is_array($array)){
3796 return @implode($separator, $array);
3797 }
3798
3799 if(is_null($array)){
3800 return '';
3801 }
3802
3803 return $array;
3804
3805 }
3806
3807 // Sanitize text field or array
3808 function pagelayer_sanitize_text_field($str) {
3809
3810 if ( !is_object( $str ) && !is_array( $str ) ) {
3811 return sanitize_text_field($str);
3812 }
3813
3814 if( is_array($str) ){
3815 return array_map( 'pagelayer_sanitize_text_field', $str);
3816 }
3817
3818 return $str;
3819 }
3820
3821 // Sanitize posts data for WP_Query
3822 function pagelayer_sanitize_posts_data($data, $only_allowed = true) {
3823
3824 $allowed_keys = [
3825 'post_type', 'posts_per_page', 'order', 'orderby', 'paged',
3826 'filter_by', 'term', 'exc_term', 'cat', 'category_name',
3827 'tag', 'author', 'author_name', 'post__in', 'post__not_in',
3828 'include', 'exclude', 'search', 's', 'exact', 'sentence',
3829 'post_status', 'post_parent', 'offset',
3830 'posts_per_archive_page', 'page', 'ignore_sticky_posts'
3831 ];
3832
3833 $sanitized = [];
3834
3835 foreach($data as $key => $value){
3836 if($only_allowed && !in_array($key, $allowed_keys)) {
3837 continue;
3838 }
3839
3840 $sanitized[$key] = pagelayer_sanitize_text_field($value);
3841 }
3842
3843 // Security: Restrict post_status to prevent information disclosure
3844 // Only users who can read private posts or edit others' posts should be able to query non-public statuses
3845 if(isset($sanitized['post_status'])){
3846 $requested_status = $sanitized['post_status'];
3847
3848 // If requesting something other than publish, verify permissions
3849 if ($requested_status !== 'publish') {
3850 // Check if the user has permission to read private posts or edit others' posts
3851 // This prevents contributors from seeing titles of private posts they don't own.
3852 if (!current_user_can('read_private_posts') && !current_user_can('edit_others_posts')) {
3853 $sanitized['post_status'] = 'publish';
3854 }
3855 }
3856 }else{
3857 // Default to publish for safety if not specified
3858 $sanitized['post_status'] = 'publish';
3859 }
3860
3861 if(isset($sanitized['posts_per_page'])){
3862 $sanitized['posts_per_page'] = (int) $sanitized['posts_per_page'];
3863 if ($sanitized['posts_per_page'] > 100) {
3864 $sanitized['posts_per_page'] = 100;
3865 }
3866 }
3867
3868 if(isset($sanitized['paged'])){
3869 $sanitized['paged'] = (int) $sanitized['paged'];
3870 }
3871
3872 if(isset($sanitized['offset'])){
3873 $sanitized['offset'] = (int) $sanitized['offset'];
3874 }
3875
3876 if(isset($sanitized['post__in']) && is_string($sanitized['post__in'])){
3877 $sanitized['post__in'] = array_map('intval', explode(',', $sanitized['post__in']));
3878 }
3879
3880 if(isset($sanitized['post__not_in']) && is_string($sanitized['post__not_in'])){
3881 $sanitized['post__not_in'] = array_map('intval', explode(',', $sanitized['post__not_in']));
3882 }
3883
3884 if(isset($sanitized['post_parent'])){
3885 $sanitized['post_parent'] = (int) $sanitized['post_parent'];
3886 }
3887
3888 if(isset($sanitized['cat'])){
3889 $sanitized['cat'] = (int) $sanitized['cat'];
3890 }
3891
3892 if(isset($sanitized['author'])){
3893 $sanitized['author'] = (int) $sanitized['author'];
3894 }
3895
3896 return $sanitized;
3897 }
3898
3899 // Update nav menu item
3900 function pagelayer_save_nav_menu_items($items){
3901
3902 // Menu setting keys
3903 $allowed_post = array('title' => 'post_title');
3904 $allowed_meta = array('_pagelayer_content');
3905
3906 foreach($items as $id => $values){
3907
3908 $meta_input = array();
3909 $id = (int) $id;
3910 $post = get_post($id);
3911
3912 // Post found ?
3913 if(empty($post)){
3914 continue;
3915 }
3916
3917 foreach($values as $kk => $vv){
3918
3919 if(array_key_exists($kk, $allowed_post)){
3920 $name = $allowed_post[$kk];
3921 $post->$name = $vv;
3922 }
3923
3924 if(!in_array($kk, $allowed_meta)){
3925 continue;
3926 }
3927
3928 $meta_input[$kk] = stripslashes_deep($vv);
3929 }
3930
3931 if(isset($meta_input['_pagelayer_content'])){
3932 $meta_input['_pagelayer_content'] = base64_decode($meta_input['_pagelayer_content']);
3933 }
3934
3935 $post->meta_input = $meta_input;
3936 wp_update_post($post);
3937 }
3938 }
3939
3940 // Update customizer options
3941 function pagelayer_save_customizer_options($customizer_options){
3942 global $pagelayer;
3943
3944 // To load customizer params array
3945 if(empty($pagelayer->customizer_params)){
3946 pagelayer_load_shortcodes();
3947 }
3948
3949 $customizer_options = apply_filters( 'pagelayer_save_customizer_options', $customizer_options );
3950 $customizer_data = array();
3951 $customizer_option_data = array();
3952
3953 foreach($pagelayer->customizer_params as $ck => $cv){
3954 foreach($pagelayer->screens as $sk => $sv){
3955
3956 $screen = (!empty($sv) ? '_'.$sv : '');
3957 $screen_key = $ck.$screen;
3958
3959 // Save to pagelayer customizer mods
3960 if( empty($cv['option']) ){
3961 if( isset($customizer_options[$screen_key])){
3962 $customizer_data[$screen_key] = $customizer_options[$screen_key];
3963 }
3964 continue;
3965 }
3966
3967 $option_key = $cv['option'].$screen;
3968
3969 // Update option
3970 if( isset($customizer_options[$screen_key] ) ) {
3971 update_option( $option_key, $customizer_options[$screen_key]);
3972 continue;
3973 }
3974
3975 delete_option($option_key);
3976 }
3977 }
3978
3979 // Set Typography settings for global and current post type
3980 $posttype=array(
3981 '' => '', // Make this empty for global values
3982 'current' => $customizer_options['pagelayer_current_post_type']
3983 );
3984
3985 $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
3986
3987 foreach($posttype as $type => $tv){
3988
3989 foreach($pagelayer->css_settings as $set => $params){
3990
3991 foreach($pagelayer->screens as $sk => $sv){
3992
3993 $screen = (!empty($sv) ? '_'.$sv : '');
3994 $post_type = (!empty($tv) ? '_'.$tv : '');
3995 $post_key = (!empty($type) ? $type.'_' : '');
3996 $post_key_set = $post_key.$set;
3997
3998 // Create key as per customizer shortcode
3999 $key = $post_key_set.$screen;
4000
4001 // Create option keys for the global and current post type
4002 $option_setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$post_type : $params['key'].$post_type;
4003
4004 $props = array(
4005 'color' => $post_key_set.'_color'.$screen,
4006 'padding' => $post_key_set.'_padding'.$screen,
4007 'margin' => $post_key_set.'_margin'.$screen,
4008 'background-color' => $post_key_set.'_background_color'.$screen,
4009 );
4010
4011 $globalsetting = array();
4012
4013 // Set Typography
4014 if(isset($customizer_options[$key])){
4015 foreach($customizer_options[$key] as $k => $v){
4016 if($v == 'Default' || empty($v)){
4017 unset($customizer_options[$key][$k]);
4018 }
4019
4020 // For sidebar, width default should not be saved
4021 if($set == 'sidebar' && $k == 'width' && $v == 20){
4022 unset($customizer_options[$key][$k]);
4023 }
4024
4025 $globalsetting[$key][$typo[$k]] = $customizer_options[$key][$k];
4026 unset($customizer_options[$key][$k]);
4027 }
4028 }
4029
4030 // Set other props with typography
4031 foreach($props as $pk => $pv){
4032
4033 if(!isset($customizer_options[$pv])){
4034 continue;
4035 }
4036
4037 $globalsetting[$key][$pk] = $customizer_options[$pv];
4038 unset($customizer_options[$pv]);
4039
4040 }
4041
4042 // Are we to save ?
4043 if(!empty($globalsetting[$key])){
4044 update_option($option_setting.$screen, $globalsetting[$key]);
4045 }else{
4046 delete_option($option_setting.$screen);
4047 }
4048
4049 }
4050
4051 }
4052 }
4053
4054 $customizer_data = apply_filters( 'pagelayer_customizer_options', $customizer_data, $customizer_options);
4055
4056 update_option('pagelayer_customizer_mods', $customizer_data);
4057 }
4058
4059 // Get customizer options values
4060 // return array to set customizer block attributes
4061 function pagelayer_get_customizer_options(){
4062 global $pagelayer, $post;
4063
4064 $options = array();
4065
4066 // To load customizer params array
4067 if(empty($pagelayer->customizer_params)){
4068 pagelayer_load_shortcodes();
4069 }
4070
4071 // Get values form options
4072 foreach($pagelayer->customizer_params as $ck => $cv){
4073
4074 if( empty($cv['option']) ){
4075 continue;
4076 }
4077
4078 $screens = array('desktop' => '');
4079
4080 if( !empty($cv['screen']) ){
4081 $screens = $pagelayer->screens;
4082 }
4083
4084 foreach($screens as $sk => $sv){
4085
4086 $screen = (!empty($sv) ? '_'.$sv : '');
4087 $screen_key = $cv['option'].$screen;
4088 $screen_ck = $ck.$screen;
4089
4090 // Get option value
4091 $c_option = get_option($screen_key);
4092
4093 if(empty($c_option)){
4094 continue;
4095 }
4096
4097 $options[$screen_ck] = $c_option;
4098 }
4099 }
4100
4101
4102 // Get values from pagelayer mods
4103 $mods = get_option('pagelayer_customizer_mods');
4104
4105 if(!empty($mods)){
4106 $options = array_merge($options, $mods);
4107 }
4108
4109 $posttype=array(
4110 '' => '',
4111 'current' => $post->post_type
4112 );
4113
4114 $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4115
4116 foreach($posttype as $type => $tv){
4117
4118 foreach($pagelayer->css_settings as $set => $params){
4119
4120 foreach($pagelayer->screens as $sk => $sv){
4121
4122 $screen = (!empty($sv) ? '_'.$sv : '');
4123 $postprefix = (!empty($tv) ? '_'.$tv : '');
4124 $postk = (!empty($type) ? $type.'_' : '');
4125 $post_key_set = $postk.$set;
4126
4127 // Create key as per customizer shortcode
4128 $key = $post_key_set.$screen;
4129
4130 // Create option keys for the global and current post type
4131 $setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$postprefix : $params['key'].$postprefix;
4132
4133 $props = array(
4134 'color' => $post_key_set.'_color'.$screen,
4135 'padding' => $post_key_set.'_padding'.$screen,
4136 'margin' => $post_key_set.'_margin'.$screen,
4137 'background-color' => $post_key_set.'_background_color'.$screen,
4138 );
4139
4140 $optarray = get_option($setting.$screen);
4141
4142 if(!empty($optarray)){
4143
4144 // Get Typography
4145 foreach($typo as $tk => $tv){
4146
4147 $tval = '';
4148
4149 if(isset($optarray[$tv])){
4150 $tval = $optarray[$tv];
4151 }
4152
4153 $options[$key][$tk] = $tval;
4154 }
4155
4156 foreach($props as $pk => $pv){
4157 if(empty( $optarray[$pk])){
4158 continue;
4159 }
4160
4161 $options[$pv] = $optarray[$pk];
4162 }
4163 }
4164 }
4165 }
4166 }
4167
4168 $options = apply_filters( 'pagelayer_get_customizer_options', $options);
4169
4170 return $options;
4171 }
4172
4173 // Sanitize style props for colors and fons
4174 function pagelayer_load_global_palette(){
4175 global $pagelayer;
4176
4177 // Default global colors
4178 $pagelayer->global_colors = array(
4179 'primary' => array(
4180 'title' => 'Primary',
4181 'value' => '#007bff',
4182 ),
4183 'secondary' => array(
4184 'title' => 'Secondary',
4185 'value' => '#6c757d',
4186 ),
4187 'text' => array(
4188 'title' => 'Text',
4189 'value' => '#1d1d1d',
4190 ),
4191 'accent' => array(
4192 'title' => 'Accent',
4193 'value' => '#61ce70',
4194 ),
4195 );
4196
4197 // Global color
4198 $global_color = get_option('pagelayer_global_colors');
4199
4200 if(!empty($global_color)){
4201 $pagelayer->global_colors = json_decode($global_color, true);
4202 }
4203
4204 // Default global fonts
4205 $pagelayer->global_fonts = array(
4206 'primary' => array(
4207 'title' => 'Primary',
4208 'value' => $pagelayer->default_font_styles(),
4209 ),
4210 'secondary' => array(
4211 'title' => 'Secondary',
4212 'value' => $pagelayer->default_font_styles(array('font-family' => 'Roboto')),
4213 ),
4214 'text' => array(
4215 'title' => 'Text',
4216 'value' => $pagelayer->default_font_styles(array('font-family' => 'Montserrat')),
4217 ),
4218 'accent' => array(
4219 'title' => 'Accent',
4220 'value' => $pagelayer->default_font_styles(array('font-family' => 'Poppins')),
4221 ),
4222 );
4223
4224 // Global font
4225 $global_font = get_option('pagelayer_global_fonts');
4226
4227 if(!empty($global_font)){
4228 $pagelayer->global_fonts = json_decode($global_font, true);
4229 }
4230 }
4231
4232 // Get global colors
4233 function pagelayer_sanitize_global_color($val){
4234 global $pagelayer;
4235
4236 if(!is_string($val) || $val[0] != '$'){
4237 return $val;
4238 }
4239
4240 $gkey = substr($val, 1);
4241 $gcolor = isset($pagelayer->global_colors[$gkey]) ? $gkey : 'primary';
4242 $val = 'var(--pagelayer-color-'.$gcolor.')';
4243
4244 return $val;
4245 }
4246
4247 // Sanitize style props for colors and fons
4248 function pagelayer_sanitize_global_style($val, $val_desk = '', $mode = 'desktop'){
4249 global $pagelayer;
4250
4251 // Sanitize Global background color
4252 if( !empty($val['background-color']) && $val['background-color'][0] == '$'){
4253 $val['background-color'] = pagelayer_sanitize_global_color($val['background-color']);
4254 }
4255
4256 // Sanitize Global color
4257 if( !empty($val['color']) && $val['color'][0] == '$'){
4258 $val['color'] = pagelayer_sanitize_global_color($val['color']);
4259 }
4260
4261 // Not set global for typo
4262 if(empty($val['global-font']) && empty($val_desk['global-font'])){
4263 return $val;
4264 }
4265
4266 $_desk_global = false;
4267
4268 if(empty($val['global-font'])){
4269 $font_key = @$val_desk['global-font'];
4270 $_desk_global = true;
4271 }else{
4272 $font_key = $val['global-font'];
4273 unset($val['global-font']);
4274 }
4275
4276 if(empty($font_key)){
4277 return $val;
4278 }
4279
4280 if(!isset($pagelayer->global_fonts[$font_key])){
4281 $font_key = 'primary';
4282 }
4283
4284 $_val = [];
4285
4286 // Apply global typo
4287 foreach($pagelayer->typo_props as $typo_key){
4288
4289 if(!empty($val[$typo_key])){
4290 continue;
4291 }
4292
4293 $global_val = $pagelayer->global_fonts[$font_key]['value'];
4294
4295 if( empty($global_val[$typo_key]) || (is_array($global_val[$typo_key]) && empty($global_val[$typo_key][$mode])) || (!is_array($global_val[$typo_key]) && !empty($_desk_global) && $mode != 'desktop') ){
4296 continue;
4297 }
4298
4299 $_val[$typo_key] = 'var(--pagelayer-font-'.$font_key.'-'.$typo_key.')';
4300 }
4301
4302 // This array contains other values like margin padding so we merge the array
4303 $val = empty($val) ? array() : $val;
4304 $val = array_merge($val, $_val);
4305 return $val;
4306 }
4307
4308 //Check an empty array
4309 function pagelayer_is_empty_array($arr){
4310
4311 foreach ($arr as $key => $value) {
4312 if (trim($value) !== '') return false;
4313 }
4314
4315 return true;
4316 }
4317
4318 // Pagelayer load font family
4319 function pagelayer_load_font_family($font, $font_weight='', $font_style=''){
4320 global $pagelayer;
4321
4322 // Load global fonts
4323 if(strripos($font, 'var(') !== false){
4324 $matches = [];
4325 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-family\))/i', $font, $matches);
4326 $font_key = @$matches[2];
4327 $font = @$pagelayer->global_fonts[$font_key]['value']['font-family'];
4328 }
4329
4330 // Load global fonts weight
4331 if(!empty($font_weight) && strripos($font_weight, 'var(') !== false){
4332 $matches = [];
4333 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-weight\))/i', $font_weight, $matches);
4334 $font_key = @$matches[2];
4335
4336 // Convert in string
4337 $font_weight = @$pagelayer->global_fonts[$font_key]['value']['font-weight'];
4338 }
4339
4340 // Load global fonts style
4341 if(!empty($font_style) && strripos($font_style, 'var(') !== false){
4342 $matches = [];
4343 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-style\))/i', $font_style, $matches);
4344 $font_key = @$matches[2];
4345
4346 // Convert in string
4347 $font_style = @$pagelayer->global_fonts[$font_key]['value']['font-style'];
4348 }
4349
4350 if(empty($font)){
4351 return;
4352 }
4353
4354 $font_weights = array('400' =>'400');
4355 $font_style = empty($font_style) ? 'normal' : $font_style;
4356 $font_style = in_array($font_style, ['italic', 'oblique']) ? 'i' : '';
4357
4358 if(!empty($font_weight)){
4359
4360 if(!is_array($font_weight)){
4361 $font_weight = array($font_weight);
4362 }
4363
4364 foreach($font_weight as $weight){
4365 $font_weights[$weight.$font_style] = $weight.$font_style;
4366 }
4367 }
4368
4369 foreach($font_weights as $fkey => $fvalue){
4370 $pagelayer->runtime_fonts[$font][$fkey] = $fvalue;
4371 }
4372
4373 }
4374
4375 function pagelayer_update_plugin_notice(){
4376 if(defined('SOFTACULOUS_PLUGIN_UPDATE_NOTICE')){
4377 return;
4378 }
4379
4380 $to_update_plugins = apply_filters('softaculous_plugin_update_notice', []);
4381
4382 if(empty($to_update_plugins)){
4383 return;
4384 }
4385
4386 /* translators: %1$s is replaced with a "string" of name of plugins, and %2$s is replaced with "string" which can be "is" or "are" based on the count of the plugin */
4387 $msg = sprintf(__('New versions of %1$s %2$s available. Updating ensures better performance, security, and access to the latest features.', 'pagelayer'), '<b>'.esc_html(implode(', ', $to_update_plugins)).'</b>', (count($to_update_plugins) > 1 ? 'are' : 'is')) . ' <a class="button button-primary" href='.esc_url(admin_url('plugins.php?plugin_status=upgrade')).'>Update Now</a>';
4388
4389 define('SOFTACULOUS_PLUGIN_UPDATE_NOTICE', true); // To make sure other plugins don't return a Notice
4390 echo '<div class="notice notice-info is-dismissible" id="pagelayer-plugin-update-notice">
4391 <p>'.$msg. '</p>
4392 </div>';
4393
4394 wp_register_script('pagelayer-update-notice', '', ['jquery'], '', true);
4395 wp_enqueue_script('pagelayer-update-notice');
4396 wp_add_inline_script('pagelayer-update-notice', 'jQuery("#pagelayer-plugin-update-notice").on("click", function(e){
4397 let target = jQuery(e.target);
4398
4399 if(!target.hasClass("notice-dismiss")){
4400 return;
4401 }
4402
4403 var data;
4404
4405 // Hide it
4406 jQuery("#pagelayer-plugin-update-notice").hide();
4407
4408 // Save this preference
4409 jQuery.post("'.admin_url('admin-ajax.php?action=pagelayer_close_update_notice').'&pagelayer_nonce='.wp_create_nonce('pagelayer_promo_nonce').'", data, function(response) {
4410 //alert(response);
4411 });
4412 });');
4413 }
4414
4415 function pagelayer_update_plugin_notice_filter($plugins = []){
4416 $plugins['pagelayer/pagelayer.php'] = 'Pagelayer';
4417 return $plugins;
4418 }
4419
4420 function pagelayer_is_comment_mode(){
4421
4422 if(file_exists(PAGELAYER_DIR.'/comment_mode.php') && !empty($_REQUEST['cmode'])){
4423 include_once(PAGELAYER_DIR.'/comment_mode.php');
4424 return true;
4425 }
4426
4427 return false;
4428 }
4429
4430 function pagelayer_esc_crlf($value){
4431
4432 // Remove CRLF to prevent header injection
4433 $value = str_replace(array("\r", "\n", "%0a", "%0d"), '', $value);
4434
4435 // Trim spaces
4436 $value = trim($value);
4437
4438 return $value;
4439 }