PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 2.2.1
Page Builder: Pagelayer – Drag and Drop website builder v2.2.1
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.2.1, at main/functions.php

4,455 lines 111.0 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 // Walk a parsed block tree and sanitize every Pagelayer block in it, at any depth
1363 function pagelayer_sanitize_block_tree($block){
1364
1365 $block_name = isset($block['blockName']) ? $block['blockName'] : '';
1366
1367 // Is pagelayer block ? pagelayer_sanitize_blocks_save_pre() walks the
1368 // whole subtree, so we are done for this branch
1369 if(is_string($block_name) && 0 === strpos($block_name, 'pagelayer/')){
1370 return pagelayer_sanitize_blocks_save_pre($block);
1371 }
1372
1373 // Any other block can still hold Pagelayer blocks inside it
1374 if(!empty($block['innerBlocks']) && is_array($block['innerBlocks'])){
1375 foreach($block['innerBlocks'] as $k => $inner){
1376 $block['innerBlocks'][$k] = pagelayer_sanitize_block_tree($inner);
1377 }
1378 }
1379
1380 return $block;
1381 }
1382
1383 // Check for XSS codes in our shortcode attributes
1384 function pagelayer_sanitize_shortcode_atts($content){
1385
1386 // Do we have something suspicious ?
1387 $tmp_check = pagelayer_xss_content($content);
1388 if(empty($tmp_check)){
1389 return $content;
1390 }
1391
1392 pagelayer_load_shortcodes();
1393
1394 preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
1395
1396 $prefixes = ['pl_'];
1397
1398 $prefixes = apply_filters( 'pagelayer_valid_shortcode_tag', $prefixes);
1399
1400 foreach ($matches as $shortcode) {
1401
1402 $shortcode_name = $shortcode[2];
1403
1404 $vailid = false;
1405
1406 foreach($prefixes as $prefix) {
1407 if (strpos($shortcode_name, $prefix) === 0) {
1408 $vailid = true;
1409 break;
1410 }
1411 }
1412
1413 if(!$vailid){
1414 continue;
1415 }
1416
1417 $attrs = shortcode_parse_atts( $shortcode[3] );
1418 $atts = ' ';
1419
1420 foreach($attrs as $key => $value){
1421
1422 // Skip if key contains XSS
1423 if (!is_numeric($key) && strlen(pagelayer_xss_content($key . '=')) > 0) continue;
1424
1425 $value = wp_filter_post_kses($value);
1426
1427 // Skip if value contains XSS
1428 if (strlen(pagelayer_xss_content('"' . $value)) > 0) continue;
1429
1430 $atts .= is_numeric($key) ? $value . ' ' : $key . '="' . $value . '" ';
1431
1432 }
1433
1434 $new_shortcode = '[' . $shortcode_name . $atts . ']';
1435
1436 if(!empty($shortcode[5])){
1437 // Recurse into the inner content so nested shortcodes
1438 $inner = pagelayer_sanitize_shortcode_atts($shortcode[5]);
1439 $new_shortcode .= $inner.'[/' . $shortcode_name .']';
1440 }
1441
1442 // Replace the original shortcode with sanitized attributes
1443 $content = str_replace($shortcode[0], $new_shortcode, $content);
1444 }
1445
1446 return $content;
1447 }
1448
1449 // Scan post content for XSS payloads and return a structured report.
1450 function pagelayer_xss_scan_post($post_id = 0){
1451 global $post;
1452
1453 $report = ['found' => false, 'items' => []];
1454
1455 if(empty($post_id)){
1456 if(!empty($post->ID)){
1457 $post_id = $post->ID;
1458 }else{
1459 return $report;
1460 }
1461 }
1462
1463 $content = get_post_field('post_content', $post_id);
1464 if(empty($content)){
1465 return $report;
1466 }
1467
1468 // Scan the raw content (shortcodes are not yet expanded here, so we
1469 // catch on*= attributes inside [pl_* ele_attributes="onclick=..."])
1470 $found = pagelayer_xss_content($content);
1471 if(strlen($found) > 0){
1472 $report['found'] = true;
1473 $report['items'][] = $found;
1474 }
1475
1476 return $report;
1477 }
1478
1479 // Determine whether the XSS warning should be shown for the current post/user.
1480 function pagelayer_should_show_xss_warning($post_id = 0){
1481 global $post;
1482
1483 if(empty($post_id)){
1484 if(!empty($post->ID)){
1485 $post_id = $post->ID;
1486 }else{
1487 return false;
1488 }
1489 }
1490
1491 // Rule 5: user already acknowledged — don't block again
1492 $view_token = get_transient('pagelayer_xss_view_'.$post_id.'_'.get_current_user_id());
1493 if(!empty($view_token) && isset($_GET['pl_xss_view']) && $_GET['pl_xss_view'] === $view_token){
1494 return false;
1495 }
1496
1497 // Rule 2: the post must contain XSS
1498 $scan = pagelayer_xss_scan_post($post_id);
1499 if(empty($scan['found'])){
1500 return false;
1501 }
1502
1503 // Rule 3: skip if the current user is the post author
1504 $author_id = (int) get_post_field('post_author', $post_id);
1505 $current_user_id = (int) get_current_user_id();
1506 if($author_id > 0 && $author_id === $current_user_id){
1507 return false;
1508 }
1509
1510 // Rule 4: skip if the post author has the JS/unfiltered_html capability.
1511 // We check this by temporarily switching to the author's context.
1512 $author_can_js = false;
1513 if($author_id > 0){
1514 $author_user = get_userdata($author_id);
1515 if(!empty($author_user)){
1516 $author_can_js = user_can($author_user, 'unfiltered_html');
1517 if(!$author_can_js){
1518 $author_can_js = pagelayer_user_can_add_js_content_for_user($author_user);
1519 }
1520 }
1521 }
1522 if($author_can_js){
1523 return false;
1524 }
1525
1526 return true;
1527 }
1528
1529 // Check if a specific user (not the current user) can add JS content.
1530 function pagelayer_user_can_add_js_content_for_user($user){
1531 if(empty($user) || !($user instanceof WP_User)){
1532 return false;
1533 }
1534
1535 if(user_can($user, 'unfiltered_html')){
1536 return true;
1537 }
1538
1539 $pagelayer_js_permission = get_option('pagelayer_js_permission');
1540 if(empty($pagelayer_js_permission) || empty($user->roles)){
1541 return false;
1542 }
1543
1544 foreach($user->roles as $role){
1545 if(in_array($role, $pagelayer_js_permission)){
1546 return true;
1547 }
1548 }
1549
1550 return false;
1551 }
1552
1553 // Render the XSS warning page that completely blocks content rendering.
1554 function pagelayer_render_xss_warning_block($context = 'frontend'){
1555 global $post;
1556
1557 if(empty($post->ID)){
1558 return;
1559 }
1560
1561 if(!pagelayer_should_show_xss_warning($post->ID)){
1562 return;
1563 }
1564
1565 // Re-scan to get the items for display
1566 $scan = pagelayer_xss_scan_post($post->ID);
1567 if(empty($scan['found'])){
1568 return;
1569 }
1570
1571 $items_html = '';
1572 foreach($scan['items'] as $item){
1573 $items_html .= '<div class="pagelayer-xss-item">'.htmlspecialchars($item, ENT_QUOTES, 'UTF-8').'</div>';
1574 }
1575
1576 // Generate a one-time view token so the admin can explicitly consent
1577 $view_token = wp_generate_password(32, false);
1578 set_transient('pagelayer_xss_view_'.$post->ID.'_'.get_current_user_id(), $view_token, 3600);
1579
1580 // Build the "view page" URL with the consent token
1581 $current_url = $_SERVER['REQUEST_URI'] ?? '/';
1582 $separator = strpos($current_url, '?') !== false ? '&' : '?';
1583 $view_url = htmlspecialchars($current_url.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1584
1585 // Build the edit link
1586 $edit_link = pagelayer_livelink($post->ID);
1587 $edit_link = htmlspecialchars($edit_link.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1588
1589 // Get author info for the warning message
1590 $author_id = (int) get_post_field('post_author', $post->ID);
1591 $author_name = '';
1592 if($author_id > 0){
1593 $author_data = get_userdata($author_id);
1594 if(!empty($author_data)){
1595 $author_name = $author_data->display_name;
1596 }
1597 }
1598
1599 $warning_title = __pl('xss_warning_title');
1600 $warning_body = $author_name
1601 ? sprintf(__pl('xss_warning_body_author'), htmlspecialchars($author_name, ENT_QUOTES, 'UTF-8'))
1602 : __pl('xss_warning_body');
1603 $warning_items_label = __pl('xss_warning_items');
1604 $view_btn = __pl('xss_warning_view_page');
1605 $edit_btn = __pl('xss_warning_edit_post');
1606 $warning_blocked = __pl('xss_warning_blocked');
1607
1608 $dashboard_link = admin_url();
1609 $go_dashboard = __pl('xss_warning_go_dashboard');
1610
1611 // Output the warning page and die() — the malicious post content is
1612 // NEVER sent to the browser, so inline <script>, <img onerror>, and
1613 // <iframe onload> payloads cannot execute.
1614 $status_code = ($context === 'editor') ? 200 : 403;
1615 if(!headers_sent()){
1616 status_header($status_code);
1617 header('Content-Type: text/html; charset=utf-8');
1618 }
1619
1620 echo <<<HTML
1621 <!DOCTYPE html>
1622 <html lang="en">
1623 <head>
1624 <meta charset="utf-8">
1625 <meta name="viewport" content="width=device-width, initial-scale=1">
1626 <title>{$warning_title}</title>
1627 <style>
1628 *{ margin:0; padding:0; box-sizing:border-box; }
1629 body{
1630 background:#f0f0f1;
1631 font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
1632 display:flex;
1633 align-items:center;
1634 justify-content:center;
1635 min-height:100vh;
1636 padding:20px;
1637 }
1638 #pagelayer-xss-block-dialog{
1639 background:#fff;
1640 border-radius:8px;
1641 max-width:640px;
1642 width:100%;
1643 max-height:90vh;
1644 overflow-y:auto;
1645 box-shadow:0 4px 24px rgba(0,0,0,0.15);
1646 }
1647 .pagelayer-xss-block-header{
1648 background:#d63638;
1649 color:#fff;
1650 padding:20px 24px;
1651 border-radius:8px 8px 0 0;
1652 font-size:20px;
1653 font-weight:700;
1654 display:flex;
1655 align-items:center;
1656 gap:12px;
1657 }
1658 .pagelayer-xss-block-body{
1659 padding:24px;
1660 color:#1d2327;
1661 font-size:14px;
1662 line-height:1.6;
1663 }
1664 .pagelayer-xss-block-body p{
1665 margin:0 0 14px 0;
1666 }
1667 .pagelayer-xss-blocked-tag{
1668 display:inline-block;
1669 background:#fff4f4;
1670 border:1px solid #d63638;
1671 color:#d63638;
1672 padding:3px 10px;
1673 border-radius:3px;
1674 font-size:12px;
1675 font-weight:600;
1676 margin-bottom:14px;
1677 }
1678 .pagelayer-xss-items{
1679 background:#fff4f4;
1680 border:1px solid #d63638;
1681 border-radius:4px;
1682 padding:14px;
1683 margin:14px 0;
1684 max-height:220px;
1685 overflow-y:auto;
1686 }
1687 .pagelayer-xss-item{
1688 font-family:monospace;
1689 font-size:12px;
1690 padding:6px 8px;
1691 border-bottom:1px solid #f5cccc;
1692 word-break:break-all;
1693 color:#b32d2e;
1694 }
1695 .pagelayer-xss-item:last-child{
1696 border-bottom:none;
1697 }
1698 .pagelayer-xss-block-actions{
1699 padding:18px 24px;
1700 border-top:1px solid #dcdcde;
1701 display:flex;
1702 gap:12px;
1703 flex-wrap:wrap;
1704 border-radius:0 0 8px 8px;
1705 }
1706 .pagelayer-xss-btn-view{
1707 background:#2271b1;
1708 color:#fff;
1709 border:none;
1710 border-radius:4px;
1711 padding:10px 24px;
1712 font-size:14px;
1713 cursor:pointer;
1714 font-weight:600;
1715 text-decoration:none;
1716 display:inline-block;
1717 }
1718 .pagelayer-xss-btn-view:hover{
1719 background:#135e96;
1720 }
1721 .pagelayer-xss-btn-edit{
1722 background:#fff;
1723 color:#2271b1;
1724 border:1px solid #2271b1;
1725 border-radius:4px;
1726 padding:10px 24px;
1727 font-size:14px;
1728 cursor:pointer;
1729 font-weight:600;
1730 text-decoration:none;
1731 display:inline-block;
1732 }
1733 .pagelayer-xss-btn-edit:hover{
1734 background:#f0f6fc;
1735 }
1736 .pagelayer-xss-btn-dashboard{
1737 background:transparent;
1738 color:#646970;
1739 border:none;
1740 border-radius:4px;
1741 padding:10px 16px;
1742 font-size:13px;
1743 cursor:pointer;
1744 text-decoration:none;
1745 display:inline-block;
1746 margin-left:auto;
1747 }
1748 .pagelayer-xss-btn-dashboard:hover{
1749 color:#1d2327;
1750 }
1751 </style>
1752 </head>
1753 <body>
1754 <div id="pagelayer-xss-block-dialog">
1755 <div class="pagelayer-xss-block-header">
1756 <span style="font-size:28px;">&#9888;</span>
1757 {$warning_title}
1758 </div>
1759 <div class="pagelayer-xss-block-body">
1760 <span class="pagelayer-xss-blocked-tag">{$warning_blocked}</span>
1761 <p>{$warning_body}</p>
1762 <p style="font-weight:600; margin-bottom:6px;">{$warning_items_label}:</p>
1763 <div class="pagelayer-xss-items">{$items_html}</div>
1764 </div>
1765 <div class="pagelayer-xss-block-actions">
1766 <a href="{$view_url}" class="pagelayer-xss-btn-view">{$view_btn}</a>
1767 <a href="{$edit_link}" class="pagelayer-xss-btn-edit">{$edit_btn}</a>
1768 <a href="{$dashboard_link}" class="pagelayer-xss-btn-dashboard">{$go_dashboard}</a>
1769 </div>
1770 </div>
1771 </body>
1772 </html>
1773 HTML;
1774
1775 // CRITICAL: die() here ensures the malicious post content (the_content,
1776 // do_shortcode output, block rendering) is NEVER sent to the browser.
1777 // Without die(), WordPress would continue rendering the template and
1778 // output the post body — including any <script>/<img onerror>/<iframe
1779 // onload> payloads — which would execute even if hidden with CSS.
1780 die();
1781 }
1782
1783 function pagelayer_getting_started_notice(){
1784
1785 // Is Sitepad setup done?
1786 $setup_done = get_option('sp_setup_done');
1787
1788 if(defined('SITEPAD') && empty($setup_done)){
1789 return;
1790 }
1791
1792 // If SitePad used custom BRAND SM
1793 if(defined('BRAND_SM_CUSTOM')){
1794 return;
1795 }
1796
1797 echo '
1798 <script type="application/javascript">
1799 jQuery(document).ready(function(){
1800 jQuery("#pagelayer-getting-started-notice").click(function(e){
1801
1802 if(jQuery(e.target).hasClass("notice-dismiss")){
1803 var data;
1804 jQuery("#pagelayer-getting-started-notice").hide();
1805 // Save this preference
1806 jQuery.post("'.admin_url('?pagelayer-getting-started=0&pagelayer_nonce='.wp_create_nonce("pagelayer_getting_started_nonce") ).'", data, function(response) {
1807 //alert(response);
1808 });
1809 return false;
1810 }
1811
1812 });
1813 });
1814 </script>
1815
1816 <div id="pagelayer-getting-started-notice" class="notice notice-success is-dismissible">
1817 <p style="font-size: 14px; font-weight: 600">';
1818 if(defined('SITEPAD')){
1819
1820 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.'.');
1821
1822 }else{
1823
1824 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');
1825
1826 }
1827
1828 echo '</p>
1829 </div>';
1830
1831 }
1832
1833 // Show Changelog promo
1834 function pagelayer_show_changelog_notice(){
1835
1836 // Is Sitepad setup done?
1837 if(defined('SITEPAD')){
1838 return;
1839 }
1840
1841 echo '
1842 <script type="application/javascript">
1843 jQuery(document).ready(function(){
1844 jQuery("#pagelayer-changelog-notice").click(function(e){
1845
1846 if(jQuery(e.target).hasClass("notice-dismiss")){
1847 var data;
1848 jQuery("#pagelayer-changelog-notice").hide();
1849 // Save this preference
1850 jQuery.post("'.admin_url('?pagelayer-changelog-notice=0&pagelayer_nonce='.wp_create_nonce("pagelayer_changelog_nonce") ).'", data, function(response) {
1851 //alert(response);
1852 });
1853 return false;
1854 }
1855
1856 });
1857 });
1858 </script>
1859
1860 <div id="pagelayer-changelog-notice" class="notice notice-info is-dismissible">
1861 <p style="font-size: 14px; font-weight: 600">
1862 <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') .'
1863 </p>
1864 </div>';
1865
1866 }
1867
1868 // Show promo notice on dashboard
1869 function pagelayer_show_promo(){
1870
1871 global $pagelayer_promo_opts;
1872 $opts = $pagelayer_promo_opts;
1873
1874 echo '<style>
1875 #pagelayer_promo a.pagelayer_promo_button {
1876 background-color: #4CAF50 !important;
1877 border: none !important;
1878 color: white !important;
1879 padding: 6px 10px !important;
1880 text-align: center !important;
1881 text-decoration: none !important;
1882 display: inline-block !important;
1883 font-size: 13px !important;
1884 margin: 4px 2px !important;
1885 -webkit-transition-duration: 0.4s;
1886 transition-duration: 0.4s;
1887 cursor: pointer !important;
1888 }
1889 #pagelayer_promo a.pagelayer_promo_button:focus,
1890 #pagelayer_promo a.pagelayer_promo_button:hover{
1891 border: none !important;
1892 color: white !important;
1893 box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19) !important;
1894 }
1895 #pagelayer_promo a.pagelayer_promo_buy {
1896 color: white !important;
1897 padding: 8px 12px !important;
1898 font-size: 14px !important;
1899 background-color: #4CAF50 !important;
1900 }
1901 #pagelayer_promo a.pagelayer_promo_button1 {
1902 color: white !important;
1903 background-color: #4CAF50 !important;
1904 border:3px solid #4CAF50 !important;
1905 }
1906 #pagelayer_promo a.pagelayer_promo_button1:hover {
1907 border:3px solid #4CAF50 !important;
1908 }
1909 #pagelayer_promo a.pagelayer_promo_button2 {
1910 color: white !important;
1911 background-color: #0085ba !important;
1912 }
1913 #pagelayer_promo a.pagelayer_promo_button3 {
1914 color: white !important;
1915 background-color: #365899 !important;
1916 }
1917 #pagelayer_promo a.pagelayer_promo_button4 {
1918 color: white !important;
1919 background-color: rgb(66, 184, 221) !important;
1920 }
1921 #pagelayer_promo a.pagelayer_promo-close{
1922 float:right !important;
1923 text-decoration:none !important;
1924 margin: 5px 10px 0px 0px !important;
1925 color: #666 !important;
1926 }
1927 #pagelayer_promo a.pagelayer_promo-close:hover{
1928 color: red !important;
1929 }
1930 #pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close) {
1931 color: #4CAF50 !important;
1932 text-decoration: underline !important;
1933 }
1934 #pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close):hover {
1935 color: #3e8e41 !important;
1936 }
1937 </style>
1938 <script type="application/javascript">
1939 jQuery(document).ready(function(){
1940 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
1941 var data;
1942 jQuery("#pagelayer_promo").hide();
1943 // Save this preference
1944 jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
1945 //alert(response);
1946 });
1947 });
1948 });
1949 </script>
1950
1951 <div class="notice notice-success" id="pagelayer_promo" style="min-height:90px">
1952 <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
1953 <span class="dashicons dashicons-dismiss"></span> Dismiss
1954 </a>';
1955
1956 if(!empty($opts['image'])){
1957 echo '<a href="'.$opts['website'].'"><img src="'.$opts['image'].'" style="float:left; margin:10px 20px 10px 10px" width="67" /></a>';
1958 }
1959
1960 echo '
1961 <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>
1962 <p>
1963 '.(empty($opts['pro_url']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_buy" target="_blank" href="'.$opts['pro_url'].'">Buy Pagelayer Pro</a>').'
1964 '.(empty($opts['rating']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button2" target="_blank" href="'.$opts['rating'].'">Rate it 5�
1965 \'s</a>').'
1966 '.(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>').'
1967 '.(empty($opts['twitter']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['twitter'].'"><span class="dashicons dashicons-twitter"></span> Tweet</a>').'
1968 '.(empty($opts['website']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['website'].'">Visit our website</a>').'
1969 </p>
1970 <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.';
1971
1972 if(date('Ymd') <= 20200331){
1973 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>.';
1974 }
1975
1976 echo '</p></div>';
1977
1978 }
1979
1980 // Are we to show a promo ?
1981 function pagelayer_maybe_promo($opts){
1982
1983 global $pagelayer_promo_opts;
1984
1985 // There must be an interval
1986 if(!current_user_can('activate_plugins')){
1987 return false;
1988 }
1989
1990 // There must be an interval
1991 if(empty($opts['interval'])){
1992 return false;
1993 }
1994
1995 // Are we to show a promo
1996 $opt_name = 'pagelayer_promo_time';
1997 $promo_time = get_option($opt_name);
1998
1999 // First time access
2000 if(empty($promo_time)){
2001 update_option($opt_name, time() + (!empty($opts['after']) ? $opts['after'] * 86400 : 0));
2002 $promo_time = get_option($opt_name);
2003 }
2004
2005 // Is there interval elapsed
2006 if(time() > $promo_time){
2007 $pagelayer_promo_opts = $opts;
2008 add_action('admin_notices', 'pagelayer_show_promo');
2009 }
2010
2011 // Are we to disable the promo
2012 if(isset($_GET['pagelayer_promo']) && (int)$_GET['pagelayer_promo'] == 0){
2013 update_option($opt_name, time() + ($opts['interval'] * 86400));
2014 die('DONE');
2015 }
2016
2017 }
2018
2019 // Show the Pro notice
2020 function pagelayer_show_pro_notice(){
2021
2022 if(defined('PAGELAYER_PREMIUM')){
2023 return;
2024 }
2025
2026 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>';
2027
2028 }
2029
2030 // Show the Pro Div
2031 function pagelayer_show_pro_div($head = '', $message = '', $admin_css = 1){
2032
2033 if(defined('PAGELAYER_PREMIUM')){
2034 return;
2035 }
2036
2037 if(basename(get_template_directory()) == 'popularfx'){
2038 $pro_url = 'https://popularfx.com/pricing?from=pagelayer-plugin';
2039 $pro_txt = 'PopularFX Pro';
2040 }else{
2041 $pro_url = PAGELAYER_PRO_PRICE_URL;
2042 $pro_txt = 'Pagelayer Pro';
2043 }
2044
2045 if(!empty($admin_css)){
2046 wp_enqueue_style( 'pagelayer-admin', PAGELAYER_CSS.'/pagelayer-admin.css', array(), PAGELAYER_VERSION);
2047 }
2048
2049 echo '<div class="pagelayer-pro-div">';
2050
2051 if(!empty($head)){
2052 echo '<h1 class="pagelayer-pro-head">'.$head.'</h1>';
2053 }
2054
2055 echo '<div class="pagelayer-pro-message">';
2056
2057 if(empty($message)){
2058
2059 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.');
2060
2061 }else{
2062
2063 echo $message;
2064 echo ' '.__('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>.');
2065
2066 }
2067
2068 echo '</div>
2069 <center><a class="button-pagelayer" href="'.$pro_url.'" target="_blank" style="text-decoration: none;">'.sprintf(__('Upgrade to %s', 'pagelayer'), $pro_txt).'</a></center>
2070 </div>';
2071
2072 }
2073
2074 // Bread Crumbs with links
2075 function pagelayer_get_breadcrumb(){
2076 global $post;
2077
2078 // Home page
2079 $ret = '<a href="'.home_url().'" rel="nofollow">{{home}}</a>';
2080
2081 // Is Front page
2082 if(is_front_page()){
2083 return $ret;
2084
2085 // Is search query
2086 }elseif(is_search()){
2087 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><b class="pagelayer-breadcrumb-prefix" if="{{search_prefix}}">{{search_prefix}}</b>';
2088 $ret .= '<em>';
2089 $ret .= '<span>'.get_search_query().'</span>';
2090 $ret .= '</em>';
2091
2092 //Is category or single post
2093 }elseif(is_category() || is_single()){
2094
2095 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2096 $categories = get_the_category();
2097 $separator = ' ';
2098 $output = '';
2099 if(!empty($categories)){
2100 $ret .= get_category_parents($categories[0], true, ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ');
2101 }
2102 if(is_single()) {
2103 /* if (empty($categories)){
2104 $ret .= ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ';
2105 } */
2106 $ret .= '<span>'.get_the_title().'</span>';
2107 }
2108
2109 // Is page
2110 }elseif(is_page() ){
2111 if(!empty($post->post_parent)){
2112 $ancestors = array_reverse(get_post_ancestors( $post->ID ));
2113 $page_on_front = get_option('page_on_front');
2114
2115 foreach( $ancestors as $ancestor ){
2116
2117 if($page_on_front == $ancestor){
2118 continue;
2119 }
2120
2121 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><a href="'.get_permalink($ancestor).'">'.get_the_title($ancestor).'</a>';
2122 }
2123
2124 $ret .=' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><span>'.get_the_title().'</span>';
2125
2126 }else{
2127 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2128 $ret .= '<span>'.get_the_title().'</span>';
2129 }
2130 }else{
2131 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2132 $ret .= '<span>'.wp_title('', false ).'</span>';
2133 }
2134
2135 // wooCommerce Integration left
2136
2137 return $ret;
2138 }
2139
2140 // Portfolio Posts
2141 function pagelayer_widget_posts($args){
2142
2143 $r = new \WP_Query($args);
2144
2145 $ret = '';
2146 if ( $r->have_posts() ){
2147
2148 if($args['filter_by'] != 'none' && $args['post_type'] == 'post'){
2149 $ret .= '<div class="pagelayer-postfolio-filter">';
2150
2151 $data_filter = ( $args['filter_by'] == 'category' ? get_categories() : get_tags() );
2152
2153 if(!empty($data_filter)){
2154
2155 $ret .= '<div class="pagelayer-postfolio-btn" data-filter="all">'.__pl('All').'</div>';
2156
2157 foreach($data_filter as $filter) {
2158 $ret .= '<div class="pagelayer-postfolio-btn" data-filter="'.$filter->name.'">' . $filter->name . '</div>';
2159 }
2160
2161 }
2162
2163 $ret .= '</div>';
2164 }
2165
2166 $ret .= '<div class="pagelayer-postfolio-container">';
2167
2168 while ( $r->have_posts() ) : $r->the_post();
2169 $post_meta = ( $args['filter_by'] == 'category' ? get_the_category() : get_the_tags() );
2170 $meta_attr = '';
2171 if($post_meta){
2172
2173 $meta_array = array();
2174 foreach( $post_meta as $meta ){
2175 $meta_array[] = $meta->name;
2176 }
2177 $meta_attr .= ' data-category="'.implode(' ', $meta_array).'"';
2178 }
2179
2180 $ret .= '<div class="pagelayer-postfolio-thumb"'.(has_post_thumbnail() ? ' style="background:url('.get_the_post_thumbnail_url().')"' : ''). $meta_attr .'>';
2181 $ret .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark">';
2182 $ret .= '<div class="pagelayer-postfolio-content">';
2183 $ret .= '<h4 class="pagelayer-entry-title">'.get_the_title().'</h4>';
2184 $ret .= '</div></a></div>';
2185
2186 endwhile;
2187
2188 $ret .= '</div>';
2189
2190 }
2191
2192 return $ret;
2193 }
2194
2195 // List all available sizes of images registered on WordPress
2196 function pagelayer_image_sizes(){
2197
2198 $sizes = array();
2199 $sizes = get_intermediate_image_sizes();
2200 $ret = array();
2201 foreach($sizes as $size){
2202 $ret[$size] = __pl($size);
2203 }
2204
2205 return $ret;
2206 }
2207
2208 function pagelayer_remove_excerpt_more($more){
2209 return '';
2210 }
2211
2212 function pagelayer_posts($params, $args = []){
2213 global $post, $wp_query;
2214
2215 if(isset($params['exc_length'])){
2216 $exc_length = (int) $params['exc_length'];
2217 add_filter( 'excerpt_length', function($length) use($exc_length){
2218 return $exc_length;
2219 }, 999 );
2220 }
2221
2222 // add_filter('excerpt_more', 'pagelayer_remove_excerpt_more', 999);
2223
2224 // If args is empty
2225 if(empty($args)){
2226 $args = array(
2227 'post_type' => $params['post_type'],
2228 'posts_per_page' => $params['posts_per_page'],
2229 'order' => $params['order']
2230 );
2231
2232 if(!empty($params['paged'])){
2233 $args['paged'] = $params['paged'];
2234 }
2235
2236 if (!empty($params['term']) || !empty($params['exc_term'])) {
2237 $tax_query = [];
2238
2239 if (!empty($params['term'])) {
2240 $terms = explode(',', $params['term']);
2241 $include = array_reduce($terms, function ($carry, $term) {
2242 list($taxonomy, $slug, $id) = explode(':', $term);
2243 $carry[$taxonomy][] = $slug;
2244 return $carry;
2245 }, []);
2246
2247 $filters = array_filter([
2248 isset($include['category']) ? [
2249 'taxonomy' => 'category',
2250 'field' => 'slug',
2251 'terms' => $include['category'],
2252 'include_children' => false,
2253 ] : null,
2254 isset($include['post_tag']) ? [
2255 'taxonomy' => 'post_tag',
2256 'field' => 'slug',
2257 'terms' => $include['post_tag'],
2258 'include_children' => false,
2259 ] : null,
2260 ]);
2261
2262 if(!empty($filters)){
2263 $tax_query[] = array_merge(['relation' => 'OR'], $filters);
2264 }
2265 }
2266
2267 // Handle exclusions
2268 if (!empty($params['exc_term'])) {
2269 $terms = explode(',', $params['exc_term']);
2270 $include = array_reduce($terms, function ($carry, $term) {
2271 list($taxonomy, $slug, $id) = explode(':', $term);
2272 $carry[$taxonomy][] = $slug;
2273 return $carry;
2274 }, []);
2275
2276 $filters = array_filter([
2277 isset($include['category']) ? [
2278 'taxonomy' => 'category',
2279 'field' => 'slug',
2280 'terms' => $include['category'],
2281 'operator' => 'NOT IN',
2282 'include_children' => false,
2283 ] : null,
2284 isset($include['post_tag']) ? [
2285 'taxonomy' => 'post_tag',
2286 'field' => 'slug',
2287 'terms' => $include['post_tag'],
2288 'operator' => 'NOT IN',
2289 'include_children' => false,
2290 ] : null,
2291 ]);
2292
2293 if(!empty($filters)){
2294 $tax_query[] = array_merge(['relation' => 'AND'], $filters);
2295 }
2296 }
2297
2298 // Apply the combined tax_query
2299 if(!empty($tax_query)){
2300 $args['tax_query'] = array_merge(['relation' => 'AND'], $tax_query);
2301 }
2302 }
2303
2304 if(!empty($params['author_name'])){
2305 $author_name = explode(':', $params['author_name']);
2306 $args['author_name'] = $author_name[0];
2307 }
2308
2309 if(!empty($params['exc_author'])){
2310 $exc_author = explode(':', $params['exc_author']);
2311 $args['author'] = '-'.$exc_author[1];
2312 }
2313
2314 if (!empty($params['offset'])) {
2315 $args['offset'] = (int) $params['offset'];
2316
2317 // Make Compatible for Infinite load
2318 if(!empty($params['infinite_types']) && !empty($params['paged']) && $params['paged'] > 1 ){
2319 $args['offset'] = $args['offset'] + (($params['paged'] - 1) * $params['posts_per_page']);
2320 }
2321 }
2322
2323 if(!empty($params['ignore_sticky'])){
2324 $args['ignore_sticky_posts'] = $params['ignore_sticky'];
2325 }
2326
2327 if(!empty($params['orderby'])){
2328 $args['orderby'] = $params['orderby'];
2329 }
2330
2331 if(!empty($params['by_period'])){
2332 $date_arg = array();
2333 switch($params['by_period']){
2334 case 'last_day':
2335 $date_arg['day'] = date('j')-1;
2336 break;
2337 case 'last_week':
2338 $date_arg['week'] = date('W')-1;
2339 break;
2340 case 'last_month':
2341 $date_arg['month'] = date('n')-1;
2342 break;
2343 case 'last_year':
2344 $date_arg['year'] = date('Y')-1;
2345 break;
2346 case 'custom':
2347 $date_arg['before'] = $params['before_date'];
2348 $date_arg['after'] = $params['after_date'];
2349 break;
2350 }
2351 $args['date_query'] = array(
2352 $date_arg
2353 );
2354 }
2355 }
2356 //pagelayer_print($args);
2357
2358 // Only see published posts
2359 $args['post_status'] = 'publish';
2360
2361 $postsquery = new WP_Query($args);
2362 $data = '';
2363
2364 if(!wp_doing_ajax() && !empty($params['infinite_types'])){
2365 $data.='<div class="pagelayer-post-max" data-max="'.$postsquery->max_num_pages.'"></div>';
2366 }
2367
2368 if(!$postsquery->have_posts()){
2369 return '<h3>No posts found!</h3>';
2370 }
2371
2372 // To reset the post when the $wp_query->post is empty
2373 $orig_post = $post;
2374
2375 while($postsquery->have_posts()) : $postsquery->the_post();
2376 $data .= '<div class="pagelayer-wposts-col">
2377 <div class="pagelayer-wposts-post">
2378 <div class="pagelayer-wposts-featured">';
2379 $data .= '<a href="'. get_the_permalink() .'">';
2380
2381 if(!empty($params['show_thumb'])){
2382 $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>';
2383 }
2384 /* if($params['show_thumb'] && has_post_thumbnail( $postsquery->ID )){
2385 $data .= get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']);
2386 } */
2387 $data .= '</a></div>
2388 <div class="pagelayer-wposts-content">';
2389
2390 if(!empty($params['show_title'])){
2391 $data .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark"><div class="pagelayer-wposts-title">'. get_the_title().'</div></a>';
2392 }
2393
2394 $data .= '<div class="pagelayer-wposts-meta">';
2395 $sep = '';
2396 if(!empty($params['meta_sep'])){
2397 $sep = ' <b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b> ';
2398 }
2399 if(!empty($params['author'])){
2400 $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;
2401 }
2402 if(!empty($params['date'])){
2403 $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;
2404 }
2405
2406 if(!empty($params['category'])){
2407 $category = get_the_category();
2408 $singlecategory = '';
2409 foreach( $category as $cat ){
2410 $singlecategory .= '<a href="' . get_tag_link( $cat->term_id ) . '">'. $cat->name .'</a>';
2411 }
2412
2413 if(!empty($singlecategory)){
2414 $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2415 }
2416
2417 }
2418
2419 if(!empty($params['tags'])){
2420 $tags = get_the_tags();
2421 $singletag = '';
2422 if(!empty($tags)){
2423 foreach( $tags as $tag ){
2424 $singletag .= '<a href="' . get_tag_link( $tag->term_id ) . '">'. $tag->name .'</a>';
2425 }
2426 if(!empty($singletag)){
2427 $data .= '<span class="pagelayer-wposts-tags">'.$singletag.'</span>'.$sep;
2428 }
2429 }
2430
2431 }
2432
2433 if(!empty($params['comments']) && comments_open($postsquery->ID)){
2434 $data .= '<span class="pagelayer-wposts-comments"><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()).' '.__pl('comments').'</a></span>'.$sep;
2435 }
2436
2437 $data .= '</div>';
2438
2439 if(!empty($params['show_content'])){
2440 $data .= '<div class="pagelayer-wposts-excerpt">';
2441
2442 if($params['show_content'] == 'excerpt'){
2443 $post_excerpt = get_the_excerpt();
2444 $trimmed_excerpt = !empty($params['exc_length']) ? wp_trim_words($post_excerpt, (int) $params['exc_length']) : $post_excerpt;
2445 $data .= pagelayer_the_content($trimmed_excerpt);
2446 } elseif ($params['show_content'] == 'full') {
2447 $data .= pagelayer_the_content(get_the_content());
2448 }
2449 $data .= '</div>';
2450 }
2451
2452 if(!empty($params['show_more'])){
2453 $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().'">';
2454
2455 if($params['icon']){
2456 $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2457 }
2458
2459 if($params['more']){
2460 $data .= '<span class="pagelayer-btn-text">'.$params['more'].'</span>';
2461 }
2462
2463 if($params['icon']){
2464 $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2465 }
2466
2467 $data .= '</a></div>';
2468
2469 //$data .= '<a class="pagelayer-wposts-more" href="'. get_the_permalink().'">'.$params['more'].'</a>';
2470 }
2471
2472 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
2473 $data .= '</div></div></div>';
2474 endwhile;
2475
2476 // In the Gutenberg while adding new page the $wp_query->post was empty
2477 if ( !isset( $wp_query ) || empty($wp_query->post) ) {
2478 $GLOBALS['post'] = $orig_post;
2479 }else{
2480 wp_reset_postdata();
2481 }
2482
2483 return $data;
2484 }
2485
2486 // Get Post Revision
2487 function pagelayer_get_post_revision_by_id($postID){
2488
2489 // Insert the post revision into the database
2490 $post_revisions = array();
2491 $reviews = wp_get_post_revisions($postID);
2492
2493 foreach($reviews as $values){
2494
2495 $date_format = date_i18n('j-M @ H:i', strtotime( $values->post_modified ) );
2496 $user_meta = get_userdata($values->post_author);
2497
2498 if ( false !== strpos( $values->post_name, 'autosave' ) ) {
2499 $type = 'autosave';
2500 } else {
2501 $type = 'revision';
2502 }
2503
2504 $post_tmp_data = array(
2505 'ID' => $values->ID,
2506 'post_author_name' => $user_meta->data->display_name,
2507 'post_author_url' => get_avatar_url($values->post_author),
2508 'post_date' => $date_format,
2509 'post_date_ago' => human_time_diff(strtotime($values->post_modified), current_time( 'timestamp' )) . ' ago ',
2510 'post_type' => $type,
2511 );
2512
2513 $post_revisions[] = $post_tmp_data;
2514 }
2515
2516 return $post_revisions;
2517 }
2518
2519 // Gets author data
2520 function pagelayer_author_data($postID){
2521
2522 $authorID = get_post_field('post_author', $postID);
2523 $data['display_name'] = get_the_author_meta( 'display_name' , $authorID );
2524 $data['description'] = get_the_author_meta( 'description' , $authorID );
2525 $data['user_url'] = get_author_posts_url( $authorID , '' );
2526 $data['avatar'] = get_avatar_url( $authorID );
2527
2528 return $data;
2529 }
2530
2531 // Posts Slider
2532 function pagelayer_posts_slider($params){
2533
2534 $args = array();
2535
2536 if(isset($params['post']['post_type'])){
2537 $args['post_type'] = $params['post']['post_type'];
2538 }
2539
2540 if(isset($params['post']['category'])){
2541 $args['category_name'] = $params['post']['category'];
2542 }
2543
2544 if(isset($params['post']['tags'])){
2545 $args['tag'] = $params['post']['tags'];
2546 }
2547
2548 if(isset($params['post']['order_by'])){
2549 $args['orderby'] = $params['post']['order_by'];
2550 }
2551
2552 if(isset($params['post']['sort_order'])){
2553 $args['order'] = $params['post']['sort_order'];
2554 }
2555
2556 if(isset($params['post']['post_count'])){
2557 $args['posts_per_page'] = $params['post']['post_count'];
2558 }
2559
2560 $data = '';
2561 $postsquery = new WP_Query($args);
2562 if ( $postsquery->have_posts() ){
2563 while ( $postsquery->have_posts() ) : $postsquery->the_post();
2564 $data .= '<li class="pagelayer-posts-slider-col">
2565 <div class="pagelayer-posts-slider-post">
2566 <div class="pagelayer-posts-slider-featured-img">';
2567 $data .= '<a href="'. get_the_permalink() .'">';
2568 if(has_post_thumbnail( $postsquery->ID ) ){
2569 if($params['post']['image_size'] == "full" || $params['post']['image_size'] == "custom" ){
2570 $image = wp_get_attachment_image_src( get_post_thumbnail_id( $postsquery->ID ), 'single-post-thumbnail' );
2571 $data .= '<img class="pagelayer-img" src="'.$image[0].'" class="pagelayer-posts-slider-img">';
2572 }else if($params['post']['image_size'] == "thumbnail"){
2573 $data .= get_the_post_thumbnail();
2574 }
2575 }
2576 $data .= '</a></div>
2577 <div class="pagelayer-posts-slider-content">';
2578 $data .= '<p class="pagelayer-posts-slider-title">'. get_the_title().'</p>';
2579 $data .= '<div class="pagelayer-posts-slider-excerpt">';
2580 if($params['post']['show_excerpt'] == "true"){
2581 if(has_excerpt()){
2582 $excerpt = get_the_excerpt();
2583 $data .= pagelayer_the_content($excerpt);
2584 }
2585 }
2586 $data .= '</div>';
2587 $data .= '<a class="pagelayer-posts-slider-link" href="'. get_the_permalink().'">Read More</a>';
2588 $data .= '<div class="pagelayer-post-slider-date"><p>'.get_the_date().'</p></div>';
2589 $data .= '</div></div></li>';
2590 endwhile;
2591 }
2592 return $data;
2593 }
2594
2595 // Gets the site logo URLs
2596 function pagelayer_site_logo(){
2597
2598 if(get_theme_mod('custom_logo')){
2599 $logo_id = get_theme_mod('custom_logo');
2600 return pagelayer_image($logo_id);
2601 }
2602
2603 return NULL;
2604 }
2605
2606 // Create select options
2607 function pagelayer_create_sel_options( $opt_array , $selected = ''){
2608 $options = '';
2609 foreach($opt_array as $x => $val){
2610
2611 // Single item
2612 if(is_string($opt_array[$x])){
2613 $options .= pagelayer_sel_option($x, $val, $selected);
2614
2615 // Groups
2616 }else{
2617
2618 if(array_key_exists('hide_drop', $opt_array[$x]) && !empty($opt_array[$x]['hide_drop'])){
2619 continue;
2620 }
2621
2622 // If Label is there, then its a normal option
2623 if(array_key_exists('label', $opt_array[$x])){
2624 $options .= pagelayer_sel_option($x, $opt_array[$x]['label'], $selected);
2625
2626 // Optgroups
2627 } else{
2628 $options .= '<optgroup label="'. $x .'">';
2629 $options .= pagelayer_create_sel_options($opt_array[$x], $selected);
2630 $options .= '</optgroup>';
2631 }
2632 }
2633 }
2634
2635 return $options;
2636 }
2637
2638 // Create option HTML
2639 function pagelayer_sel_option($val, $lang, $selected){
2640 return '<option value="'. $val .'" '. (($val != $selected) ? '' : 'selected="selected"') .' >'. $lang .'</option>';
2641 }
2642
2643 // Get values from multi-dimensional array by key
2644 function pagelayer_multi_array_search(&$array, $key){
2645
2646 if(!is_array($array)){
2647 return false;
2648 }
2649
2650 foreach ($array as $k => $v) {
2651
2652 if($k == $key){
2653 return $v;
2654 }
2655
2656 if (is_array($v)) {
2657 $found = pagelayer_multi_array_search($v, $key);
2658 if(!empty($found)){
2659 return $found;
2660 }
2661 }
2662 }
2663
2664 return false;
2665 }
2666
2667 function pagelayer_get_post_term(){
2668
2669 $args = [
2670 'taxonomy' => array('category','post_tag'),
2671 'hide_empty' => false,
2672 ];
2673
2674 $terms = get_terms( $args );
2675
2676 $ret = array();
2677 foreach ( $terms as $term ) {
2678 $ret[$term->taxonomy.':'.$term->slug.':'.$term->term_taxonomy_id] = $term->taxonomy .': '. $term->name;
2679 }
2680 //pagelayer_print($terms);die();
2681 return $ret;
2682 }
2683
2684 function pagelayer_get_post_author(){
2685
2686 $args = [
2687 'capability' => array( 'edit_posts' ),
2688 'fields' => [
2689 'ID',
2690 'display_name',
2691 'user_nicename',
2692 ]
2693 ];
2694
2695 // Capability queries were only introduced in WP 5.9.
2696 if( version_compare( $GLOBALS['wp_version'], '5.9-alpha', '<' ) ){
2697 $args['who'] = 'authors';
2698 unset( $args['capability'] );
2699 }
2700
2701 $authors = new \WP_User_Query( $args );
2702
2703 $ret = array();
2704 foreach ( $authors->get_results() as $author ) {
2705 $ret[$author->user_nicename.':'.$author->ID] = $author->display_name;
2706 }
2707 //pagelayer_print($authors->get_results());die();
2708 return $ret;
2709 }
2710
2711 // Gets the registered post types
2712 function pagelayer_get_public_post_types( $args = [] ) {
2713
2714 global $pagelayer;
2715
2716 $post_type_args = [
2717 'public' => true,
2718 ];
2719
2720 $post_type_args = wp_parse_args( $post_type_args, $args );
2721
2722 $_post_types = get_post_types( $post_type_args, 'objects' );
2723
2724 $post_types = array();
2725
2726 foreach ( $_post_types as $post_type => $object ) {
2727
2728 if($post_type == $pagelayer->builder['name']){
2729 continue;
2730 }
2731
2732 $post_types[ $post_type ] = $object->label;
2733 }
2734 //print_r($post_types);
2735
2736 return $post_types;
2737 }
2738
2739 // Simply echo and dir
2740 function pagelayer_json_output(&$done){
2741
2742 echo json_encode($done);
2743 wp_die();
2744
2745 }
2746
2747 // Get the current query for render the product
2748 function pagelayer_shortcode_current_query($query_args, $atts, $type){
2749 global $wp_query;
2750
2751 if($type == 'pagelayer_current_query'){
2752
2753 if ( ! is_page( wc_get_page_id( 'shop' ) ) ) {
2754 $query_args = $wp_query->query_vars;
2755 }
2756
2757 add_action( "woocommerce_shortcode_before_{$type}_loop", function () {
2758 wc_set_loop_prop( 'is_shortcode', false );
2759 } );
2760
2761 if(!empty($atts['paginate'])){
2762 $page = get_query_var( 'paged', 1 );
2763
2764 if( 1 < $page ) {
2765 $query_args['paged'] = $page;
2766 }
2767 }
2768
2769 // Always query only IDs
2770 $query_args['fields'] = 'ids';
2771
2772 }
2773
2774 return $query_args;
2775 }
2776
2777 // Loads the tags which have parameters of a particular type
2778 function pagelayer_get_prop_type($types){
2779
2780 global $pagelayer;
2781
2782 if(!is_array($types)){
2783 $types = [$types];
2784 }
2785
2786 $ret = [];
2787
2788 // Loop thru all shortcodes
2789 foreach($pagelayer->shortcodes as $tag => $vvv){
2790
2791 // Lets create the CSS, Classes, Attr. Also clean the dependent atts
2792 foreach($pagelayer->tabs as $tab){
2793
2794 if(empty($pagelayer->shortcodes[$tag][$tab])){
2795 continue;
2796 }
2797
2798 foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
2799
2800 $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
2801
2802 //echo $tab.' - '.$section.' - <br>';
2803
2804 if(empty($props)){
2805 continue;
2806 }
2807
2808 // Loop all props
2809 foreach($props as $prop => $param){
2810
2811 // Load any attachment values
2812 if(!in_array($param['type'], $types)){
2813 continue;
2814 }
2815
2816 $ret[$tag][$prop] = $param['type'];
2817
2818 }
2819
2820 }
2821
2822 }
2823
2824 }
2825
2826 //r_print($ret);die();
2827
2828 return $ret;
2829
2830 }
2831
2832 function pagelayer_export_content($content){
2833
2834 global $pagelayer;
2835
2836 // Just call do_shortcode so we can get list of media files to export
2837 //do_shortcode($content);
2838
2839 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
2840
2841 $content = preg_replace('/http(s?):\/\/'.preg_quote($theme_url, '/').'/is', '{{theme_url}}', $content);
2842
2843 // Remove unnecessary spaces
2844 $content = preg_replace('/--\>\s*(?!<!--)/is', '-->', $content);
2845
2846 // Replace links of the theme for images
2847 $content = pagelayer_export_theme_links($content, get_stylesheet_directory_uri());
2848
2849 ////////////////////
2850 // Handle Link IDs
2851 ////////////////////
2852
2853 if(empty($pagelayer->e['link_tags'])){
2854 $pagelayer->e['link_tags'] = pagelayer_get_prop_type('link');
2855 //r_print($pagelayer->e['link_tags']);die();
2856 }
2857
2858 if(empty($pagelayer->e['media_tags'])){
2859 $pagelayer->e['media_tags'] = pagelayer_get_prop_type(['image', 'video', 'audio', 'media', 'multi_image']);
2860 //r_print($pagelayer->e['media_tags']);die();
2861 }
2862
2863 $parsed = parse_blocks($content);
2864 //r_print($parsed);//die();
2865
2866 $parsed = pagelayer_export_link_ids($parsed);
2867
2868 // Export the media as well
2869 $parsed = pagelayer_export_media($parsed);
2870
2871 // Reserialize
2872 $content = serialize_blocks($parsed);
2873
2874 // We store in SP format
2875 $content = str_replace('<!-- wp:pagelayer', '<!-- sp:pagelayer', $content);
2876 $content = str_replace('<!-- /wp:pagelayer', '<!-- /sp:pagelayer', $content);
2877
2878 // Just pass the content to
2879 $content = pagelayer_export_extract_menu_ids($content);
2880
2881 // Apply a filter
2882 $content = apply_filters('pagelayer_export_content', $content);
2883
2884 return $content;
2885
2886 }
2887
2888 // Export the menu IDs
2889 function pagelayer_export_extract_menu_ids($content){
2890
2891 global $pagelayer;
2892
2893 // Any Menu ?
2894 preg_match_all('/\[pl_wp_menu ([^\]]*)nav_list="(\d*)"([^\]]*)\]/is', $content, $matches);
2895 //r_print($matches);
2896
2897 if(!empty($matches[2])){
2898
2899 foreach($matches[2] as $k => $v){
2900 $v = (int)$v;
2901 $pagelayer->export_menus[$v] = $v;
2902 }
2903 //r_print($pagelayer->export_menus);
2904
2905 }
2906
2907 // Also for block format
2908 preg_match_all('/<!--\s+(?P<closer>\/)?sp:pagelayer\/pl_wp_menu\s+(?P<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?}\s+)?(?P<void>\/)?-->/s', $content, $matches);
2909
2910 if(!empty($matches['attrs'])){
2911
2912 foreach($matches['attrs'] as $k => $v){
2913 preg_match('/nav_list"\s*:\s*"(\d*)"/is', $v, $vmatch);
2914
2915 if(!empty($vmatch[1])){
2916 $vmatch[1] = (int)$vmatch[1];
2917 $pagelayer->export_menus[$vmatch[1]] = $vmatch[1];
2918 //r_print($pagelayer->export_menus);
2919 }
2920 }
2921 }
2922
2923 return $content;
2924
2925 }
2926
2927 function pagelayer_export_link_ids($parsed){
2928
2929 global $pagelayer;
2930
2931 foreach($parsed as $k => $v){
2932
2933 if(!empty($v['innerBlocks'])){
2934 $parsed[$k]['innerBlocks'] = pagelayer_export_link_ids($v['innerBlocks']);
2935 }
2936
2937 if(!preg_match('/pagelayer/is', $v['blockName'])){
2938 continue;
2939 }
2940
2941 $tag = str_replace('pagelayer/', '', $v['blockName']);
2942 //echo $tag;//die();
2943
2944 // Is there a tag having a link prop
2945 if(empty($pagelayer->e['link_tags'][$tag])){
2946 continue;
2947 }
2948 //echo $tag;die();
2949
2950 foreach($pagelayer->e['link_tags'][$tag] as $kk => $vv){
2951 if(!empty($v['attrs'][$kk]) && is_numeric($v['attrs'][$kk])){
2952 $linked = get_post($v['attrs'][$kk]);
2953 if(!empty($linked->post_name)){
2954 $parsed[$k]['attrs'][$kk] = '||link_id|'.$linked->post_type.'|'.$linked->post_name.'||';
2955 }
2956 //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
2957 }
2958 }
2959
2960 }
2961
2962 return $parsed;
2963
2964 }
2965
2966 // Replace theme links with theme_url
2967 function pagelayer_export_theme_links($content, $link){
2968
2969 global $sitepad;
2970
2971 // Theme URL correction code
2972 $theme_url = preg_replace('/http(s?):\/\//is', '', $link);
2973
2974 $theme_urls[0] = 'http://'.$theme_url;
2975 $theme_urls[1] = 'https://'.$theme_url;
2976 $theme_urls[2] = $theme_url;
2977 $theme_urls[3] = str_replace('/', '\\/', $theme_urls[0]);
2978 $theme_urls[4] = str_replace('/', '\\/', $theme_urls[1]);
2979 $theme_urls[5] = str_replace('/', '\\/', $theme_url);
2980
2981 foreach($theme_urls as $k => $v){
2982 $content = preg_replace_callback('/'.preg_quote($v, '/').'([^"]*)/is', 'pagelayer_export_theme_links_replacer', $content);
2983 }
2984
2985 $content = str_ireplace($theme_urls, '{{theme_url}}', $content);
2986
2987 return $content;
2988 }
2989
2990 // This function is a part of pagelayer_export_theme_links
2991 function pagelayer_export_theme_links_replacer($matches){
2992 return '{{theme_url}}'.str_replace('\\/', '/', $matches[1]);
2993 }
2994
2995 // Export media
2996 function pagelayer_export_media($parsed){
2997
2998 global $pagelayer;
2999
3000 foreach($parsed as $k => $v){
3001
3002 if(!empty($v['innerBlocks'])){
3003 $parsed[$k]['innerBlocks'] = pagelayer_export_media($v['innerBlocks']);
3004 }
3005
3006 if(!preg_match('/pagelayer/is', $v['blockName'])){
3007 continue;
3008 }
3009
3010 $tag = str_replace('pagelayer/', '', $v['blockName']);
3011 //echo $tag;//die();
3012
3013 // Is there a tag having a link prop
3014 if(empty($pagelayer->e['media_tags'][$tag])){
3015 continue;
3016 }
3017 //echo $tag;die();
3018
3019 foreach($pagelayer->e['media_tags'][$tag] as $kk => $vv){
3020
3021 if(empty($v['attrs'][$kk])){
3022 continue;
3023 }
3024
3025 $is_array = 0;
3026
3027 // Is it an array
3028 if(is_array($v['attrs'][$kk])){
3029
3030 $ids = $v['attrs'][$kk];
3031 $is_array = 1;
3032
3033 // Is it of the format 1,2,3
3034 }elseif(preg_match('/^((\d*)(,?))*$/is', $v['attrs'][$kk])){
3035
3036 $ids = pagelayer_maybe_explode(',', $v['attrs'][$kk]);
3037
3038 // Its a number or string
3039 }else{
3040
3041 // Is it our default image ?
3042 if(preg_match('/pagelayer-pro\/images\/default-image.png$/is', $v['attrs'][$kk])){
3043 $parsed[$k]['attrs'][$kk] = '';
3044 continue;
3045 }
3046
3047 // It should be a number
3048 if(!is_numeric($v['attrs'][$kk])){
3049 continue;
3050 }
3051
3052 $ids = [$v['attrs'][$kk]];
3053 }
3054
3055 $ret = [];
3056
3057 foreach($ids as $id){
3058
3059 $file = pagelayer_export_media_files($id, $exp_img_url);
3060
3061 // Did it export ?
3062 if(empty($file) || !file_exists($file)){
3063 continue;
3064 }
3065
3066 $ret[] = $exp_img_url;
3067
3068 }
3069
3070 $parsed[$k]['attrs'][$kk] = $is_array ? $ret : implode(',', $ret);
3071 //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
3072 }
3073
3074 }
3075
3076 return $parsed;
3077
3078 }
3079
3080 // Exports the media to the images folder of the current theme
3081 function pagelayer_export_media_files($id, &$url = ''){
3082
3083 $theme_dir = get_stylesheet_directory();
3084 $image_dir = $theme_dir.'/images/';
3085 @mkdir($image_dir);
3086
3087 // Get the file path
3088 $file = get_attached_file($id);
3089
3090 if(empty($file) || !file_exists($file)){
3091 return false;
3092 }
3093
3094 $dest_file = $image_dir.basename($file);
3095
3096 // Copy the file
3097 copy($file, $dest_file);
3098
3099 // Replace the text
3100 $url = '{{theme_url}}/images/'.basename($file);
3101
3102 return $dest_file;
3103
3104 //echo $content;
3105 }
3106
3107 // Insert a post which is a Pagelayer Post
3108 function pagelayer_sanitize_content($content){
3109
3110 if(empty($content)){
3111 return $content;
3112 }
3113
3114 // Replace Vars
3115 $template_vars = pagelayer_template_vars();
3116
3117 foreach($template_vars as $key => $val){
3118 $content = str_replace($key, $val, $content);
3119 }
3120
3121 if(defined('PAGELAYER_BLOCK_PREFIX') && PAGELAYER_BLOCK_PREFIX == 'wp'){
3122 $content = str_replace('<!-- sp:pagelayer', '<!-- wp:pagelayer', $content);
3123 $content = str_replace('<!-- /sp:pagelayer', '<!-- /wp:pagelayer', $content);
3124 }
3125
3126 return $content;
3127 }
3128
3129 // Insert a post which is a Pagelayer Post
3130 function pagelayer_insert_content($post, &$ret){
3131
3132 $post = apply_filters('pagelayer_start_insert_content', $post);
3133
3134 $post['post_content'] = pagelayer_sanitize_content($post['post_content']);
3135 //pagelayer_print($post);die();
3136
3137 // Add slashes for safe insert
3138 $post['post_content'] = wp_slash($post['post_content']);
3139
3140 $post = apply_filters('pagelayer_pre_insert_content', $post);
3141
3142 // Now insert / update the post
3143 $ret = wp_insert_post($post);
3144
3145 // Did we save the post ?
3146 if(empty($ret) || is_wp_error($ret)){
3147 return false;
3148 }
3149
3150 // Convert to pagelayer accessed post
3151 if(!add_post_meta($ret, 'pagelayer-data', time(), true)){
3152 update_post_meta($ret, 'pagelayer-data', time());
3153 }
3154
3155 return $ret;
3156
3157 }
3158
3159 // Gets the list of enabled fonts
3160 function pagelayer_enabled_icons(){
3161
3162 $stored_icons = get_option('pagelayer_icons_set');
3163 if(empty($stored_icons)){
3164 update_option('pagelayer_icons_set', ['font-awesome5']);
3165 $stored_icons = get_option('pagelayer_icons_set');
3166 }
3167
3168 return $stored_icons;
3169
3170 }
3171
3172 // Prevent pro activate text for installer
3173 function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
3174
3175 if($plugin_file == PAGELAYER_PREMIUM_BASE){
3176 return array();
3177 }
3178
3179 return $install_actions;
3180 }
3181
3182 // Handle hexa to rgba and also remove alpha which is ff
3183 function pagelayer_hex8_to_rgba($val){
3184
3185 // If opacity is ff then discard ff
3186 if(preg_match('/^#([a-f0-9]{6})ff$/is', $val)){
3187 return substr($val, 0, 7);
3188 }
3189
3190 // Lets handle the RGB+opacity
3191 if(preg_match('/#([a-f0-9]{8})$/is', $val)){
3192 $val = pagelayer_hex2rgba($val);
3193 }
3194
3195 return $val;
3196
3197 }
3198
3199 // Convert to RGBA from HEX
3200 function pagelayer_hex2rgba($color){
3201
3202 //Return if no color provided
3203 if(empty($color)){
3204 return;
3205 }
3206
3207 //Sanitize $color if "#" is provided
3208 if ($color[0] == '#'){
3209 $color = substr( $color, 1 );
3210 }
3211
3212 //Check if color has 6 or 3 characters and get values
3213 if (strlen($color) == 8) {
3214 $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3215 $alpha = $color[6] . $color[7];
3216 //$alpha = '';
3217 } elseif (strlen($color) == 6) {
3218 $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3219 } elseif ( strlen( $color ) == 3 ) {
3220 $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
3221 } else {
3222 return;
3223 }
3224
3225 //Convert hexadec to rgb
3226 $rgb = array_map('hexdec', $hex);
3227
3228 //Check if opacity is set(rgba or rgb)
3229 if($alpha){
3230
3231 $alpha = number_format((float)hexdec($alpha) / 255, 2, '.', '');
3232 //print_r($alpha);
3233 if(abs($alpha) > 1){
3234 $alpha = 1.0;
3235 }
3236 $output = 'rgba('.implode(",",$rgb).','.$alpha.')';
3237 } else {
3238 $output = 'rgb('.implode(",",$rgb).')';
3239 }
3240
3241 //Return rgb(a) color string
3242 return $output;
3243 }
3244
3245 // Get social URLs
3246 function pagelayer_get_social_urls(){
3247
3248 $urls = array();
3249
3250 $urls['facebook'] = get_option('pagelayer-facebook-url');
3251 $urls['twitter'] = get_option('pagelayer-twitter-url');
3252 $urls['instagram'] = get_option('pagelayer-instagram-url');
3253 $urls['linkedin'] = get_option('pagelayer-linkedin-url');
3254 $urls['youtube'] = get_option('pagelayer-youtube-url');
3255 $urls['google'] = get_option('pagelayer-gplus-url');
3256
3257 foreach($urls as $k => $v){
3258
3259 if(empty($v)){
3260 unset($urls[$k]);
3261 continue;
3262 }
3263
3264 $urls[$k] = esc_url($v);
3265 }
3266
3267 return $urls;
3268 }
3269
3270 function pagelayer_get_option($opt){
3271 $ret = get_option($opt);
3272
3273 $opts = array(
3274 'pagelayer-address' => '1, My Address, My Street, New York City, NY, USA',
3275 'pagelayer-phone' => '+1234567890',
3276 'pagelayer-copyright' => '© '.date('Y').' '.get_option('blogname'),
3277 'pagelayer_cf_to_email' => 'contact@domain.com',
3278 'pagelayer_cf_success' => __pl('cf_success'),
3279 'pagelayer_cf_failed' => __pl('cf_failed'),
3280 );
3281
3282 if(empty($ret)){
3283 return $opts[$opt];
3284 }
3285
3286 return wp_kses_post($ret);
3287 }
3288
3289 // Uploads an image / media
3290 function pagelayer_upload_media($filename, $blob){
3291
3292 if(empty($blob)){
3293 return false;
3294 }
3295
3296 $md5 = md5($blob);
3297
3298 // Do we have this image
3299 $args = array(
3300 'post_type' => 'attachment',
3301 'post_status' => 'inherit',
3302 'meta_query' => array(
3303 array(
3304 'key' => 'pagelayer_image_md5',
3305 'value' => $md5,
3306 )
3307 )
3308 );
3309
3310 $query = new WP_Query($args);
3311
3312 // If we found the image, return
3313 foreach($query->posts as $ck => $cv){
3314 return $cv->ID;
3315 }
3316
3317 $upload = wp_upload_bits($filename, null, $blob);
3318
3319 if( !empty( $upload['error'] ) ) {
3320 return false;
3321 }
3322
3323 $file_path = $upload['file'];
3324 $file_name = basename( $file_path );
3325 $file_type = wp_check_filetype( $file_name, null );
3326 $attachment_title = sanitize_file_name( pathinfo( $file_name, PATHINFO_FILENAME ) );
3327 $wp_upload_dir = wp_upload_dir();
3328
3329 $post_info = array(
3330 'guid' => $wp_upload_dir['url'] . '/' . $file_name,
3331 'post_mime_type'=> $file_type['type'],
3332 'post_title' => $attachment_title,
3333 'post_content' => '',
3334 'post_status' => 'inherit',
3335 );
3336
3337 $attach_id = wp_insert_attachment( $post_info, $file_path, $parent_post_id );
3338 update_post_meta($attach_id, 'pagelayer_image_md5', $md5);
3339
3340 $lib = ABSPATH . 'site-admin/includes/image.php';
3341 $media = ABSPATH . 'site-admin/includes/media.php';
3342 $media = file_exists($media) ? $media : ABSPATH . 'wp-admin/includes/media.php';
3343 $lib = file_exists($lib) ? $lib : ABSPATH . 'wp-admin/includes/image.php';
3344
3345 require_once($media);
3346 require_once($lib);
3347
3348 $attach_data = wp_generate_attachment_metadata( $attach_id, $file_path );
3349 wp_update_attachment_metadata( $attach_id, $attach_data );
3350
3351 return $attach_id;
3352
3353 }
3354
3355 // Show the notice of importing the active themes content
3356 function pagelayer_theme_import_notices($return = false){
3357
3358 $theme = wp_get_theme();
3359
3360 $imported = get_option('pagelayer_theme_'.get_template().'_imported');
3361 $show = 0;
3362
3363 // We need to import the content
3364 if(empty($imported) && is_admin() && current_user_can('switch_themes')){
3365 $show = 1;
3366 }
3367
3368 $dismissed = get_option('pagelayer_theme_'.get_template().'_dismissed');
3369
3370 // Is this dismissed
3371 if($dismissed){
3372 $show = 0;
3373 }
3374
3375 //$show = 1;
3376
3377 // Is it the importer page ?
3378 if(!empty($_REQUEST['page']) && $_REQUEST['page'] == 'pagelayer_import'){
3379 $show = 0;
3380 }
3381
3382 // Show the message
3383 if(!empty($show)){
3384 $str = '
3385
3386 <style>
3387 .pagelayer_promo_button {
3388 background-color: #4CAF50; /* Green */
3389 border: none;
3390 color: white;
3391 padding: 6px 10px;
3392 text-align: center;
3393 text-decoration: none;
3394 display: inline-block;
3395 font-size: 13px;
3396 margin: 4px 2px;
3397 -webkit-transition-duration: 0.4s; /* Safari */
3398 transition-duration: 0.4s;
3399 cursor: pointer;
3400 }
3401 .pagelayer_promo_button:focus,
3402 .pagelayer_promo_button:hover{
3403 border: none;
3404 color: white;
3405 box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
3406 color: white;
3407 }
3408 .pagelayer_promo_buy {
3409 color: white;
3410 padding: 8px 12px;
3411 font-size: 14px;
3412 }
3413 .pagelayer_promo_button1 {
3414 color: white;
3415 background-color: #4CAF50;
3416 border:3px solid #4CAF50;
3417 }
3418 .pagelayer_promo_button1:hover {
3419 border:3px solid #4CAF50;
3420 }
3421 .pagelayer_promo_button2 {
3422 color: white;
3423 background-color: #0085ba;
3424 }
3425 .pagelayer_promo_button3 {
3426 color: white;
3427 background-color: #365899;
3428 }
3429 .pagelayer_promo_button4 {
3430 color: white;
3431 background-color: rgb(66, 184, 221);
3432 }
3433 .pagelayer_promo-close{
3434 float:right;
3435 text-decoration:none;
3436 margin: 5px 10px 0px 0px;
3437 }
3438 .pagelayer_promo-close:hover{
3439 color: red;
3440 }
3441 </style>
3442
3443 <script type="application/javascript">
3444 jQuery(document).ready(function(){
3445 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
3446 var data;
3447 jQuery("#pagelayer_promo").hide();
3448 // Save this preference
3449 jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
3450 //alert(response);
3451 });
3452 });
3453 });
3454 </script>
3455
3456 <div class="notice notice-success" id="pagelayer_promo" style="min-height:30px">
3457 <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
3458 <span class="dashicons dashicons-dismiss"></span> Dismiss
3459 </a>';
3460
3461 $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>').'
3462
3463 </div>';
3464
3465 // Print it
3466 if(empty($return)){
3467 echo $str;
3468 }else{
3469 return $str;
3470 }
3471 }
3472
3473 }
3474
3475 // Parse the array variables
3476 function pagelayer_replace_vars( $string, $array, $pre = '', $suf = ''){
3477
3478 $array = (array) $array;
3479
3480 foreach($array as $kk => $vv){
3481
3482 if(is_array($vv)){
3483 $vv = pagelayer_flat_join($vv);
3484 }
3485
3486 $string = str_replace($pre.$kk.$suf, $vv, $string);
3487 }
3488 return $string;
3489 }
3490
3491 // Add Attachment to the contact the email
3492 function pagelayer_cf_email_attachment( &$phpmailer) {
3493
3494 if ( ! empty( $_FILES ) ) {
3495 foreach ( $_FILES as $attachment ) {
3496
3497 if(empty($attachment['name'])){
3498 continue;
3499 }
3500
3501 try {
3502 $phpmailer->addAttachment($attachment['tmp_name'], $attachment['name'], $encoding = 'base64', $attachment['type'], $disposition = 'attachment');
3503 } catch ( \Exception $e ) {
3504 continue;
3505 }
3506 }
3507 }
3508
3509 }
3510
3511 function pagelayer_array_flatten( $input ) {
3512 if ( ! is_array( $input ) ) {
3513 return array( $input );
3514 }
3515
3516 $output = array();
3517
3518 foreach ( $input as $value ) {
3519 $output = array_merge( $output, pagelayer_array_flatten( $value ) );
3520 }
3521
3522 return $output;
3523 }
3524
3525 function pagelayer_flat_join( $array ) {
3526 $array = pagelayer_array_flatten( $array );
3527 $output = array();
3528
3529 foreach ( (array) $array as $value ) {
3530 $output[] = trim( (string) $value );
3531 }
3532
3533 return implode( ', ', $output );
3534 }
3535
3536 // Verifies the Google Captcha
3537 function pagelayer_captcha_verify(){
3538
3539 // If secret key is not there, return
3540 $captcha_secret = get_option('pagelayer_google_captcha_secret');
3541
3542 if(empty($captcha_secret)){
3543 return true;
3544 }
3545
3546 $response = !empty($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : '';
3547
3548 $ip = pagelayer_getip();
3549
3550 // Is the IP or response not there ?
3551 if(empty($response) || empty($ip)){
3552 return false;
3553 }
3554
3555 $url = 'https://www.google.com/recaptcha/api/siteverify';
3556
3557 // Verify the post
3558 $req = wp_remote_post($url, array(
3559 'timeout' => 20,
3560 'body' => array(
3561 'secret' => $captcha_secret,
3562 'response' => $response,
3563 'remoteip' => $ip
3564 )
3565 )
3566 );
3567
3568 // Was there an error posting ?
3569 if(is_wp_error($req)){
3570 return false;
3571 }
3572
3573 // Process the post response
3574 $resp = wp_remote_retrieve_body($req);
3575
3576 // Is the body valid
3577 if(empty($resp)){
3578 return false;
3579 }
3580
3581 $json = json_decode($resp, true);
3582
3583 if(!empty($json['success']) && // for v2 and v3
3584 (!isset($json['score']) || $json['score'] >= 0.5 && $json['action'] === 'submit') // For v3
3585 ){
3586 return true;
3587 }
3588
3589 return false;
3590 }
3591
3592 // Pagelayer serialize attributes
3593 function pagelayer_serialize_attributes( $block_attributes ) {
3594 $encoded_attributes = json_encode( $block_attributes, JSON_FORCE_OBJECT);
3595 $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
3596 $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
3597 $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
3598 // Regex: /\\"/
3599 $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
3600
3601 return $encoded_attributes;
3602 }
3603
3604 function pagelayer_enable_giver(){
3605 global $pagelayer;
3606 return !empty($pagelayer->settings['enable_giver']) && $pagelayer->settings['enable_giver'] == 1;
3607 }
3608
3609 function pagelayer_load_font_options(){
3610 include_once(PAGELAYER_DIR.'/main/font-options.php');
3611 }
3612
3613 function pagelayer_is_utf8($str) {
3614 return (bool) preg_match('//u', $str);
3615 }
3616
3617 // Create blank images
3618 function pagelayer_create_blank_image($width, $height){
3619
3620 $im = @imagecreate($width, $height);
3621
3622 if(empty($im)){
3623 return false;
3624 }
3625
3626 $background_color = imagecolorallocate($im, 220, 220, 220);
3627 $text_color = imagecolorallocate($im, 110, 110, 110);
3628 $string = $width.' x '.$height;
3629 $font_size = 30;
3630 $sx = ($width / 2) - (strlen($string) * $font_size / 2);
3631 $sy = ($height / 2) - ($font_size / 2);
3632 $font = realpath(PAGELAYER_DIR.'/fonts/OpenSans-BoldItalic.ttf');
3633 imagettftext($im, $font_size, 0, $sx, $sy, $text_color, $font, $string);
3634 ob_start();
3635 imagepng($im);
3636 imagedestroy($im);
3637
3638 return ob_get_clean();
3639
3640 }
3641
3642 // Get category name by post type
3643 function pagelayer_post_type_category($post_type){
3644
3645 $taxonomies = get_object_taxonomies( $post_type, 'objects');
3646
3647 foreach( $taxonomies as $key => $tax){
3648 if(!empty($tax->hierarchical)){
3649 return $key;
3650 }
3651 }
3652
3653 return false;
3654
3655 }
3656
3657 // Get tag name by post type
3658 function pagelayer_post_type_tag($post_type){
3659
3660 $taxonomies = get_object_taxonomies( $post_type, 'objects');
3661
3662 foreach( $taxonomies as $key => $tax){
3663 if(!empty($tax->show_tagcloud) && empty($tax->hierarchical)){
3664 return $key;
3665 }
3666 }
3667 return false;
3668
3669 }
3670
3671 // Get checkboxes of post categories.
3672 function pagelayer_post_cats($post, &$cat_name = ''){
3673
3674 $ret = [];
3675
3676 $cat_name = pagelayer_post_type_category($post->post_type);
3677
3678 if(empty($cat_name)){
3679 return $ret;
3680 }
3681
3682 include_once(ABSPATH.PAGELAYER_CMS_DIR_PREFIX.'-admin/includes/template.php');
3683
3684 $args1 = array(
3685 'taxonomy' => $cat_name,
3686 'checked_ontop' => false,
3687 'echo' => false,
3688 );
3689
3690 $ret['with_checkbox'] = '<ul class="pagelayer-post-category" >'.wp_terms_checklist($post->ID, $args1 ).'</ul>';
3691
3692 $args2 = array(
3693 'taxonomy' => $cat_name,
3694 'hierarchical' => true,
3695 'echo' => 0,
3696 'class' => 'pagelayer-add-cat',
3697 'name' => 'pagelayer_cat_parent',
3698 'style' => 'list',
3699 'hide_empty' => 0,
3700 );
3701
3702 $ret['without_checkbox'] = wp_dropdown_categories( $args2 );
3703
3704 return $ret;
3705
3706 }
3707
3708 // Get checkboxes of post tags.
3709 function pagelayer_post_tags($post, &$tag_name = ''){
3710
3711 $ret = [];
3712
3713 $tag_name = pagelayer_post_type_tag($post->post_type);
3714
3715 if(empty($tag_name)){
3716 return $ret;
3717 }
3718
3719 $ret['postTags'] = wp_get_post_terms( $post->ID, $tag_name );
3720
3721 $ret['allTags'] = get_tags(array( 'hide_empty' => false, 'taxonomy' => $tag_name ));
3722
3723 return $ret;
3724
3725 }
3726
3727 // Get users who can edit a particular post type
3728 function pagelayer_post_authors_by_type($post_type){
3729
3730 global $wp_post_types;
3731
3732 if(empty($wp_post_types[$post_type])){
3733 $users = get_users( array( 'role__in' => array( 'administrator' ) ) );
3734 }else{
3735 $req_role = $wp_post_types[$post_type]->cap->edit_posts;
3736 $roles__in = [];
3737 foreach( wp_roles()->roles as $role_slug => $role ){
3738 if( ! empty( $role['capabilities'][$req_role] ) )
3739 $roles__in[] = $role_slug;
3740 }
3741 $users = get_users( array( 'role__in' => $roles__in ) );
3742 }
3743
3744 $user_array = [];
3745
3746 foreach ( $users as $user ) {
3747 $user_array[$user->ID] = $user->display_name;
3748 }
3749
3750 return $user_array;
3751 }
3752
3753 function pagelayer_parent_post_prop($post){
3754 global $wp_post_types;
3755
3756 if(!empty($wp_post_types[$post->post_type]->hierarchical)){
3757 return pagelayer_list_parents(0, 0, $post, []);
3758 }else{
3759 return [];
3760 }
3761 }
3762
3763 function pagelayer_list_parents( $parent = 0, $level = 0, $post = null, $values=array('')){
3764
3765 global $wpdb;
3766
3767 $post = get_post( $post );
3768
3769 $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_title FROM $wpdb->posts
3770 WHERE post_parent = %d
3771 AND post_type = '".$post->post_type."'
3772 AND post_status = 'publish'
3773 ORDER BY menu_order", $parent ) );
3774
3775 if ( !empty($items) ) {
3776 foreach ( $items as $item ) {
3777
3778 // A page cannot be its own parent.
3779 if ( $post && $post->ID && (int) $item->ID === $post->ID ) {
3780 continue;
3781 }
3782
3783 $pad = str_repeat( '-', $level );
3784
3785 if(count($values)==0){
3786 $values[] = '';
3787 }
3788
3789 $values[' '.$item->ID] = $pad.esc_html( $item->post_title );
3790
3791 $values = pagelayer_list_parents($item->ID, $level + 1, $post, $values );
3792 }
3793 }
3794
3795 return $values;
3796 }
3797
3798 // Maybe explode
3799 function pagelayer_maybe_explode( $separator = ',', $string = '', $limit = PHP_INT_MAX ){
3800
3801 if(is_array($string)){
3802 return $string;
3803 }
3804
3805 return explode($separator, $string, $limit);
3806 }
3807
3808 // Maybe implode
3809 function pagelayer_maybe_implode($array, $separator = ','){
3810
3811 if(is_array($array)){
3812 return @implode($separator, $array);
3813 }
3814
3815 if(is_null($array)){
3816 return '';
3817 }
3818
3819 return $array;
3820
3821 }
3822
3823 // Sanitize text field or array
3824 function pagelayer_sanitize_text_field($str) {
3825
3826 if ( !is_object( $str ) && !is_array( $str ) ) {
3827 return sanitize_text_field($str);
3828 }
3829
3830 if( is_array($str) ){
3831 return array_map( 'pagelayer_sanitize_text_field', $str);
3832 }
3833
3834 return $str;
3835 }
3836
3837 // Sanitize posts data for WP_Query
3838 function pagelayer_sanitize_posts_data($data, $only_allowed = true) {
3839
3840 $allowed_keys = [
3841 'post_type', 'posts_per_page', 'order', 'orderby', 'paged',
3842 'filter_by', 'term', 'exc_term', 'cat', 'category_name',
3843 'tag', 'author', 'author_name', 'post__in', 'post__not_in',
3844 'include', 'exclude', 'search', 's', 'exact', 'sentence',
3845 'post_status', 'post_parent', 'offset',
3846 'posts_per_archive_page', 'page', 'ignore_sticky_posts'
3847 ];
3848
3849 $sanitized = [];
3850
3851 foreach($data as $key => $value){
3852 if($only_allowed && !in_array($key, $allowed_keys)) {
3853 continue;
3854 }
3855
3856 $sanitized[$key] = pagelayer_sanitize_text_field($value);
3857 }
3858
3859 // Security: Restrict post_status to prevent information disclosure
3860 // Only users who can read private posts or edit others' posts should be able to query non-public statuses
3861 if(isset($sanitized['post_status'])){
3862 $requested_status = $sanitized['post_status'];
3863
3864 // If requesting something other than publish, verify permissions
3865 if ($requested_status !== 'publish') {
3866 // Check if the user has permission to read private posts or edit others' posts
3867 // This prevents contributors from seeing titles of private posts they don't own.
3868 if (!current_user_can('read_private_posts') && !current_user_can('edit_others_posts')) {
3869 $sanitized['post_status'] = 'publish';
3870 }
3871 }
3872 }else{
3873 // Default to publish for safety if not specified
3874 $sanitized['post_status'] = 'publish';
3875 }
3876
3877 if(isset($sanitized['posts_per_page'])){
3878 $sanitized['posts_per_page'] = (int) $sanitized['posts_per_page'];
3879 if ($sanitized['posts_per_page'] > 100) {
3880 $sanitized['posts_per_page'] = 100;
3881 }
3882 }
3883
3884 if(isset($sanitized['paged'])){
3885 $sanitized['paged'] = (int) $sanitized['paged'];
3886 }
3887
3888 if(isset($sanitized['offset'])){
3889 $sanitized['offset'] = (int) $sanitized['offset'];
3890 }
3891
3892 if(isset($sanitized['post__in']) && is_string($sanitized['post__in'])){
3893 $sanitized['post__in'] = array_map('intval', explode(',', $sanitized['post__in']));
3894 }
3895
3896 if(isset($sanitized['post__not_in']) && is_string($sanitized['post__not_in'])){
3897 $sanitized['post__not_in'] = array_map('intval', explode(',', $sanitized['post__not_in']));
3898 }
3899
3900 if(isset($sanitized['post_parent'])){
3901 $sanitized['post_parent'] = (int) $sanitized['post_parent'];
3902 }
3903
3904 if(isset($sanitized['cat'])){
3905 $sanitized['cat'] = (int) $sanitized['cat'];
3906 }
3907
3908 if(isset($sanitized['author'])){
3909 $sanitized['author'] = (int) $sanitized['author'];
3910 }
3911
3912 return $sanitized;
3913 }
3914
3915 // Update nav menu item
3916 function pagelayer_save_nav_menu_items($items){
3917
3918 // Menu setting keys
3919 $allowed_post = array('title' => 'post_title');
3920 $allowed_meta = array('_pagelayer_content');
3921
3922 foreach($items as $id => $values){
3923
3924 $meta_input = array();
3925 $id = (int) $id;
3926 $post = get_post($id);
3927
3928 // Post found ?
3929 if(empty($post)){
3930 continue;
3931 }
3932
3933 foreach($values as $kk => $vv){
3934
3935 if(array_key_exists($kk, $allowed_post)){
3936 $name = $allowed_post[$kk];
3937 $post->$name = $vv;
3938 }
3939
3940 if(!in_array($kk, $allowed_meta)){
3941 continue;
3942 }
3943
3944 $meta_input[$kk] = stripslashes_deep($vv);
3945 }
3946
3947 if(isset($meta_input['_pagelayer_content'])){
3948 $meta_input['_pagelayer_content'] = base64_decode($meta_input['_pagelayer_content']);
3949 }
3950
3951 $post->meta_input = $meta_input;
3952 wp_update_post($post);
3953 }
3954 }
3955
3956 // Update customizer options
3957 function pagelayer_save_customizer_options($customizer_options){
3958 global $pagelayer;
3959
3960 // To load customizer params array
3961 if(empty($pagelayer->customizer_params)){
3962 pagelayer_load_shortcodes();
3963 }
3964
3965 $customizer_options = apply_filters( 'pagelayer_save_customizer_options', $customizer_options );
3966 $customizer_data = array();
3967 $customizer_option_data = array();
3968
3969 foreach($pagelayer->customizer_params as $ck => $cv){
3970 foreach($pagelayer->screens as $sk => $sv){
3971
3972 $screen = (!empty($sv) ? '_'.$sv : '');
3973 $screen_key = $ck.$screen;
3974
3975 // Save to pagelayer customizer mods
3976 if( empty($cv['option']) ){
3977 if( isset($customizer_options[$screen_key])){
3978 $customizer_data[$screen_key] = $customizer_options[$screen_key];
3979 }
3980 continue;
3981 }
3982
3983 $option_key = $cv['option'].$screen;
3984
3985 // Update option
3986 if( isset($customizer_options[$screen_key] ) ) {
3987 update_option( $option_key, $customizer_options[$screen_key]);
3988 continue;
3989 }
3990
3991 delete_option($option_key);
3992 }
3993 }
3994
3995 // Set Typography settings for global and current post type
3996 $posttype=array(
3997 '' => '', // Make this empty for global values
3998 'current' => $customizer_options['pagelayer_current_post_type']
3999 );
4000
4001 $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4002
4003 foreach($posttype as $type => $tv){
4004
4005 foreach($pagelayer->css_settings as $set => $params){
4006
4007 foreach($pagelayer->screens as $sk => $sv){
4008
4009 $screen = (!empty($sv) ? '_'.$sv : '');
4010 $post_type = (!empty($tv) ? '_'.$tv : '');
4011 $post_key = (!empty($type) ? $type.'_' : '');
4012 $post_key_set = $post_key.$set;
4013
4014 // Create key as per customizer shortcode
4015 $key = $post_key_set.$screen;
4016
4017 // Create option keys for the global and current post type
4018 $option_setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$post_type : $params['key'].$post_type;
4019
4020 $props = array(
4021 'color' => $post_key_set.'_color'.$screen,
4022 'padding' => $post_key_set.'_padding'.$screen,
4023 'margin' => $post_key_set.'_margin'.$screen,
4024 'background-color' => $post_key_set.'_background_color'.$screen,
4025 );
4026
4027 $globalsetting = array();
4028
4029 // Set Typography
4030 if(isset($customizer_options[$key])){
4031 foreach($customizer_options[$key] as $k => $v){
4032 if($v == 'Default' || empty($v)){
4033 unset($customizer_options[$key][$k]);
4034 }
4035
4036 // For sidebar, width default should not be saved
4037 if($set == 'sidebar' && $k == 'width' && $v == 20){
4038 unset($customizer_options[$key][$k]);
4039 }
4040
4041 $globalsetting[$key][$typo[$k]] = $customizer_options[$key][$k];
4042 unset($customizer_options[$key][$k]);
4043 }
4044 }
4045
4046 // Set other props with typography
4047 foreach($props as $pk => $pv){
4048
4049 if(!isset($customizer_options[$pv])){
4050 continue;
4051 }
4052
4053 $globalsetting[$key][$pk] = $customizer_options[$pv];
4054 unset($customizer_options[$pv]);
4055
4056 }
4057
4058 // Are we to save ?
4059 if(!empty($globalsetting[$key])){
4060 update_option($option_setting.$screen, $globalsetting[$key]);
4061 }else{
4062 delete_option($option_setting.$screen);
4063 }
4064
4065 }
4066
4067 }
4068 }
4069
4070 $customizer_data = apply_filters( 'pagelayer_customizer_options', $customizer_data, $customizer_options);
4071
4072 update_option('pagelayer_customizer_mods', $customizer_data);
4073 }
4074
4075 // Get customizer options values
4076 // return array to set customizer block attributes
4077 function pagelayer_get_customizer_options(){
4078 global $pagelayer, $post;
4079
4080 $options = array();
4081
4082 // To load customizer params array
4083 if(empty($pagelayer->customizer_params)){
4084 pagelayer_load_shortcodes();
4085 }
4086
4087 // Get values form options
4088 foreach($pagelayer->customizer_params as $ck => $cv){
4089
4090 if( empty($cv['option']) ){
4091 continue;
4092 }
4093
4094 $screens = array('desktop' => '');
4095
4096 if( !empty($cv['screen']) ){
4097 $screens = $pagelayer->screens;
4098 }
4099
4100 foreach($screens as $sk => $sv){
4101
4102 $screen = (!empty($sv) ? '_'.$sv : '');
4103 $screen_key = $cv['option'].$screen;
4104 $screen_ck = $ck.$screen;
4105
4106 // Get option value
4107 $c_option = get_option($screen_key);
4108
4109 if(empty($c_option)){
4110 continue;
4111 }
4112
4113 $options[$screen_ck] = $c_option;
4114 }
4115 }
4116
4117
4118 // Get values from pagelayer mods
4119 $mods = get_option('pagelayer_customizer_mods');
4120
4121 if(!empty($mods)){
4122 $options = array_merge($options, $mods);
4123 }
4124
4125 $posttype=array(
4126 '' => '',
4127 'current' => $post->post_type
4128 );
4129
4130 $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4131
4132 foreach($posttype as $type => $tv){
4133
4134 foreach($pagelayer->css_settings as $set => $params){
4135
4136 foreach($pagelayer->screens as $sk => $sv){
4137
4138 $screen = (!empty($sv) ? '_'.$sv : '');
4139 $postprefix = (!empty($tv) ? '_'.$tv : '');
4140 $postk = (!empty($type) ? $type.'_' : '');
4141 $post_key_set = $postk.$set;
4142
4143 // Create key as per customizer shortcode
4144 $key = $post_key_set.$screen;
4145
4146 // Create option keys for the global and current post type
4147 $setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$postprefix : $params['key'].$postprefix;
4148
4149 $props = array(
4150 'color' => $post_key_set.'_color'.$screen,
4151 'padding' => $post_key_set.'_padding'.$screen,
4152 'margin' => $post_key_set.'_margin'.$screen,
4153 'background-color' => $post_key_set.'_background_color'.$screen,
4154 );
4155
4156 $optarray = get_option($setting.$screen);
4157
4158 if(!empty($optarray)){
4159
4160 // Get Typography
4161 foreach($typo as $tk => $tv){
4162
4163 $tval = '';
4164
4165 if(isset($optarray[$tv])){
4166 $tval = $optarray[$tv];
4167 }
4168
4169 $options[$key][$tk] = $tval;
4170 }
4171
4172 foreach($props as $pk => $pv){
4173 if(empty( $optarray[$pk])){
4174 continue;
4175 }
4176
4177 $options[$pv] = $optarray[$pk];
4178 }
4179 }
4180 }
4181 }
4182 }
4183
4184 $options = apply_filters( 'pagelayer_get_customizer_options', $options);
4185
4186 return $options;
4187 }
4188
4189 // Sanitize style props for colors and fons
4190 function pagelayer_load_global_palette(){
4191 global $pagelayer;
4192
4193 // Default global colors
4194 $pagelayer->global_colors = array(
4195 'primary' => array(
4196 'title' => 'Primary',
4197 'value' => '#007bff',
4198 ),
4199 'secondary' => array(
4200 'title' => 'Secondary',
4201 'value' => '#6c757d',
4202 ),
4203 'text' => array(
4204 'title' => 'Text',
4205 'value' => '#1d1d1d',
4206 ),
4207 'accent' => array(
4208 'title' => 'Accent',
4209 'value' => '#61ce70',
4210 ),
4211 );
4212
4213 // Global color
4214 $global_color = get_option('pagelayer_global_colors');
4215
4216 if(!empty($global_color)){
4217 $pagelayer->global_colors = json_decode($global_color, true);
4218 }
4219
4220 // Default global fonts
4221 $pagelayer->global_fonts = array(
4222 'primary' => array(
4223 'title' => 'Primary',
4224 'value' => $pagelayer->default_font_styles(),
4225 ),
4226 'secondary' => array(
4227 'title' => 'Secondary',
4228 'value' => $pagelayer->default_font_styles(array('font-family' => 'Roboto')),
4229 ),
4230 'text' => array(
4231 'title' => 'Text',
4232 'value' => $pagelayer->default_font_styles(array('font-family' => 'Montserrat')),
4233 ),
4234 'accent' => array(
4235 'title' => 'Accent',
4236 'value' => $pagelayer->default_font_styles(array('font-family' => 'Poppins')),
4237 ),
4238 );
4239
4240 // Global font
4241 $global_font = get_option('pagelayer_global_fonts');
4242
4243 if(!empty($global_font)){
4244 $pagelayer->global_fonts = json_decode($global_font, true);
4245 }
4246 }
4247
4248 // Get global colors
4249 function pagelayer_sanitize_global_color($val){
4250 global $pagelayer;
4251
4252 if(!is_string($val) || $val[0] != '$'){
4253 return $val;
4254 }
4255
4256 $gkey = substr($val, 1);
4257 $gcolor = isset($pagelayer->global_colors[$gkey]) ? $gkey : 'primary';
4258 $val = 'var(--pagelayer-color-'.$gcolor.')';
4259
4260 return $val;
4261 }
4262
4263 // Sanitize style props for colors and fons
4264 function pagelayer_sanitize_global_style($val, $val_desk = '', $mode = 'desktop'){
4265 global $pagelayer;
4266
4267 // Sanitize Global background color
4268 if( !empty($val['background-color']) && $val['background-color'][0] == '$'){
4269 $val['background-color'] = pagelayer_sanitize_global_color($val['background-color']);
4270 }
4271
4272 // Sanitize Global color
4273 if( !empty($val['color']) && $val['color'][0] == '$'){
4274 $val['color'] = pagelayer_sanitize_global_color($val['color']);
4275 }
4276
4277 // Not set global for typo
4278 if(empty($val['global-font']) && empty($val_desk['global-font'])){
4279 return $val;
4280 }
4281
4282 $_desk_global = false;
4283
4284 if(empty($val['global-font'])){
4285 $font_key = @$val_desk['global-font'];
4286 $_desk_global = true;
4287 }else{
4288 $font_key = $val['global-font'];
4289 unset($val['global-font']);
4290 }
4291
4292 if(empty($font_key)){
4293 return $val;
4294 }
4295
4296 if(!isset($pagelayer->global_fonts[$font_key])){
4297 $font_key = 'primary';
4298 }
4299
4300 $_val = [];
4301
4302 // Apply global typo
4303 foreach($pagelayer->typo_props as $typo_key){
4304
4305 if(!empty($val[$typo_key])){
4306 continue;
4307 }
4308
4309 $global_val = $pagelayer->global_fonts[$font_key]['value'];
4310
4311 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') ){
4312 continue;
4313 }
4314
4315 $_val[$typo_key] = 'var(--pagelayer-font-'.$font_key.'-'.$typo_key.')';
4316 }
4317
4318 // This array contains other values like margin padding so we merge the array
4319 $val = empty($val) ? array() : $val;
4320 $val = array_merge($val, $_val);
4321 return $val;
4322 }
4323
4324 //Check an empty array
4325 function pagelayer_is_empty_array($arr){
4326
4327 foreach ($arr as $key => $value) {
4328 if (trim($value) !== '') return false;
4329 }
4330
4331 return true;
4332 }
4333
4334 // Pagelayer load font family
4335 function pagelayer_load_font_family($font, $font_weight='', $font_style=''){
4336 global $pagelayer;
4337
4338 // Load global fonts
4339 if(strripos($font, 'var(') !== false){
4340 $matches = [];
4341 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-family\))/i', $font, $matches);
4342 $font_key = @$matches[2];
4343 $font = @$pagelayer->global_fonts[$font_key]['value']['font-family'];
4344 }
4345
4346 // Load global fonts weight
4347 if(!empty($font_weight) && strripos($font_weight, 'var(') !== false){
4348 $matches = [];
4349 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-weight\))/i', $font_weight, $matches);
4350 $font_key = @$matches[2];
4351
4352 // Convert in string
4353 $font_weight = @$pagelayer->global_fonts[$font_key]['value']['font-weight'];
4354 }
4355
4356 // Load global fonts style
4357 if(!empty($font_style) && strripos($font_style, 'var(') !== false){
4358 $matches = [];
4359 preg_match('/(var\(--pagelayer-font-)(\w+)(-font-style\))/i', $font_style, $matches);
4360 $font_key = @$matches[2];
4361
4362 // Convert in string
4363 $font_style = @$pagelayer->global_fonts[$font_key]['value']['font-style'];
4364 }
4365
4366 if(empty($font)){
4367 return;
4368 }
4369
4370 $font_weights = array('400' =>'400');
4371 $font_style = empty($font_style) ? 'normal' : $font_style;
4372 $font_style = in_array($font_style, ['italic', 'oblique']) ? 'i' : '';
4373
4374 if(!empty($font_weight)){
4375
4376 if(!is_array($font_weight)){
4377 $font_weight = array($font_weight);
4378 }
4379
4380 foreach($font_weight as $weight){
4381 $font_weights[$weight.$font_style] = $weight.$font_style;
4382 }
4383 }
4384
4385 foreach($font_weights as $fkey => $fvalue){
4386 $pagelayer->runtime_fonts[$font][$fkey] = $fvalue;
4387 }
4388
4389 }
4390
4391 function pagelayer_update_plugin_notice(){
4392 if(defined('SOFTACULOUS_PLUGIN_UPDATE_NOTICE')){
4393 return;
4394 }
4395
4396 $to_update_plugins = apply_filters('softaculous_plugin_update_notice', []);
4397
4398 if(empty($to_update_plugins)){
4399 return;
4400 }
4401
4402 /* 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 */
4403 $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>';
4404
4405 define('SOFTACULOUS_PLUGIN_UPDATE_NOTICE', true); // To make sure other plugins don't return a Notice
4406 echo '<div class="notice notice-info is-dismissible" id="pagelayer-plugin-update-notice">
4407 <p>'.$msg. '</p>
4408 </div>';
4409
4410 wp_register_script('pagelayer-update-notice', '', ['jquery'], '', true);
4411 wp_enqueue_script('pagelayer-update-notice');
4412 wp_add_inline_script('pagelayer-update-notice', 'jQuery("#pagelayer-plugin-update-notice").on("click", function(e){
4413 let target = jQuery(e.target);
4414
4415 if(!target.hasClass("notice-dismiss")){
4416 return;
4417 }
4418
4419 var data;
4420
4421 // Hide it
4422 jQuery("#pagelayer-plugin-update-notice").hide();
4423
4424 // Save this preference
4425 jQuery.post("'.admin_url('admin-ajax.php?action=pagelayer_close_update_notice').'&pagelayer_nonce='.wp_create_nonce('pagelayer_promo_nonce').'", data, function(response) {
4426 //alert(response);
4427 });
4428 });');
4429 }
4430
4431 function pagelayer_update_plugin_notice_filter($plugins = []){
4432 $plugins['pagelayer/pagelayer.php'] = 'Pagelayer';
4433 return $plugins;
4434 }
4435
4436 function pagelayer_is_comment_mode(){
4437
4438 if(file_exists(PAGELAYER_DIR.'/comment_mode.php') && !empty($_REQUEST['cmode'])){
4439 include_once(PAGELAYER_DIR.'/comment_mode.php');
4440 return true;
4441 }
4442
4443 return false;
4444 }
4445
4446 function pagelayer_esc_crlf($value){
4447
4448 // Remove CRLF to prevent header injection
4449 $value = str_replace(array("\r", "\n", "%0a", "%0d"), '', $value);
4450
4451 // Trim spaces
4452 $value = trim($value);
4453
4454 return $value;
4455 }