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
← All changes | main/functions.php +3189 -199 1.0.02.2.1 View file →
@@ -41,20 +41,21 @@
41 41 global $pagelayer;
42 42
43 43 // Just so that we have something
44 44 $ip = _pagelayer_getip();
45 + $method = @$pagelayer->ip_method;
46 +
47 + $pagelayer->ip_method = (int) $method;
45 48
46 - $pagelayer['ip_method'] = (int) @$pagelayer['ip_method'];
47 -
48 49 if(isset($_SERVER["REMOTE_ADDR"])){
49 50 $ip = $_SERVER["REMOTE_ADDR"];
50 51 }
51 52
52 - if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && @$pagelayer['ip_method'] == 1){
53 + if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && $method == 1){
53 54 $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
54 55 }
55 56
56 - if(isset($_SERVER["HTTP_CLIENT_IP"]) && @$pagelayer['ip_method'] == 2){
57 + if(isset($_SERVER["HTTP_CLIENT_IP"]) && $method == 2){
57 58 $ip = $_SERVER["HTTP_CLIENT_IP"];
58 59 }
59 60
60 61 // Hacking fix for X-Forwarded-For
@@ -139,9 +140,9 @@
139 140
140 141 // For filling in posted values
141 142 function pagelayer_POSTval($name, $default = ''){
142 143
143 - return (!empty($_POST) ? (!isset($_POST[$name]) ? '' : $_POST[$name]) : $default);
144 + return (!empty($_POST) ? (!isset($_POST[$name]) ? '' : esc_html($_POST[$name])) : $default);
144 145
145 146 }
146 147
147 148 function pagelayer_POSTchecked($name, $default = false){
@@ -149,8 +150,15 @@
149 150 return (!empty($_POST) ? (isset($_POST[$name]) ? 'checked="checked"' : '') : (!empty($default) ? 'checked="checked"' : ''));
150 151
151 152 }
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 +
153 161 function pagelayer_POSTselect($name, $value, $default = false){
154 162
155 163 if(empty($_POST)){
156 164 if(!empty($default)){
@@ -167,19 +175,10 @@
167 175 }
168 176
169 177 function pagelayer_inputsec($string){
170 178
171 - if(!get_magic_quotes_gpc()){
179 + $string = addslashes($string);
172 180
173 - $string = addslashes($string);
174 -
175 - }else{
176 -
177 - $string = stripslashes($string);
178 - $string = addslashes($string);
179 -
180 - }
181 -
182 181 // This is to replace ` which can cause the command to be executed in exec()
183 182 $string = str_replace('`', '\`', $string);
184 183
185 184 return $string;
@@ -221,9 +220,9 @@
221 220 }
222 221 return false;
223 222 }
224 223
225 -// Reoort an error
224 +// Report an error
226 225 function pagelayer_report_error($error = array()){
227 226
228 227 if(empty($error)){
229 228 return true;
@@ -387,8 +386,12 @@
387 386
388 387 if(in_array($type, $pagelayer->settings['post_types'])){
389 388 return true;
390 389 }
390 +
391 + if($type == $pagelayer->builder['name']){
392 + return true;
393 + }
391 394
392 395 return false;
393 396
394 397 }
@@ -393,9 +396,26 @@
393 396
394 397 }
395 398
396 399 function pagelayer_shortlink($id){
397 - $link = wp_get_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 +
398 418 $link .= substr_count($link, '?') > 0 ? '' : '?';
399 419 return $link;
400 420 }
401 421
@@ -404,19 +424,34 @@
404 424 return pagelayer_shortlink($id).'&pagelayer-live=1';
405 425 }
406 426
407 427 // Are we in live mode ?
408 -function pagelayer_is_live(){
428 +function pagelayer_is_live(&$error = array()){
409 429
410 430 global $post;
411 431
412 432 // Are we seeing the post ?
413 433 if(!isset($post) || !isset($post->ID) || empty($post->ID)){
434 + $error[] = 'Post ID is missing or blank - '.@$post->ID;
414 435 return false;
415 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 + }
416 450
417 451 // Is it the live mode ?
418 452 if(pagelayer_optreq('pagelayer-live')){
453 + $error[] = 'pagelayer-live is missing';
419 454 return true;
420 455 }
421 456
422 457 return false;
@@ -423,12 +458,12 @@
423 458
424 459 }
425 460
426 461 // Are we in live IFRAME mode ?
427 -function pagelayer_is_live_iframe(){
462 +function pagelayer_is_live_iframe(&$error = array()){
428 463
429 464 // Are we seeing the post ?
430 - if(!pagelayer_is_live()){
465 + if(!pagelayer_is_live($error)){
431 466 return false;
432 467 }
433 468
434 469 // Is it the live mode ?
@@ -434,29 +469,95 @@
434 469 // Is it the live mode ?
435 470 if(pagelayer_optreq('pagelayer-iframe')){
436 471 return true;
437 472 }
473 +
474 + $error[] = 'pagelayer-iframe missing in GET';
438 475
439 476 return false;
440 477
441 478 }
442 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 +
443 515 // Can the current user edit the post ?
444 516 function pagelayer_user_can_edit($post = NULL){
445 517
446 - global $wp_the_query, $current_user;
518 + global $wp_the_query, $current_user, $pagelayer;
519 +
520 + $post = get_post($post);
447 521
448 - if(!isset($post) || empty($post) || $post === NULL){
449 - global $post;
522 + if(empty($post)){
523 + return false;
450 524 }
451 525
452 - wp_get_current_user();
453 -
454 - if(isset($post) && is_object($post) && isset($post->ID) && isset($post->post_author) && isset($current_user) && is_object($current_user) && isset($current_user->ID) && (current_user_can('edit_others_posts', $post->ID) || ($post->post_author == $current_user->ID))){
455 - return true;
526 + // No trash editing
527 + if(get_post_status($post->ID) === 'trash'){
528 + return false;
456 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 + }
457 558
458 - return false;
559 + return true;
459 560
460 561 }
461 562
462 563 // Language sting function
@@ -476,17 +577,81 @@
476 577 function pagelayer_icon_sources(){
477 578 return array();
478 579 }
479 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 +
480 639 // Loads the shortcodes
481 640 function pagelayer_load_shortcodes(){
482 -
483 - include_once(PAGELAYER_DIR.'/main/shortcode_functions.php');
484 - if(defined('PAGELAYER_PREMIUM')){
485 - include_once(PAGELAYER_DIR.'/main/premium_functions.php');
641 + global $pagelayer, $post;
642 +
643 + if(!empty($pagelayer->shortcode_loaded)){
644 + return;
486 645 }
487 - include_once(PAGELAYER_DIR.'/main/shortcodes.php');
488 -
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 +
489 654 // pQuery
490 655 include_once(PAGELAYER_DIR.'/lib/pquery/IQuery.php');
491 656 include_once(PAGELAYER_DIR.'/lib/pquery/gan_formatter.php');
492 657 include_once(PAGELAYER_DIR.'/lib/pquery/gan_node_html.php');
@@ -494,35 +659,80 @@
494 659 include_once(PAGELAYER_DIR.'/lib/pquery/gan_parser_html.php');
495 660 include_once(PAGELAYER_DIR.'/lib/pquery/gan_selector_html.php');
496 661 include_once(PAGELAYER_DIR.'/lib/pquery/gan_xml2array.php');
497 662 include_once(PAGELAYER_DIR.'/lib/pquery/pQuery.php');
663 +
664 + include_once(PAGELAYER_DIR.'/main/shortcode_functions.php');
498 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 +
499 671 // Apply filter to load custom widgets
500 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;
501 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');
502 716 }
503 717
504 718 // Add the shortcodes to the pagelayer list
505 719 function pagelayer_add_shortcode($tag, $params = array()){
506 720
507 - global $pagelayer;
508 -
509 - // Is there a handler function ?
510 - if(!empty($params['func'])){
721 + global $pagelayer, $post;
511 722
512 - if($tag == 'pl_row'){
513 - $inner_tag = 'pl_inner_row';
514 - add_shortcode($inner_tag, 'pagelayer_render_shortcode');
515 - }
516 -
517 - if($tag == 'pl_col'){
518 - $inner_tag = 'pl_inner_col';
519 - add_shortcode($inner_tag, 'pagelayer_render_shortcode');
520 - }
521 -
522 - add_shortcode($tag, 'pagelayer_render_shortcode');//$params['func']);
523 - //unset($params['func']);
723 + if($tag == 'pl_row'){
724 + $inner_tag = 'pl_inner_row';
725 + add_shortcode($inner_tag, 'pagelayer_render_shortcode');
524 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']);
525 735
526 736 // Is there a group ?
527 737 if(empty($params['group'])){
528 738 $params['group'] = 'misc';
@@ -532,12 +742,22 @@
532 742 $params['options'] = [
533 743 'ele_bg_styles' => __pl('ele_bg_styles'),
534 744 'ele_styles' => __pl('ele_styles'),
535 745 'border_styles' => __pl('border_styles'),
746 + 'font_style' => __pl('font_style'),
747 + 'position_styles' => __pl('position_styles'),
536 748 'animation_styles' => __pl('animation_styles'),
749 + 'motion_effects' => __pl('Motion Effects'),
537 750 'responsive_styles' => __pl('responsive_styles'),
751 + 'attributes' => __pl('attributes'),
538 752 'custom_styles' => __pl('custom_styles'),
539 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 + }
540 760
541 761 // Are the settings there which hold the params ?
542 762 if(empty($params['settings'])){
543 763 $params['settings'] = [
@@ -568,26 +788,133 @@
568 788 }
569 789 }
570 790 }
571 791 //die();*/
572 -
792 +
793 + $params = apply_filters( 'pagelayer_shortcode_params', $params, $tag );
794 +
573 795 // Insert the shortcode
574 796 $pagelayer->shortcodes[$tag] = $params;
575 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 +}
576 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);
577 867 }
578 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 +
579 886 // Returns the Image values
580 -function pagelayer_image($id){
887 +function pagelayer_image($id = 0){
581 888
582 889 global $pagelayer;
583 890
584 891 $ret = [];
585 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 +
586 913 // External image ?
587 914 if(pagelayer_is_external_img($id)){
588 915
589 - $ret['full-url'] = $id;
916 + $ret['url'] = $id;
590 917
591 918 // Attachment
592 919 }elseif(!empty($id)){
593 920
@@ -615,28 +942,43 @@
615 942 $title = esc_attr($image->post_title);
616 943 $alt = get_post_meta($id, '_wp_attachment_image_alt', true);
617 944 $alt = empty($alt) ? $image->post_excerpt : $alt;
618 945 $alt = empty($alt) ? $image->post_title : $alt;
619 - $alt = empty($alt) ? '' : trim(strip_tags($alt));
946 + $alt = empty($alt) ? '' : esc_attr(trim(strip_tags($alt)));
620 947 $link = get_attachment_link($id);
621 948 $caption = wp_get_attachment_caption($id);
622 - $caption = !empty($caption) ? $caption : '';
949 + $caption = !empty($caption) ? esc_attr($caption) : '';
623 950
624 - $ret['alt'] = $alt;
625 - $ret['title'] = $title;
626 - $ret['link'] = $link;
627 - $ret['caption'] = $caption;
951 + }
628 952
629 - }
953 + }
630 954
955 + // First preference to full url
956 + if(!empty($ret['full-url'])){
957 + $ret['url'] = $ret['full-url'];
631 958 }
632 959
633 960 // No image
634 - if(empty($ret['full-url'])){
635 - $ret['full-url'] = PAGELAYER_URL.'/images/default-image.png';
961 + if(empty($ret['url'])){
962 + $ret['url'] = PAGELAYER_URL.'/images/default-image.png';
636 963 }
637 964
638 - $ret['url'] = $ret['full-url'];
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 + }
639 981
640 982 return $ret;
641 983
642 984 }
@@ -655,8 +997,19 @@
655 997 return false;
656 998
657 999 }
658 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 +
659 1012 // Returns the attachment url
660 1013 function pagelayer_attachment($id){
661 1014
662 1015 $ret = [];
@@ -676,8 +1029,10 @@
676 1029
677 1030 $ret['url'] = wp_get_attachment_url($id);
678 1031
679 1032 }
1033 +
1034 + $ret = apply_filters('pagelayer_attachment', $ret);
680 1035
681 1036 return $ret;
682 1037
683 1038 }
@@ -683,15 +1038,20 @@
683 1038 }
684 1039
685 1040 // Convert the regular URL of a Video to a Embed URL
686 1041 // Todo : Check
687 -function pagelayer_video_url($source){
1042 +function pagelayer_video_url($source, $no_url = false){
688 1043
1044 + global $pagelayer;
1045 +
689 1046 if (!empty($source)) {
690 - $source = filter_var($source, FILTER_SANITIZE_URL);
1047 +
1048 + $source = esc_url( $source );
691 1049 $source = str_replace('&amp;', '&', $source);
692 1050 $url = parse_url($source);
693 1051 $videoSite ='';
1052 + $videoId ='';
1053 + $vid_atts = [];
694 1054
695 1055 $youtubeRegExp = '/youtube\.com|youtu\.be/is';
696 1056 $vimeoRegExp = '/vimeo\.com/is';
697 1057
@@ -703,8 +1063,10 @@
703 1063
704 1064 switch ($videoSite) {
705 1065 case 'youtube':
706 1066
1067 + $pagelayer->append_yt_api = true;
1068 +
707 1069 if (preg_match('/youtube\.com/is', $source)) {
708 1070
709 1071 if (preg_match('/watch/is', $source)) {
710 1072 parse_str($url['query'], $parameters);
@@ -727,38 +1089,44 @@
727 1089 $videoId = $path[1];
728 1090 }
729 1091
730 1092 }
1093 +
1094 + $vid_atts['type'] = 'youtube';
1095 + $vid_atts['src'] = '//www.youtube.com/embed/'.$videoId;
1096 + $vid_atts['id'] = $videoId;
731 1097
732 - return '//youtube.com/embed/'.$videoId;
733 -
734 1098 break;
735 1099 case 'vimeo':
736 1100
737 1101 if (preg_match('/player\.vimeo\.com/is', $url['host']) && preg_match('/video/is', $url['path'])) {
738 - $path = explode('/', $url['path']);
739 -
740 - if (isset($path[2]) && !empty($path[2])) {
741 - $videoId = $path[2];
742 - }
743 -
1102 + $path = explode('video/', $source);
744 1103 } else if (preg_match('/vimeo\.com/is', $url['host'])) {
745 - $path = explode('/', $url['path']);
746 -
747 - if (isset($path[1]) && !empty($path[1])) {
748 - $videoId = $path[1];
749 - }
750 -
1104 + $path = explode('.com/', $source);
751 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;
752 1114
753 - return '//player.vimeo.com/video/'.$videoId;
754 -
755 1115 break;
756 1116 default:
1117 +
1118 + $vid_atts['type'] = 'local';
1119 + $vid_atts['src'] = $source;
1120 + $vid_atts['id'] = $videoId;
757 1121
758 - return $source;
759 -
760 1122 }
1123 +
1124 + if(!$no_url){
1125 + return $vid_atts['src'];
1126 + }
1127 +
1128 + return $vid_atts;
761 1129
762 1130 }
763 1131 }
764 1132
@@ -765,8 +1133,12 @@
765 1133
766 1134 // As per the JS specification
767 1135 function pagelayer_escapeHTML($str){
768 1136
1137 + if(is_null($str)){
1138 + return '';
1139 + }
1140 +
769 1141 $replace = [
770 1142 ']' => '&#93;',
771 1143 '[' => '&#91;',
772 1144 //'=' => '&#61;',
@@ -773,9 +1145,10 @@
773 1145 '<' => '&lt;',
774 1146 '>' => '&gt;',
775 1147 '"' => '&quot;',
776 1148 //'&' => '&amp;',
777 - '\'' => '&#39;'
1149 + '\'' => '&#39;',
1150 + '\\' => '&#92;'
778 1151 ];
779 1152
780 1153 $str = str_replace(array_keys($replace), array_values($replace), $str);
781 1154
@@ -783,8 +1156,13 @@
783 1156 }
784 1157
785 1158 // As per the JS specification
786 1159 function pagelayer_unescapeHTML($str){
1160 +
1161 + if(is_null($str)){
1162 + return '';
1163 + }
1164 +
787 1165 $replace = [
788 1166 '#93' => ']',
789 1167 '#91' => '[',
790 1168 //'#61' => '=',
@@ -791,9 +1169,10 @@
791 1169 'lt' => '<',
792 1170 'gt' => '>',
793 1171 'quot' => '"',
794 1172 //'amp' => '&',
795 - '#39' => '\''
1173 + '#39' => '\'',
1174 + '#92' => '\\'
796 1175 ];
797 1176
798 1177 foreach($replace as $k => $v){
799 1178 $str = str_replace('&'.$k.';', $v, $str);
@@ -800,8 +1179,693 @@
800 1179 }
801 1180 return $str;
802 1181 }
803 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 +
804 1868 // Show promo notice on dashboard
805 1869 function pagelayer_show_promo(){
806 1870
807 1871 global $pagelayer_promo_opts;
@@ -807,67 +1871,69 @@
807 1871 global $pagelayer_promo_opts;
808 1872 $opts = $pagelayer_promo_opts;
809 1873
810 1874 echo '<style>
811 -.pagelayer_promo_button {
812 -background-color: #4CAF50; /* Green */
813 -border: none;
814 -color: white;
815 -padding: 6px 10px;
816 -text-align: center;
817 -text-decoration: none;
818 -display: inline-block;
819 -font-size: 13px;
820 -margin: 4px 2px;
821 --webkit-transition-duration: 0.4s; /* Safari */
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;
822 1886 transition-duration: 0.4s;
823 -cursor: pointer;
1887 +cursor: pointer !important;
824 1888 }
825 -.pagelayer_promo_button:focus{
826 -border: none;
827 -color: white;
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;
828 1894 }
829 -.pagelayer_promo_button1 {
830 -color: white;
831 -background-color: #4CAF50;
832 -border:3px solid #4CAF50;
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;
833 1900 }
834 -.pagelayer_promo_button1:hover {
835 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
836 -color: white;
837 -border:3px solid #4CAF50;
1901 +#pagelayer_promo a.pagelayer_promo_button1 {
1902 +color: white !important;
1903 +background-color: #4CAF50 !important;
1904 +border:3px solid #4CAF50 !important;
838 1905 }
839 -.pagelayer_promo_button2 {
840 -color: white;
841 -background-color: #0085ba;
1906 +#pagelayer_promo a.pagelayer_promo_button1:hover {
1907 +border:3px solid #4CAF50 !important;
842 1908 }
843 -.pagelayer_promo_button2:hover {
844 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
845 -color: white;
1909 +#pagelayer_promo a.pagelayer_promo_button2 {
1910 +color: white !important;
1911 +background-color: #0085ba !important;
846 1912 }
847 -.pagelayer_promo_button3 {
848 -color: white;
849 -background-color: #365899;
1913 +#pagelayer_promo a.pagelayer_promo_button3 {
1914 +color: white !important;
1915 +background-color: #365899 !important;
850 1916 }
851 -.pagelayer_promo_button3:hover {
852 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
853 -color: white;
1917 +#pagelayer_promo a.pagelayer_promo_button4 {
1918 +color: white !important;
1919 +background-color: rgb(66, 184, 221) !important;
854 1920 }
855 -.pagelayer_promo_button4 {
856 -color: white;
857 -background-color: rgb(66, 184, 221);
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;
858 1926 }
859 -.pagelayer_promo_button4:hover {
860 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
861 -color: white;
1927 +#pagelayer_promo a.pagelayer_promo-close:hover{
1928 +color: red !important;
862 1929 }
863 -.pagelayer_promo-close{
864 -float:right;
865 -text-decoration:none;
866 -margin: 5px 10px 0px 0px;
1930 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close) {
1931 +color: #4CAF50 !important;
1932 +text-decoration: underline !important;
867 1933 }
868 -.pagelayer_promo-close:hover{
869 -color: red;
1934 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close):hover {
1935 +color: #3e8e41 !important;
870 1936 }
871 1937 </style>
872 1938 <script type="application/javascript">
873 1939 jQuery(document).ready(function(){
@@ -893,14 +1959,21 @@
893 1959
894 1960 echo '
895 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>
896 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>').'
897 1964 '.(empty($opts['rating']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button2" target="_blank" href="'.$opts['rating'].'">Rate it 5★\'s</a>').'
898 1965 '.(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>').'
899 1966 '.(empty($opts['twitter']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['twitter'].'"><span class="dashicons dashicons-twitter"></span> Tweet</a>').'
900 1967 '.(empty($opts['website']) ? '' : '<a class="pagelayer_promo_button pagelayer_promo_button4" target="_blank" href="'.$opts['website'].'">Visit our website</a>').'
901 1968 </p>
902 -</div>';
1969 + <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.';
1970 +
1971 + if(date('Ymd') <= 20200331){
1972 + 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>.';
1973 + }
1974 +
1975 +echo '</p></div>';
903 1976
904 1977 }
905 1978
906 1979 // Are we to show a promo ?
@@ -908,8 +1981,13 @@
908 1981
909 1982 global $pagelayer_promo_opts;
910 1983
911 1984 // There must be an interval
1985 + if(!current_user_can('activate_plugins')){
1986 + return false;
1987 + }
1988 +
1989 + // There must be an interval
912 1990 if(empty($opts['interval'])){
913 1991 return false;
914 1992 }
915 1993
@@ -936,10 +2014,66 @@
936 2014 }
937 2015
938 2016 }
939 2017
2018 +// Show the Pro notice
2019 +function pagelayer_show_pro_notice(){
2020 +
2021 + if(defined('PAGELAYER_PREMIUM')){
2022 + return;
2023 + }
2024 +
2025 + 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>';
2026 +
2027 +}
2028 +
2029 +// Show the Pro Div
2030 +function pagelayer_show_pro_div($head = '', $message = '', $admin_css = 1){
2031 +
2032 + if(defined('PAGELAYER_PREMIUM')){
2033 + return;
2034 + }
2035 +
2036 + if(basename(get_template_directory()) == 'popularfx'){
2037 + $pro_url = 'https://popularfx.com/pricing?from=pagelayer-plugin';
2038 + $pro_txt = 'PopularFX Pro';
2039 + }else{
2040 + $pro_url = PAGELAYER_PRO_PRICE_URL;
2041 + $pro_txt = 'Pagelayer Pro';
2042 + }
2043 +
2044 + if(!empty($admin_css)){
2045 + wp_enqueue_style( 'pagelayer-admin', PAGELAYER_CSS.'/pagelayer-admin.css', array(), PAGELAYER_VERSION);
2046 + }
2047 +
2048 + echo '<div class="pagelayer-pro-div">';
2049 +
2050 + if(!empty($head)){
2051 + echo '<h1 class="pagelayer-pro-head">'.$head.'</h1>';
2052 + }
2053 +
2054 + echo '<div class="pagelayer-pro-message">';
2055 +
2056 + if(empty($message)){
2057 +
2058 + 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.');
2059 +
2060 + }else{
2061 +
2062 + echo $message;
2063 + echo ' '.__('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>.');
2064 +
2065 + }
2066 +
2067 + echo '</div>
2068 + <center><a class="button-pagelayer" href="'.$pro_url.'" target="_blank" style="text-decoration: none;">'.sprintf(__('Upgrade to %s', 'pagelayer'), $pro_txt).'</a></center>
2069 + </div>';
2070 +
2071 +}
2072 +
940 2073 // Bread Crumbs with links
941 2074 function pagelayer_get_breadcrumb(){
2075 + global $post;
942 2076
943 2077 // Home page
944 2078 $ret = '<a href="'.home_url().'" rel="nofollow">{{home}}</a>';
945 2079
@@ -950,9 +2084,9 @@
950 2084 // Is search query
951 2085 }elseif(is_search()){
952 2086 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><b class="pagelayer-breadcrumb-prefix" if="{{search_prefix}}">{{search_prefix}}</b>';
953 2087 $ret .= '<em>';
954 - $ret .= get_search_query();
2088 + $ret .= '<span>'.get_search_query().'</span>';
955 2089 $ret .= '</em>';
956 2090
957 2091 //Is category or single post
958 2092 }elseif(is_category() || is_single()){
@@ -967,18 +2101,35 @@
967 2101 if(is_single()) {
968 2102 /* if (empty($categories)){
969 2103 $ret .= ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ';
970 2104 } */
971 - $ret .= get_the_title();
2105 + $ret .= '<span>'.get_the_title().'</span>';
972 2106 }
973 2107
974 2108 // Is page
975 - }elseif(is_page()){
976 - $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
977 - $ret .= get_the_title();
2109 + }elseif(is_page() ){
2110 + if(!empty($post->post_parent)){
2111 + $ancestors = array_reverse(get_post_ancestors( $post->ID ));
2112 + $page_on_front = get_option('page_on_front');
2113 +
2114 + foreach( $ancestors as $ancestor ){
2115 +
2116 + if($page_on_front == $ancestor){
2117 + continue;
2118 + }
2119 +
2120 + $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><a href="'.get_permalink($ancestor).'">'.get_the_title($ancestor).'</a>';
2121 + }
2122 +
2123 + $ret .=' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><span>'.get_the_title().'</span>';
2124 +
2125 + }else{
2126 + $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2127 + $ret .= '<span>'.get_the_title().'</span>';
2128 + }
978 2129 }else{
979 2130 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
980 - $ret .= wp_title('', false );
2131 + $ret .= '<span>'.wp_title('', false ).'</span>';
981 2132 }
982 2133
983 2134 // wooCommerce Integration left
984 2135
@@ -1052,10 +2203,24 @@
1052 2203
1053 2204 return $ret;
1054 2205 }
1055 2206
2207 +function pagelayer_remove_excerpt_more($more){
2208 + return '';
2209 +}
2210 +
1056 2211 function pagelayer_posts($params, $args = []){
2212 + global $post, $wp_query;
1057 2213
2214 + if(isset($params['exc_length'])){
2215 + $exc_length = (int) $params['exc_length'];
2216 + add_filter( 'excerpt_length', function($length) use($exc_length){
2217 + return $exc_length;
2218 + }, 999 );
2219 + }
2220 +
2221 + // add_filter('excerpt_more', 'pagelayer_remove_excerpt_more', 999);
2222 +
1058 2223 // If args is empty
1059 2224 if(empty($args)){
1060 2225 $args = array(
1061 2226 'post_type' => $params['post_type'],
@@ -1061,52 +2226,98 @@
1061 2226 'post_type' => $params['post_type'],
1062 2227 'posts_per_page' => $params['posts_per_page'],
1063 2228 'order' => $params['order']
1064 2229 );
1065 -
1066 - if($params['exc_length']){
1067 - $exc_length = (int) $params['exc_length'];
1068 - add_filter( 'excerpt_length', function($length) use($exc_length){
1069 - return $exc_length;
1070 - }, 999 );
2230 +
2231 + if(!empty($params['paged'])){
2232 + $args['paged'] = $params['paged'];
1071 2233 }
1072 2234
1073 - if($params['term']){
2235 + if (!empty($params['term']) || !empty($params['exc_term'])) {
2236 + $tax_query = [];
1074 2237
1075 - $term = explode(':', $params['term']);
1076 - //pagelayer_print($term);
1077 - if($term[0] == 'category'){
1078 - $args['category_name'] = $term[1];
2238 + if (!empty($params['term'])) {
2239 + $terms = explode(',', $params['term']);
2240 + $include = array_reduce($terms, function ($carry, $term) {
2241 + list($taxonomy, $slug, $id) = explode(':', $term);
2242 + $carry[$taxonomy][] = $slug;
2243 + return $carry;
2244 + }, []);
2245 +
2246 + $filters = array_filter([
2247 + isset($include['category']) ? [
2248 + 'taxonomy' => 'category',
2249 + 'field' => 'slug',
2250 + 'terms' => $include['category'],
2251 + 'include_children' => false,
2252 + ] : null,
2253 + isset($include['post_tag']) ? [
2254 + 'taxonomy' => 'post_tag',
2255 + 'field' => 'slug',
2256 + 'terms' => $include['post_tag'],
2257 + 'include_children' => false,
2258 + ] : null,
2259 + ]);
2260 +
2261 + if(!empty($filters)){
2262 + $tax_query[] = array_merge(['relation' => 'OR'], $filters);
2263 + }
1079 2264 }
1080 - if($term[0] == 'post_tag'){
1081 - $args['tag'] = $term[1];
2265 +
2266 + // Handle exclusions
2267 + if (!empty($params['exc_term'])) {
2268 + $terms = explode(',', $params['exc_term']);
2269 + $include = array_reduce($terms, function ($carry, $term) {
2270 + list($taxonomy, $slug, $id) = explode(':', $term);
2271 + $carry[$taxonomy][] = $slug;
2272 + return $carry;
2273 + }, []);
2274 +
2275 + $filters = array_filter([
2276 + isset($include['category']) ? [
2277 + 'taxonomy' => 'category',
2278 + 'field' => 'slug',
2279 + 'terms' => $include['category'],
2280 + 'operator' => 'NOT IN',
2281 + 'include_children' => false,
2282 + ] : null,
2283 + isset($include['post_tag']) ? [
2284 + 'taxonomy' => 'post_tag',
2285 + 'field' => 'slug',
2286 + 'terms' => $include['post_tag'],
2287 + 'operator' => 'NOT IN',
2288 + 'include_children' => false,
2289 + ] : null,
2290 + ]);
2291 +
2292 + if(!empty($filters)){
2293 + $tax_query[] = array_merge(['relation' => 'AND'], $filters);
2294 + }
1082 2295 }
1083 - }
1084 2296
1085 - if($params['exc_term']){
1086 -
1087 - $term = explode(':', $params['exc_term']);
1088 - //pagelayer_print($term);
1089 - if($term[0] == 'category'){
1090 - $args['category__not_in'] = $term[2];
2297 + // Apply the combined tax_query
2298 + if(!empty($tax_query)){
2299 + $args['tax_query'] = array_merge(['relation' => 'AND'], $tax_query);
1091 2300 }
1092 - if($term[0] == 'post_tag'){
1093 - $args['tag__not_in'] = $term[2];
1094 - }
1095 2301 }
1096 2302
1097 - if($params['author_name']){
2303 + if(!empty($params['author_name'])){
1098 2304 $author_name = explode(':', $params['author_name']);
1099 2305 $args['author_name'] = $author_name[0];
1100 2306 }
1101 2307
1102 - if($params['exc_author']){
2308 + if(!empty($params['exc_author'])){
1103 2309 $exc_author = explode(':', $params['exc_author']);
1104 2310 $args['author'] = '-'.$exc_author[1];
1105 2311 }
1106 2312
1107 - if(!empty($params['offset'])){
1108 - $args['offset'] = $params['offset'];
2313 + if (!empty($params['offset'])) {
2314 + $args['offset'] = (int) $params['offset'];
2315 +
2316 + // Make Compatible for Infinite load
2317 + if(!empty($params['infinite_types']) && !empty($params['paged']) && $params['paged'] > 1 ){
2318 + $args['offset'] = $args['offset'] + (($params['paged'] - 1) * $params['posts_per_page']);
2319 + }
1109 2320 }
1110 2321
1111 2322 if(!empty($params['ignore_sticky'])){
1112 2323 $args['ignore_sticky_posts'] = $params['ignore_sticky'];
@@ -1115,9 +2326,9 @@
1115 2326 if(!empty($params['orderby'])){
1116 2327 $args['orderby'] = $params['orderby'];
1117 2328 }
1118 2329
1119 - if($params['by_period']){
2330 + if(!empty($params['by_period'])){
1120 2331 $date_arg = array();
1121 2332 switch($params['by_period']){
1122 2333 case 'last_day':
1123 2334 $date_arg['day'] = date('j')-1;
@@ -1142,15 +2353,25 @@
1142 2353 }
1143 2354 }
1144 2355 //pagelayer_print($args);
1145 2356
2357 + // Only see published posts
2358 + $args['post_status'] = 'publish';
2359 +
1146 2360 $postsquery = new WP_Query($args);
1147 2361 $data = '';
1148 2362
2363 + if(!wp_doing_ajax() && !empty($params['infinite_types'])){
2364 + $data.='<div class="pagelayer-post-max" data-max="'.$postsquery->max_num_pages.'"></div>';
2365 + }
2366 +
1149 2367 if(!$postsquery->have_posts()){
1150 - return '<h3>Something went wrong, Please give proper inputs!</h3>';
2368 + return '<h3>No posts found!</h3>';
1151 2369 }
1152 2370
2371 + // To reset the post when the $wp_query->post is empty
2372 + $orig_post = $post;
2373 +
1153 2374 while($postsquery->have_posts()) : $postsquery->the_post();
1154 2375 $data .= '<div class="pagelayer-wposts-col">
1155 2376 <div class="pagelayer-wposts-post">
1156 2377 <div class="pagelayer-wposts-featured">';
@@ -1155,10 +2376,10 @@
1155 2376 <div class="pagelayer-wposts-post">
1156 2377 <div class="pagelayer-wposts-featured">';
1157 2378 $data .= '<a href="'. get_the_permalink() .'">';
1158 2379
1159 - if(isset($params['show_thumb']) && has_post_thumbnail( $postsquery->ID )){
1160 - $data .= '<div class="pagelayer-wposts-thumb"'.(has_post_thumbnail() ? ' style="background:url('.get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']).')"' : '').'></div>';
2380 + if(!empty($params['show_thumb'])){
2381 + $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>';
1161 2382 }
1162 2383 /* if($params['show_thumb'] && has_post_thumbnail( $postsquery->ID )){
1163 2384 $data .= get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']);
1164 2385 } */
@@ -1164,58 +2385,88 @@
1164 2385 } */
1165 2386 $data .= '</a></div>
1166 2387 <div class="pagelayer-wposts-content">';
1167 2388
1168 - if(isset($params['show_title'])){
2389 + if(!empty($params['show_title'])){
1169 2390 $data .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark"><div class="pagelayer-wposts-title">'. get_the_title().'</div></a>';
1170 2391 }
1171 2392
1172 2393 $data .= '<div class="pagelayer-wposts-meta">';
1173 2394 $sep = '';
1174 - if(isset($params['meta_sep'])){
1175 - $sep = '<b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b>';
2395 + if(!empty($params['meta_sep'])){
2396 + $sep = ' <b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b> ';
1176 2397 }
1177 - if(isset($params['author'])){
2398 + if(!empty($params['author'])){
1178 2399 $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;
1179 2400 }
1180 - if(isset($params['date'])){
1181 - $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;
2401 + if(!empty($params['date'])){
2402 + $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;
1182 2403 }
1183 - if(isset($params['category'])){
2404 +
2405 + if(!empty($params['category'])){
1184 2406 $category = get_the_category();
1185 2407 $singlecategory = '';
1186 2408 foreach( $category as $cat ){
1187 2409 $singlecategory .= '<a href="' . get_tag_link( $cat->term_id ) . '">'. $cat->name .'</a>';
1188 2410 }
1189 - $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2411 +
2412 + if(!empty($singlecategory)){
2413 + $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2414 + }
2415 +
1190 2416 }
1191 - if(isset($params['tags'])){
2417 +
2418 + if(!empty($params['tags'])){
1192 2419 $tags = get_the_tags();
1193 2420 $singletag = '';
1194 - foreach( $tags as $tag ){
1195 - $singletag .= '<a href="' . get_tag_link( $tag->term_id ) . '">'. $tag->name .'</a>';
2421 + if(!empty($tags)){
2422 + foreach( $tags as $tag ){
2423 + $singletag .= '<a href="' . get_tag_link( $tag->term_id ) . '">'. $tag->name .'</a>';
2424 + }
2425 + if(!empty($singletag)){
2426 + $data .= '<span class="pagelayer-wposts-tags">'.$singletag.'</span>'.$sep;
2427 + }
1196 2428 }
1197 - $data .= '<span class="pagelayer-wposts-tags">'.$singletag.'</span>'.$sep;
1198 2429
1199 2430 }
1200 - if(isset($params['comments'])){
1201 - $data .= '<span class="pagelayer-wposts-comments"><i class="far fa-comment" /><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()) . '</a></span>'.$sep;
2431 +
2432 + if(!empty($params['comments']) && comments_open($postsquery->ID)){
2433 + $data .= '<span class="pagelayer-wposts-comments"><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()).' '.__pl('comments').'</a></span>'.$sep;
1202 2434 }
1203 2435
1204 2436 $data .= '</div>';
1205 2437
1206 - if(isset($params['show_content'])){
2438 + if(!empty($params['show_content'])){
1207 2439 $data .= '<div class="pagelayer-wposts-excerpt">';
1208 - if($params['show_content'] == 'excerpt'){
1209 - $data .= do_shortcode(get_the_excerpt());
1210 - }elseif($params['show_content'] == 'full'){
1211 - $data .= do_shortcode(get_the_content());
2440 +
2441 + if($params['show_content'] == 'excerpt'){
2442 + $post_excerpt = get_the_excerpt();
2443 + $trimmed_excerpt = !empty($params['exc_length']) ? wp_trim_words($post_excerpt, (int) $params['exc_length']) : $post_excerpt;
2444 + $data .= pagelayer_the_content($trimmed_excerpt);
2445 + } elseif ($params['show_content'] == 'full') {
2446 + $data .= pagelayer_the_content(get_the_content());
1212 2447 }
1213 2448 $data .= '</div>';
1214 2449 }
1215 2450
1216 - if(isset($params['show_more'])){
1217 - $data .= '<a class="pagelayer-wposts-more" href="'. get_the_permalink().'">'.$params['more'].'</a>';
2451 + if(!empty($params['show_more'])){
2452 + $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().'">';
2453 +
2454 + if($params['icon']){
2455 + $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2456 + }
2457 +
2458 + if($params['more']){
2459 + $data .= '<span class="pagelayer-btn-text">'.$params['more'].'</span>';
2460 + }
2461 +
2462 + if($params['icon']){
2463 + $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
2464 + }
2465 +
2466 + $data .= '</a></div>';
2467 +
2468 + //$data .= '<a class="pagelayer-wposts-more" href="'. get_the_permalink().'">'.$params['more'].'</a>';
1218 2469 }
1219 2470
1220 2471 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
1221 2472 $data .= '</div></div></div>';
@@ -1220,8 +2471,15 @@
1220 2471 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
1221 2472 $data .= '</div></div></div>';
1222 2473 endwhile;
1223 2474
2475 + // In the Gutenberg while adding new page the $wp_query->post was empty
2476 + if ( !isset( $wp_query ) || empty($wp_query->post) ) {
2477 + $GLOBALS['post'] = $orig_post;
2478 + }else{
2479 + wp_reset_postdata();
2480 + }
2481 +
1224 2482 return $data;
1225 2483 }
1226 2484
1227 2485 // Get Post Revision
@@ -1320,9 +2578,9 @@
1320 2578 $data .= '<div class="pagelayer-posts-slider-excerpt">';
1321 2579 if($params['post']['show_excerpt'] == "true"){
1322 2580 if(has_excerpt()){
1323 2581 $excerpt = get_the_excerpt();
1324 - $data .= do_shortcode($excerpt);
2582 + $data .= pagelayer_the_content($excerpt);
1325 2583 }
1326 2584 }
1327 2585 $data .= '</div>';
1328 2586 $data .= '<a class="pagelayer-posts-slider-link" href="'. get_the_permalink().'">Read More</a>';
@@ -1354,17 +2612,24 @@
1354 2612 $options .= pagelayer_sel_option($x, $val, $selected);
1355 2613
1356 2614 // Groups
1357 2615 }else{
1358 - $options .= '<optgroup label="'. $x .'">';
1359 2616
1360 - foreach($opt_array[$x] as $y => $gval){
1361 - $options .= pagelayer_sel_option($y, $gval, $selected);
2617 + if(array_key_exists('hide_drop', $opt_array[$x]) && !empty($opt_array[$x]['hide_drop'])){
2618 + continue;
1362 2619 }
1363 2620
1364 - $options .= '</optgroup>';
2621 + // If Label is there, then its a normal option
2622 + if(array_key_exists('label', $opt_array[$x])){
2623 + $options .= pagelayer_sel_option($x, $opt_array[$x]['label'], $selected);
2624 +
2625 + // Optgroups
2626 + } else{
2627 + $options .= '<optgroup label="'. $x .'">';
2628 + $options .= pagelayer_create_sel_options($opt_array[$x], $selected);
2629 + $options .= '</optgroup>';
2630 + }
1365 2631 }
1366 -
1367 2632 }
1368 2633
1369 2634 return $options;
1370 2635 }
@@ -1373,8 +2638,32 @@
1373 2638 function pagelayer_sel_option($val, $lang, $selected){
1374 2639 return '<option value="'. $val .'" '. (($val != $selected) ? '' : 'selected="selected"') .' >'. $lang .'</option>';
1375 2640 }
1376 2641
2642 +// Get values from multi-dimensional array by key
2643 +function pagelayer_multi_array_search(&$array, $key){
2644 +
2645 + if(!is_array($array)){
2646 + return false;
2647 + }
2648 +
2649 + foreach ($array as $k => $v) {
2650 +
2651 + if($k == $key){
2652 + return $v;
2653 + }
2654 +
2655 + if (is_array($v)) {
2656 + $found = pagelayer_multi_array_search($v, $key);
2657 + if(!empty($found)){
2658 + return $found;
2659 + }
2660 + }
2661 + }
2662 +
2663 + return false;
2664 +}
2665 +
1377 2666 function pagelayer_get_post_term(){
1378 2667
1379 2668 $args = [
1380 2669 'taxonomy' => array('category','post_tag'),
@@ -1393,9 +2682,9 @@
1393 2682
1394 2683 function pagelayer_get_post_author(){
1395 2684
1396 2685 $args = [
1397 - 'who' => 'authors',
2686 + 'capability' => array( 'edit_posts' ),
1398 2687 'fields' => [
1399 2688 'ID',
1400 2689 'display_name',
1401 2690 'user_nicename',
@@ -1400,8 +2689,14 @@
1400 2689 'display_name',
1401 2690 'user_nicename',
1402 2691 ]
1403 2692 ];
2693 +
2694 + // Capability queries were only introduced in WP 5.9.
2695 + if( version_compare( $GLOBALS['wp_version'], '5.9-alpha', '<' ) ){
2696 + $args['who'] = 'authors';
2697 + unset( $args['capability'] );
2698 + }
1404 2699
1405 2700 $authors = new \WP_User_Query( $args );
1406 2701
1407 2702 $ret = array();
@@ -1411,8 +2706,36 @@
1411 2706 //pagelayer_print($authors->get_results());die();
1412 2707 return $ret;
1413 2708 }
1414 2709
2710 +// Gets the registered post types
2711 +function pagelayer_get_public_post_types( $args = [] ) {
2712 +
2713 + global $pagelayer;
2714 +
2715 + $post_type_args = [
2716 + 'public' => true,
2717 + ];
2718 +
2719 + $post_type_args = wp_parse_args( $post_type_args, $args );
2720 +
2721 + $_post_types = get_post_types( $post_type_args, 'objects' );
2722 +
2723 + $post_types = array();
2724 +
2725 + foreach ( $_post_types as $post_type => $object ) {
2726 +
2727 + if($post_type == $pagelayer->builder['name']){
2728 + continue;
2729 + }
2730 +
2731 + $post_types[ $post_type ] = $object->label;
2732 + }
2733 + //print_r($post_types);
2734 +
2735 + return $post_types;
2736 +}
2737 +
1415 2738 // Simply echo and dir
1416 2739 function pagelayer_json_output(&$done){
1417 2740
1418 2741 echo json_encode($done);
@@ -1449,10 +2772,67 @@
1449 2772
1450 2773 return $query_args;
1451 2774 }
1452 2775
2776 +// Loads the tags which have parameters of a particular type
2777 +function pagelayer_get_prop_type($types){
2778 +
2779 + global $pagelayer;
2780 +
2781 + if(!is_array($types)){
2782 + $types = [$types];
2783 + }
2784 +
2785 + $ret = [];
2786 +
2787 + // Loop thru all shortcodes
2788 + foreach($pagelayer->shortcodes as $tag => $vvv){
2789 +
2790 + // Lets create the CSS, Classes, Attr. Also clean the dependent atts
2791 + foreach($pagelayer->tabs as $tab){
2792 +
2793 + if(empty($pagelayer->shortcodes[$tag][$tab])){
2794 + continue;
2795 + }
2796 +
2797 + foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
2798 +
2799 + $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
2800 +
2801 + //echo $tab.' - '.$section.' - <br>';
2802 +
2803 + if(empty($props)){
2804 + continue;
2805 + }
2806 +
2807 + // Loop all props
2808 + foreach($props as $prop => $param){
2809 +
2810 + // Load any attachment values
2811 + if(!in_array($param['type'], $types)){
2812 + continue;
2813 + }
2814 +
2815 + $ret[$tag][$prop] = $param['type'];
2816 +
2817 + }
2818 +
2819 + }
2820 +
2821 + }
2822 +
2823 + }
2824 +
2825 + //r_print($ret);die();
2826 +
2827 + return $ret;
2828 +
2829 +}
2830 +
1453 2831 function pagelayer_export_content($content){
1454 -
2832 +
2833 + global $pagelayer;
2834 +
1455 2835 // Just call do_shortcode so we can get list of media files to export
1456 2836 //do_shortcode($content);
1457 2837
1458 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
@@ -1458,7 +2838,1617 @@
1458 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
1459 2839
1460 2840 $content = preg_replace('/http(s?):\/\/'.preg_quote($theme_url, '/').'/is', '{{theme_url}}', $content);
1461 2841
2842 + // Remove unnecessary spaces
2843 + $content = preg_replace('/--\>\s*(?!<!--)/is', '-->', $content);
2844 +
2845 + // Replace links of the theme for images
2846 + $content = pagelayer_export_theme_links($content, get_stylesheet_directory_uri());
2847 +
2848 + ////////////////////
2849 + // Handle Link IDs
2850 + ////////////////////
2851 +
2852 + if(empty($pagelayer->e['link_tags'])){
2853 + $pagelayer->e['link_tags'] = pagelayer_get_prop_type('link');
2854 + //r_print($pagelayer->e['link_tags']);die();
2855 + }
2856 +
2857 + if(empty($pagelayer->e['media_tags'])){
2858 + $pagelayer->e['media_tags'] = pagelayer_get_prop_type(['image', 'video', 'audio', 'media', 'multi_image']);
2859 + //r_print($pagelayer->e['media_tags']);die();
2860 + }
2861 +
2862 + $parsed = parse_blocks($content);
2863 + //r_print($parsed);//die();
2864 +
2865 + $parsed = pagelayer_export_link_ids($parsed);
2866 +
2867 + // Export the media as well
2868 + $parsed = pagelayer_export_media($parsed);
2869 +
2870 + // Reserialize
2871 + $content = serialize_blocks($parsed);
2872 +
2873 + // We store in SP format
2874 + $content = str_replace('<!-- wp:pagelayer', '<!-- sp:pagelayer', $content);
2875 + $content = str_replace('<!-- /wp:pagelayer', '<!-- /sp:pagelayer', $content);
2876 +
2877 + // Just pass the content to
2878 + $content = pagelayer_export_extract_menu_ids($content);
2879 +
2880 + // Apply a filter
2881 + $content = apply_filters('pagelayer_export_content', $content);
2882 +
1462 2883 return $content;
1463 2884
2885 +}
2886 +
2887 +// Export the menu IDs
2888 +function pagelayer_export_extract_menu_ids($content){
2889 +
2890 + global $pagelayer;
2891 +
2892 + // Any Menu ?
2893 + preg_match_all('/\[pl_wp_menu ([^\]]*)nav_list="(\d*)"([^\]]*)\]/is', $content, $matches);
2894 + //r_print($matches);
2895 +
2896 + if(!empty($matches[2])){
2897 +
2898 + foreach($matches[2] as $k => $v){
2899 + $v = (int)$v;
2900 + $pagelayer->export_menus[$v] = $v;
2901 + }
2902 + //r_print($pagelayer->export_menus);
2903 +
2904 + }
2905 +
2906 + // Also for block format
2907 + preg_match_all('/<!--\s+(?P<closer>\/)?sp:pagelayer\/pl_wp_menu\s+(?P<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?}\s+)?(?P<void>\/)?-->/s', $content, $matches);
2908 +
2909 + if(!empty($matches['attrs'])){
2910 +
2911 + foreach($matches['attrs'] as $k => $v){
2912 + preg_match('/nav_list"\s*:\s*"(\d*)"/is', $v, $vmatch);
2913 +
2914 + if(!empty($vmatch[1])){
2915 + $vmatch[1] = (int)$vmatch[1];
2916 + $pagelayer->export_menus[$vmatch[1]] = $vmatch[1];
2917 + //r_print($pagelayer->export_menus);
2918 + }
2919 + }
2920 + }
2921 +
2922 + return $content;
2923 +
2924 +}
2925 +
2926 +function pagelayer_export_link_ids($parsed){
2927 +
2928 + global $pagelayer;
2929 +
2930 + foreach($parsed as $k => $v){
2931 +
2932 + if(!empty($v['innerBlocks'])){
2933 + $parsed[$k]['innerBlocks'] = pagelayer_export_link_ids($v['innerBlocks']);
2934 + }
2935 +
2936 + if(!preg_match('/pagelayer/is', $v['blockName'])){
2937 + continue;
2938 + }
2939 +
2940 + $tag = str_replace('pagelayer/', '', $v['blockName']);
2941 + //echo $tag;//die();
2942 +
2943 + // Is there a tag having a link prop
2944 + if(empty($pagelayer->e['link_tags'][$tag])){
2945 + continue;
2946 + }
2947 + //echo $tag;die();
2948 +
2949 + foreach($pagelayer->e['link_tags'][$tag] as $kk => $vv){
2950 + if(!empty($v['attrs'][$kk]) && is_numeric($v['attrs'][$kk])){
2951 + $linked = get_post($v['attrs'][$kk]);
2952 + if(!empty($linked->post_name)){
2953 + $parsed[$k]['attrs'][$kk] = '||link_id|'.$linked->post_type.'|'.$linked->post_name.'||';
2954 + }
2955 + //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
2956 + }
2957 + }
2958 +
2959 + }
2960 +
2961 + return $parsed;
2962 +
2963 +}
2964 +
2965 +// Replace theme links with theme_url
2966 +function pagelayer_export_theme_links($content, $link){
2967 +
2968 + global $sitepad;
2969 +
2970 + // Theme URL correction code
2971 + $theme_url = preg_replace('/http(s?):\/\//is', '', $link);
2972 +
2973 + $theme_urls[0] = 'http://'.$theme_url;
2974 + $theme_urls[1] = 'https://'.$theme_url;
2975 + $theme_urls[2] = $theme_url;
2976 + $theme_urls[3] = str_replace('/', '\\/', $theme_urls[0]);
2977 + $theme_urls[4] = str_replace('/', '\\/', $theme_urls[1]);
2978 + $theme_urls[5] = str_replace('/', '\\/', $theme_url);
2979 +
2980 + foreach($theme_urls as $k => $v){
2981 + $content = preg_replace_callback('/'.preg_quote($v, '/').'([^"]*)/is', 'pagelayer_export_theme_links_replacer', $content);
2982 + }
2983 +
2984 + $content = str_ireplace($theme_urls, '{{theme_url}}', $content);
2985 +
2986 + return $content;
2987 +}
2988 +
2989 +// This function is a part of pagelayer_export_theme_links
2990 +function pagelayer_export_theme_links_replacer($matches){
2991 + return '{{theme_url}}'.str_replace('\\/', '/', $matches[1]);
2992 +}
2993 +
2994 +// Export media
2995 +function pagelayer_export_media($parsed){
2996 +
2997 + global $pagelayer;
2998 +
2999 + foreach($parsed as $k => $v){
3000 +
3001 + if(!empty($v['innerBlocks'])){
3002 + $parsed[$k]['innerBlocks'] = pagelayer_export_media($v['innerBlocks']);
3003 + }
3004 +
3005 + if(!preg_match('/pagelayer/is', $v['blockName'])){
3006 + continue;
3007 + }
3008 +
3009 + $tag = str_replace('pagelayer/', '', $v['blockName']);
3010 + //echo $tag;//die();
3011 +
3012 + // Is there a tag having a link prop
3013 + if(empty($pagelayer->e['media_tags'][$tag])){
3014 + continue;
3015 + }
3016 + //echo $tag;die();
3017 +
3018 + foreach($pagelayer->e['media_tags'][$tag] as $kk => $vv){
3019 +
3020 + if(empty($v['attrs'][$kk])){
3021 + continue;
3022 + }
3023 +
3024 + $is_array = 0;
3025 +
3026 + // Is it an array
3027 + if(is_array($v['attrs'][$kk])){
3028 +
3029 + $ids = $v['attrs'][$kk];
3030 + $is_array = 1;
3031 +
3032 + // Is it of the format 1,2,3
3033 + }elseif(preg_match('/^((\d*)(,?))*$/is', $v['attrs'][$kk])){
3034 +
3035 + $ids = pagelayer_maybe_explode(',', $v['attrs'][$kk]);
3036 +
3037 + // Its a number or string
3038 + }else{
3039 +
3040 + // Is it our default image ?
3041 + if(preg_match('/pagelayer-pro\/images\/default-image.png$/is', $v['attrs'][$kk])){
3042 + $parsed[$k]['attrs'][$kk] = '';
3043 + continue;
3044 + }
3045 +
3046 + // It should be a number
3047 + if(!is_numeric($v['attrs'][$kk])){
3048 + continue;
3049 + }
3050 +
3051 + $ids = [$v['attrs'][$kk]];
3052 + }
3053 +
3054 + $ret = [];
3055 +
3056 + foreach($ids as $id){
3057 +
3058 + $file = pagelayer_export_media_files($id, $exp_img_url);
3059 +
3060 + // Did it export ?
3061 + if(empty($file) || !file_exists($file)){
3062 + continue;
3063 + }
3064 +
3065 + $ret[] = $exp_img_url;
3066 +
3067 + }
3068 +
3069 + $parsed[$k]['attrs'][$kk] = $is_array ? $ret : implode(',', $ret);
3070 + //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
3071 + }
3072 +
3073 + }
3074 +
3075 + return $parsed;
3076 +
3077 +}
3078 +
3079 +// Exports the media to the images folder of the current theme
3080 +function pagelayer_export_media_files($id, &$url = ''){
3081 +
3082 + $theme_dir = get_stylesheet_directory();
3083 + $image_dir = $theme_dir.'/images/';
3084 + @mkdir($image_dir);
3085 +
3086 + // Get the file path
3087 + $file = get_attached_file($id);
3088 +
3089 + if(empty($file) || !file_exists($file)){
3090 + return false;
3091 + }
3092 +
3093 + $dest_file = $image_dir.basename($file);
3094 +
3095 + // Copy the file
3096 + copy($file, $dest_file);
3097 +
3098 + // Replace the text
3099 + $url = '{{theme_url}}/images/'.basename($file);
3100 +
3101 + return $dest_file;
3102 +
3103 + //echo $content;
3104 +}
3105 +
3106 +// Insert a post which is a Pagelayer Post
3107 +function pagelayer_sanitize_content($content){
3108 +
3109 + if(empty($content)){
3110 + return $content;
3111 + }
3112 +
3113 + // Replace Vars
3114 + $template_vars = pagelayer_template_vars();
3115 +
3116 + foreach($template_vars as $key => $val){
3117 + $content = str_replace($key, $val, $content);
3118 + }
3119 +
3120 + if(defined('PAGELAYER_BLOCK_PREFIX') && PAGELAYER_BLOCK_PREFIX == 'wp'){
3121 + $content = str_replace('<!-- sp:pagelayer', '<!-- wp:pagelayer', $content);
3122 + $content = str_replace('<!-- /sp:pagelayer', '<!-- /wp:pagelayer', $content);
3123 + }
3124 +
3125 + return $content;
3126 +}
3127 +
3128 +// Insert a post which is a Pagelayer Post
3129 +function pagelayer_insert_content($post, &$ret){
3130 +
3131 + $post = apply_filters('pagelayer_start_insert_content', $post);
3132 +
3133 + $post['post_content'] = pagelayer_sanitize_content($post['post_content']);
3134 + //pagelayer_print($post);die();
3135 +
3136 + // Add slashes for safe insert
3137 + $post['post_content'] = wp_slash($post['post_content']);
3138 +
3139 + $post = apply_filters('pagelayer_pre_insert_content', $post);
3140 +
3141 + // Now insert / update the post
3142 + $ret = wp_insert_post($post);
3143 +
3144 + // Did we save the post ?
3145 + if(empty($ret) || is_wp_error($ret)){
3146 + return false;
3147 + }
3148 +
3149 + // Convert to pagelayer accessed post
3150 + if(!add_post_meta($ret, 'pagelayer-data', time(), true)){
3151 + update_post_meta($ret, 'pagelayer-data', time());
3152 + }
3153 +
3154 + return $ret;
3155 +
3156 +}
3157 +
3158 +// Gets the list of enabled fonts
3159 +function pagelayer_enabled_icons(){
3160 +
3161 + $stored_icons = get_option('pagelayer_icons_set');
3162 + if(empty($stored_icons)){
3163 + update_option('pagelayer_icons_set', ['font-awesome5']);
3164 + $stored_icons = get_option('pagelayer_icons_set');
3165 + }
3166 +
3167 + return $stored_icons;
3168 +
3169 +}
3170 +
3171 +// Prevent pro activate text for installer
3172 +function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
3173 +
3174 + if($plugin_file == PAGELAYER_PREMIUM_BASE){
3175 + return array();
3176 + }
3177 +
3178 + return $install_actions;
3179 +}
3180 +
3181 +// Handle hexa to rgba and also remove alpha which is ff
3182 +function pagelayer_hex8_to_rgba($val){
3183 +
3184 + // If opacity is ff then discard ff
3185 + if(preg_match('/^#([a-f0-9]{6})ff$/is', $val)){
3186 + return substr($val, 0, 7);
3187 + }
3188 +
3189 + // Lets handle the RGB+opacity
3190 + if(preg_match('/#([a-f0-9]{8})$/is', $val)){
3191 + $val = pagelayer_hex2rgba($val);
3192 + }
3193 +
3194 + return $val;
3195 +
3196 +}
3197 +
3198 +// Convert to RGBA from HEX
3199 +function pagelayer_hex2rgba($color){
3200 +
3201 + //Return if no color provided
3202 + if(empty($color)){
3203 + return;
3204 + }
3205 +
3206 + //Sanitize $color if "#" is provided
3207 + if ($color[0] == '#'){
3208 + $color = substr( $color, 1 );
3209 + }
3210 +
3211 + //Check if color has 6 or 3 characters and get values
3212 + if (strlen($color) == 8) {
3213 + $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3214 + $alpha = $color[6] . $color[7];
3215 + //$alpha = '';
3216 + } elseif (strlen($color) == 6) {
3217 + $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
3218 + } elseif ( strlen( $color ) == 3 ) {
3219 + $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
3220 + } else {
3221 + return;
3222 + }
3223 +
3224 + //Convert hexadec to rgb
3225 + $rgb = array_map('hexdec', $hex);
3226 +
3227 + //Check if opacity is set(rgba or rgb)
3228 + if($alpha){
3229 +
3230 + $alpha = number_format((float)hexdec($alpha) / 255, 2, '.', '');
3231 + //print_r($alpha);
3232 + if(abs($alpha) > 1){
3233 + $alpha = 1.0;
3234 + }
3235 + $output = 'rgba('.implode(",",$rgb).','.$alpha.')';
3236 + } else {
3237 + $output = 'rgb('.implode(",",$rgb).')';
3238 + }
3239 +
3240 + //Return rgb(a) color string
3241 + return $output;
3242 +}
3243 +
3244 +// Get social URLs
3245 +function pagelayer_get_social_urls(){
3246 +
3247 + $urls = array();
3248 +
3249 + $urls['facebook'] = get_option('pagelayer-facebook-url');
3250 + $urls['twitter'] = get_option('pagelayer-twitter-url');
3251 + $urls['instagram'] = get_option('pagelayer-instagram-url');
3252 + $urls['linkedin'] = get_option('pagelayer-linkedin-url');
3253 + $urls['youtube'] = get_option('pagelayer-youtube-url');
3254 + $urls['google'] = get_option('pagelayer-gplus-url');
3255 +
3256 + foreach($urls as $k => $v){
3257 +
3258 + if(empty($v)){
3259 + unset($urls[$k]);
3260 + continue;
3261 + }
3262 +
3263 + $urls[$k] = esc_url($v);
3264 + }
3265 +
3266 + return $urls;
3267 +}
3268 +
3269 +function pagelayer_get_option($opt){
3270 + $ret = get_option($opt);
3271 +
3272 + $opts = array(
3273 + 'pagelayer-address' => '1, My Address, My Street, New York City, NY, USA',
3274 + 'pagelayer-phone' => '+1234567890',
3275 + 'pagelayer-copyright' => '© '.date('Y').' '.get_option('blogname'),
3276 + 'pagelayer_cf_to_email' => 'contact@domain.com',
3277 + 'pagelayer_cf_success' => __pl('cf_success'),
3278 + 'pagelayer_cf_failed' => __pl('cf_failed'),
3279 + );
3280 +
3281 + if(empty($ret)){
3282 + return $opts[$opt];
3283 + }
3284 +
3285 + return wp_kses_post($ret);
3286 +}
3287 +
3288 +// Uploads an image / media
3289 +function pagelayer_upload_media($filename, $blob){
3290 +
3291 + if(empty($blob)){
3292 + return false;
3293 + }
3294 +
3295 + $md5 = md5($blob);
3296 +
3297 + // Do we have this image
3298 + $args = array(
3299 + 'post_type' => 'attachment',
3300 + 'post_status' => 'inherit',
3301 + 'meta_query' => array(
3302 + array(
3303 + 'key' => 'pagelayer_image_md5',
3304 + 'value' => $md5,
3305 + )
3306 + )
3307 + );
3308 +
3309 + $query = new WP_Query($args);
3310 +
3311 + // If we found the image, return
3312 + foreach($query->posts as $ck => $cv){
3313 + return $cv->ID;
3314 + }
3315 +
3316 + $upload = wp_upload_bits($filename, null, $blob);
3317 +
3318 + if( !empty( $upload['error'] ) ) {
3319 + return false;
3320 + }
3321 +
3322 + $file_path = $upload['file'];
3323 + $file_name = basename( $file_path );
3324 + $file_type = wp_check_filetype( $file_name, null );
3325 + $attachment_title = sanitize_file_name( pathinfo( $file_name, PATHINFO_FILENAME ) );
3326 + $wp_upload_dir = wp_upload_dir();
3327 +
3328 + $post_info = array(
3329 + 'guid' => $wp_upload_dir['url'] . '/' . $file_name,
3330 + 'post_mime_type'=> $file_type['type'],
3331 + 'post_title' => $attachment_title,
3332 + 'post_content' => '',
3333 + 'post_status' => 'inherit',
3334 + );
3335 +
3336 + $attach_id = wp_insert_attachment( $post_info, $file_path, $parent_post_id );
3337 + update_post_meta($attach_id, 'pagelayer_image_md5', $md5);
3338 +
3339 + $lib = ABSPATH . 'site-admin/includes/image.php';
3340 + $media = ABSPATH . 'site-admin/includes/media.php';
3341 + $media = file_exists($media) ? $media : ABSPATH . 'wp-admin/includes/media.php';
3342 + $lib = file_exists($lib) ? $lib : ABSPATH . 'wp-admin/includes/image.php';
3343 +
3344 + require_once($media);
3345 + require_once($lib);
3346 +
3347 + $attach_data = wp_generate_attachment_metadata( $attach_id, $file_path );
3348 + wp_update_attachment_metadata( $attach_id, $attach_data );
3349 +
3350 + return $attach_id;
3351 +
3352 +}
3353 +
3354 +// Show the notice of importing the active themes content
3355 +function pagelayer_theme_import_notices($return = false){
3356 +
3357 + $theme = wp_get_theme();
3358 +
3359 + $imported = get_option('pagelayer_theme_'.get_template().'_imported');
3360 + $show = 0;
3361 +
3362 + // We need to import the content
3363 + if(empty($imported) && is_admin() && current_user_can('switch_themes')){
3364 + $show = 1;
3365 + }
3366 +
3367 + $dismissed = get_option('pagelayer_theme_'.get_template().'_dismissed');
3368 +
3369 + // Is this dismissed
3370 + if($dismissed){
3371 + $show = 0;
3372 + }
3373 +
3374 + //$show = 1;
3375 +
3376 + // Is it the importer page ?
3377 + if(!empty($_REQUEST['page']) && $_REQUEST['page'] == 'pagelayer_import'){
3378 + $show = 0;
3379 + }
3380 +
3381 + // Show the message
3382 + if(!empty($show)){
3383 + $str = '
3384 +
3385 +<style>
3386 +.pagelayer_promo_button {
3387 +background-color: #4CAF50; /* Green */
3388 +border: none;
3389 +color: white;
3390 +padding: 6px 10px;
3391 +text-align: center;
3392 +text-decoration: none;
3393 +display: inline-block;
3394 +font-size: 13px;
3395 +margin: 4px 2px;
3396 +-webkit-transition-duration: 0.4s; /* Safari */
3397 +transition-duration: 0.4s;
3398 +cursor: pointer;
3399 +}
3400 +.pagelayer_promo_button:focus,
3401 +.pagelayer_promo_button:hover{
3402 +border: none;
3403 +color: white;
3404 +box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
3405 +color: white;
3406 +}
3407 +.pagelayer_promo_buy {
3408 +color: white;
3409 +padding: 8px 12px;
3410 +font-size: 14px;
3411 +}
3412 +.pagelayer_promo_button1 {
3413 +color: white;
3414 +background-color: #4CAF50;
3415 +border:3px solid #4CAF50;
3416 +}
3417 +.pagelayer_promo_button1:hover {
3418 +border:3px solid #4CAF50;
3419 +}
3420 +.pagelayer_promo_button2 {
3421 +color: white;
3422 +background-color: #0085ba;
3423 +}
3424 +.pagelayer_promo_button3 {
3425 +color: white;
3426 +background-color: #365899;
3427 +}
3428 +.pagelayer_promo_button4 {
3429 +color: white;
3430 +background-color: rgb(66, 184, 221);
3431 +}
3432 +.pagelayer_promo-close{
3433 +float:right;
3434 +text-decoration:none;
3435 +margin: 5px 10px 0px 0px;
3436 +}
3437 +.pagelayer_promo-close:hover{
3438 +color: red;
3439 +}
3440 +</style>
3441 +
3442 +<script type="application/javascript">
3443 + jQuery(document).ready(function(){
3444 + jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
3445 + var data;
3446 + jQuery("#pagelayer_promo").hide();
3447 + // Save this preference
3448 + jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
3449 + //alert(response);
3450 + });
3451 + });
3452 + });
3453 +</script>
3454 +
3455 +<div class="notice notice-success" id="pagelayer_promo" style="min-height:30px">
3456 + <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
3457 + <span class="dashicons dashicons-dismiss"></span> Dismiss
3458 + </a>';
3459 +
3460 + $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>').'
3461 +
3462 +</div>';
3463 +
3464 + // Print it
3465 + if(empty($return)){
3466 + echo $str;
3467 + }else{
3468 + return $str;
3469 + }
3470 + }
3471 +
3472 +}
3473 +
3474 +// Parse the array variables
3475 +function pagelayer_replace_vars( $string, $array, $pre = '', $suf = ''){
3476 +
3477 + $array = (array) $array;
3478 +
3479 + foreach($array as $kk => $vv){
3480 +
3481 + if(is_array($vv)){
3482 + $vv = pagelayer_flat_join($vv);
3483 + }
3484 +
3485 + $string = str_replace($pre.$kk.$suf, $vv, $string);
3486 + }
3487 + return $string;
3488 +}
3489 +
3490 +// Add Attachment to the contact the email
3491 +function pagelayer_cf_email_attachment( &$phpmailer) {
3492 +
3493 + if ( ! empty( $_FILES ) ) {
3494 + foreach ( $_FILES as $attachment ) {
3495 +
3496 + if(empty($attachment['name'])){
3497 + continue;
3498 + }
3499 +
3500 + try {
3501 + $phpmailer->addAttachment($attachment['tmp_name'], $attachment['name'], $encoding = 'base64', $attachment['type'], $disposition = 'attachment');
3502 + } catch ( \Exception $e ) {
3503 + continue;
3504 + }
3505 + }
3506 + }
3507 +
3508 +}
3509 +
3510 +function pagelayer_array_flatten( $input ) {
3511 + if ( ! is_array( $input ) ) {
3512 + return array( $input );
3513 + }
3514 +
3515 + $output = array();
3516 +
3517 + foreach ( $input as $value ) {
3518 + $output = array_merge( $output, pagelayer_array_flatten( $value ) );
3519 + }
3520 +
3521 + return $output;
3522 +}
3523 +
3524 +function pagelayer_flat_join( $array ) {
3525 + $array = pagelayer_array_flatten( $array );
3526 + $output = array();
3527 +
3528 + foreach ( (array) $array as $value ) {
3529 + $output[] = trim( (string) $value );
3530 + }
3531 +
3532 + return implode( ', ', $output );
3533 +}
3534 +
3535 +// Verifies the Google Captcha
3536 +function pagelayer_captcha_verify(){
3537 +
3538 + // If secret key is not there, return
3539 + $captcha_secret = get_option('pagelayer_google_captcha_secret');
3540 +
3541 + if(empty($captcha_secret)){
3542 + return true;
3543 + }
3544 +
3545 + $response = !empty($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : '';
3546 +
3547 + $ip = pagelayer_getip();
3548 +
3549 + // Is the IP or response not there ?
3550 + if(empty($response) || empty($ip)){
3551 + return false;
3552 + }
3553 +
3554 + $url = 'https://www.google.com/recaptcha/api/siteverify';
3555 +
3556 + // Verify the post
3557 + $req = wp_remote_post($url, array(
3558 + 'timeout' => 20,
3559 + 'body' => array(
3560 + 'secret' => $captcha_secret,
3561 + 'response' => $response,
3562 + 'remoteip' => $ip
3563 + )
3564 + )
3565 + );
3566 +
3567 + // Was there an error posting ?
3568 + if(is_wp_error($req)){
3569 + return false;
3570 + }
3571 +
3572 + // Process the post response
3573 + $resp = wp_remote_retrieve_body($req);
3574 +
3575 + // Is the body valid
3576 + if(empty($resp)){
3577 + return false;
3578 + }
3579 +
3580 + $json = json_decode($resp, true);
3581 +
3582 + if(!empty($json['success']) && // for v2 and v3
3583 + (!isset($json['score']) || $json['score'] >= 0.5 && $json['action'] === 'submit') // For v3
3584 + ){
3585 + return true;
3586 + }
3587 +
3588 + return false;
3589 +}
3590 +
3591 +// Pagelayer serialize attributes
3592 +function pagelayer_serialize_attributes( $block_attributes ) {
3593 + $encoded_attributes = json_encode( $block_attributes, JSON_FORCE_OBJECT);
3594 + $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
3595 + $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
3596 + $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
3597 + // Regex: /\\"/
3598 + $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
3599 +
3600 + return $encoded_attributes;
3601 +}
3602 +
3603 +function pagelayer_enable_giver(){
3604 + global $pagelayer;
3605 + return !empty($pagelayer->settings['enable_giver']) && $pagelayer->settings['enable_giver'] == 1;
3606 +}
3607 +
3608 +function pagelayer_load_font_options(){
3609 + include_once(PAGELAYER_DIR.'/main/font-options.php');
3610 +}
3611 +
3612 +function pagelayer_is_utf8($str) {
3613 + return (bool) preg_match('//u', $str);
3614 +}
3615 +
3616 +// Create blank images
3617 +function pagelayer_create_blank_image($width, $height){
3618 +
3619 + $im = @imagecreate($width, $height);
3620 +
3621 + if(empty($im)){
3622 + return false;
3623 + }
3624 +
3625 + $background_color = imagecolorallocate($im, 220, 220, 220);
3626 + $text_color = imagecolorallocate($im, 110, 110, 110);
3627 + $string = $width.' x '.$height;
3628 + $font_size = 30;
3629 + $sx = ($width / 2) - (strlen($string) * $font_size / 2);
3630 + $sy = ($height / 2) - ($font_size / 2);
3631 + $font = realpath(PAGELAYER_DIR.'/fonts/OpenSans-BoldItalic.ttf');
3632 + imagettftext($im, $font_size, 0, $sx, $sy, $text_color, $font, $string);
3633 + ob_start();
3634 + imagepng($im);
3635 + imagedestroy($im);
3636 +
3637 + return ob_get_clean();
3638 +
3639 +}
3640 +
3641 +// Get category name by post type
3642 +function pagelayer_post_type_category($post_type){
3643 +
3644 + $taxonomies = get_object_taxonomies( $post_type, 'objects');
3645 +
3646 + foreach( $taxonomies as $key => $tax){
3647 + if(!empty($tax->hierarchical)){
3648 + return $key;
3649 + }
3650 + }
3651 +
3652 + return false;
3653 +
3654 +}
3655 +
3656 +// Get tag name by post type
3657 +function pagelayer_post_type_tag($post_type){
3658 +
3659 + $taxonomies = get_object_taxonomies( $post_type, 'objects');
3660 +
3661 + foreach( $taxonomies as $key => $tax){
3662 + if(!empty($tax->show_tagcloud) && empty($tax->hierarchical)){
3663 + return $key;
3664 + }
3665 + }
3666 + return false;
3667 +
3668 +}
3669 +
3670 +// Get checkboxes of post categories.
3671 +function pagelayer_post_cats($post, &$cat_name = ''){
3672 +
3673 + $ret = [];
3674 +
3675 + $cat_name = pagelayer_post_type_category($post->post_type);
3676 +
3677 + if(empty($cat_name)){
3678 + return $ret;
3679 + }
3680 +
3681 + include_once(ABSPATH.PAGELAYER_CMS_DIR_PREFIX.'-admin/includes/template.php');
3682 +
3683 + $args1 = array(
3684 + 'taxonomy' => $cat_name,
3685 + 'checked_ontop' => false,
3686 + 'echo' => false,
3687 + );
3688 +
3689 + $ret['with_checkbox'] = '<ul class="pagelayer-post-category" >'.wp_terms_checklist($post->ID, $args1 ).'</ul>';
3690 +
3691 + $args2 = array(
3692 + 'taxonomy' => $cat_name,
3693 + 'hierarchical' => true,
3694 + 'echo' => 0,
3695 + 'class' => 'pagelayer-add-cat',
3696 + 'name' => 'pagelayer_cat_parent',
3697 + 'style' => 'list',
3698 + 'hide_empty' => 0,
3699 + );
3700 +
3701 + $ret['without_checkbox'] = wp_dropdown_categories( $args2 );
3702 +
3703 + return $ret;
3704 +
3705 +}
3706 +
3707 +// Get checkboxes of post tags.
3708 +function pagelayer_post_tags($post, &$tag_name = ''){
3709 +
3710 + $ret = [];
3711 +
3712 + $tag_name = pagelayer_post_type_tag($post->post_type);
3713 +
3714 + if(empty($tag_name)){
3715 + return $ret;
3716 + }
3717 +
3718 + $ret['postTags'] = wp_get_post_terms( $post->ID, $tag_name );
3719 +
3720 + $ret['allTags'] = get_tags(array( 'hide_empty' => false, 'taxonomy' => $tag_name ));
3721 +
3722 + return $ret;
3723 +
3724 +}
3725 +
3726 +// Get users who can edit a particular post type
3727 +function pagelayer_post_authors_by_type($post_type){
3728 +
3729 + global $wp_post_types;
3730 +
3731 + if(empty($wp_post_types[$post_type])){
3732 + $users = get_users( array( 'role__in' => array( 'administrator' ) ) );
3733 + }else{
3734 + $req_role = $wp_post_types[$post_type]->cap->edit_posts;
3735 + $roles__in = [];
3736 + foreach( wp_roles()->roles as $role_slug => $role ){
3737 + if( ! empty( $role['capabilities'][$req_role] ) )
3738 + $roles__in[] = $role_slug;
3739 + }
3740 + $users = get_users( array( 'role__in' => $roles__in ) );
3741 + }
3742 +
3743 + $user_array = [];
3744 +
3745 + foreach ( $users as $user ) {
3746 + $user_array[$user->ID] = $user->display_name;
3747 + }
3748 +
3749 + return $user_array;
3750 +}
3751 +
3752 +function pagelayer_parent_post_prop($post){
3753 + global $wp_post_types;
3754 +
3755 + if(!empty($wp_post_types[$post->post_type]->hierarchical)){
3756 + return pagelayer_list_parents(0, 0, $post, []);
3757 + }else{
3758 + return [];
3759 + }
3760 +}
3761 +
3762 +function pagelayer_list_parents( $parent = 0, $level = 0, $post = null, $values=array('')){
3763 +
3764 + global $wpdb;
3765 +
3766 + $post = get_post( $post );
3767 +
3768 + $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_title FROM $wpdb->posts
3769 + WHERE post_parent = %d
3770 + AND post_type = '".$post->post_type."'
3771 + AND post_status = 'publish'
3772 + ORDER BY menu_order", $parent ) );
3773 +
3774 + if ( !empty($items) ) {
3775 + foreach ( $items as $item ) {
3776 +
3777 + // A page cannot be its own parent.
3778 + if ( $post && $post->ID && (int) $item->ID === $post->ID ) {
3779 + continue;
3780 + }
3781 +
3782 + $pad = str_repeat( '-', $level );
3783 +
3784 + if(count($values)==0){
3785 + $values[] = '';
3786 + }
3787 +
3788 + $values[' '.$item->ID] = $pad.esc_html( $item->post_title );
3789 +
3790 + $values = pagelayer_list_parents($item->ID, $level + 1, $post, $values );
3791 + }
3792 + }
3793 +
3794 + return $values;
3795 +}
3796 +
3797 +// Maybe explode
3798 +function pagelayer_maybe_explode( $separator = ',', $string = '', $limit = PHP_INT_MAX ){
3799 +
3800 + if(is_array($string)){
3801 + return $string;
3802 + }
3803 +
3804 + return explode($separator, $string, $limit);
3805 +}
3806 +
3807 +// Maybe implode
3808 +function pagelayer_maybe_implode($array, $separator = ','){
3809 +
3810 + if(is_array($array)){
3811 + return @implode($separator, $array);
3812 + }
3813 +
3814 + if(is_null($array)){
3815 + return '';
3816 + }
3817 +
3818 + return $array;
3819 +
3820 +}
3821 +
3822 +// Sanitize text field or array
3823 +function pagelayer_sanitize_text_field($str) {
3824 +
3825 + if ( !is_object( $str ) && !is_array( $str ) ) {
3826 + return sanitize_text_field($str);
3827 + }
3828 +
3829 + if( is_array($str) ){
3830 + return array_map( 'pagelayer_sanitize_text_field', $str);
3831 + }
3832 +
3833 + return $str;
3834 +}
3835 +
3836 +// Sanitize posts data for WP_Query
3837 +function pagelayer_sanitize_posts_data($data, $only_allowed = true) {
3838 +
3839 + $allowed_keys = [
3840 + 'post_type', 'posts_per_page', 'order', 'orderby', 'paged',
3841 + 'filter_by', 'term', 'exc_term', 'cat', 'category_name',
3842 + 'tag', 'author', 'author_name', 'post__in', 'post__not_in',
3843 + 'include', 'exclude', 'search', 's', 'exact', 'sentence',
3844 + 'post_status', 'post_parent', 'offset',
3845 + 'posts_per_archive_page', 'page', 'ignore_sticky_posts'
3846 + ];
3847 +
3848 + $sanitized = [];
3849 +
3850 + foreach($data as $key => $value){
3851 + if($only_allowed && !in_array($key, $allowed_keys)) {
3852 + continue;
3853 + }
3854 +
3855 + $sanitized[$key] = pagelayer_sanitize_text_field($value);
3856 + }
3857 +
3858 + // Security: Restrict post_status to prevent information disclosure
3859 + // Only users who can read private posts or edit others' posts should be able to query non-public statuses
3860 + if(isset($sanitized['post_status'])){
3861 + $requested_status = $sanitized['post_status'];
3862 +
3863 + // If requesting something other than publish, verify permissions
3864 + if ($requested_status !== 'publish') {
3865 + // Check if the user has permission to read private posts or edit others' posts
3866 + // This prevents contributors from seeing titles of private posts they don't own.
3867 + if (!current_user_can('read_private_posts') && !current_user_can('edit_others_posts')) {
3868 + $sanitized['post_status'] = 'publish';
3869 + }
3870 + }
3871 + }else{
3872 + // Default to publish for safety if not specified
3873 + $sanitized['post_status'] = 'publish';
3874 + }
3875 +
3876 + if(isset($sanitized['posts_per_page'])){
3877 + $sanitized['posts_per_page'] = (int) $sanitized['posts_per_page'];
3878 + if ($sanitized['posts_per_page'] > 100) {
3879 + $sanitized['posts_per_page'] = 100;
3880 + }
3881 + }
3882 +
3883 + if(isset($sanitized['paged'])){
3884 + $sanitized['paged'] = (int) $sanitized['paged'];
3885 + }
3886 +
3887 + if(isset($sanitized['offset'])){
3888 + $sanitized['offset'] = (int) $sanitized['offset'];
3889 + }
3890 +
3891 + if(isset($sanitized['post__in']) && is_string($sanitized['post__in'])){
3892 + $sanitized['post__in'] = array_map('intval', explode(',', $sanitized['post__in']));
3893 + }
3894 +
3895 + if(isset($sanitized['post__not_in']) && is_string($sanitized['post__not_in'])){
3896 + $sanitized['post__not_in'] = array_map('intval', explode(',', $sanitized['post__not_in']));
3897 + }
3898 +
3899 + if(isset($sanitized['post_parent'])){
3900 + $sanitized['post_parent'] = (int) $sanitized['post_parent'];
3901 + }
3902 +
3903 + if(isset($sanitized['cat'])){
3904 + $sanitized['cat'] = (int) $sanitized['cat'];
3905 + }
3906 +
3907 + if(isset($sanitized['author'])){
3908 + $sanitized['author'] = (int) $sanitized['author'];
3909 + }
3910 +
3911 + return $sanitized;
3912 +}
3913 +
3914 +// Update nav menu item
3915 +function pagelayer_save_nav_menu_items($items){
3916 +
3917 + // Menu setting keys
3918 + $allowed_post = array('title' => 'post_title');
3919 + $allowed_meta = array('_pagelayer_content');
3920 +
3921 + foreach($items as $id => $values){
3922 +
3923 + $meta_input = array();
3924 + $id = (int) $id;
3925 + $post = get_post($id);
3926 +
3927 + // Post found ?
3928 + if(empty($post)){
3929 + continue;
3930 + }
3931 +
3932 + foreach($values as $kk => $vv){
3933 +
3934 + if(array_key_exists($kk, $allowed_post)){
3935 + $name = $allowed_post[$kk];
3936 + $post->$name = $vv;
3937 + }
3938 +
3939 + if(!in_array($kk, $allowed_meta)){
3940 + continue;
3941 + }
3942 +
3943 + $meta_input[$kk] = stripslashes_deep($vv);
3944 + }
3945 +
3946 + if(isset($meta_input['_pagelayer_content'])){
3947 + $meta_input['_pagelayer_content'] = base64_decode($meta_input['_pagelayer_content']);
3948 + }
3949 +
3950 + $post->meta_input = $meta_input;
3951 + wp_update_post($post);
3952 + }
3953 +}
3954 +
3955 +// Update customizer options
3956 +function pagelayer_save_customizer_options($customizer_options){
3957 + global $pagelayer;
3958 +
3959 + // To load customizer params array
3960 + if(empty($pagelayer->customizer_params)){
3961 + pagelayer_load_shortcodes();
3962 + }
3963 +
3964 + $customizer_options = apply_filters( 'pagelayer_save_customizer_options', $customizer_options );
3965 + $customizer_data = array();
3966 + $customizer_option_data = array();
3967 +
3968 + foreach($pagelayer->customizer_params as $ck => $cv){
3969 + foreach($pagelayer->screens as $sk => $sv){
3970 +
3971 + $screen = (!empty($sv) ? '_'.$sv : '');
3972 + $screen_key = $ck.$screen;
3973 +
3974 + // Save to pagelayer customizer mods
3975 + if( empty($cv['option']) ){
3976 + if( isset($customizer_options[$screen_key])){
3977 + $customizer_data[$screen_key] = $customizer_options[$screen_key];
3978 + }
3979 + continue;
3980 + }
3981 +
3982 + $option_key = $cv['option'].$screen;
3983 +
3984 + // Update option
3985 + if( isset($customizer_options[$screen_key] ) ) {
3986 + update_option( $option_key, $customizer_options[$screen_key]);
3987 + continue;
3988 + }
3989 +
3990 + delete_option($option_key);
3991 + }
3992 + }
3993 +
3994 + // Set Typography settings for global and current post type
3995 + $posttype=array(
3996 + '' => '', // Make this empty for global values
3997 + 'current' => $customizer_options['pagelayer_current_post_type']
3998 + );
3999 +
4000 + $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4001 +
4002 + foreach($posttype as $type => $tv){
4003 +
4004 + foreach($pagelayer->css_settings as $set => $params){
4005 +
4006 + foreach($pagelayer->screens as $sk => $sv){
4007 +
4008 + $screen = (!empty($sv) ? '_'.$sv : '');
4009 + $post_type = (!empty($tv) ? '_'.$tv : '');
4010 + $post_key = (!empty($type) ? $type.'_' : '');
4011 + $post_key_set = $post_key.$set;
4012 +
4013 + // Create key as per customizer shortcode
4014 + $key = $post_key_set.$screen;
4015 +
4016 + // Create option keys for the global and current post type
4017 + $option_setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$post_type : $params['key'].$post_type;
4018 +
4019 + $props = array(
4020 + 'color' => $post_key_set.'_color'.$screen,
4021 + 'padding' => $post_key_set.'_padding'.$screen,
4022 + 'margin' => $post_key_set.'_margin'.$screen,
4023 + 'background-color' => $post_key_set.'_background_color'.$screen,
4024 + );
4025 +
4026 + $globalsetting = array();
4027 +
4028 + // Set Typography
4029 + if(isset($customizer_options[$key])){
4030 + foreach($customizer_options[$key] as $k => $v){
4031 + if($v == 'Default' || empty($v)){
4032 + unset($customizer_options[$key][$k]);
4033 + }
4034 +
4035 + // For sidebar, width default should not be saved
4036 + if($set == 'sidebar' && $k == 'width' && $v == 20){
4037 + unset($customizer_options[$key][$k]);
4038 + }
4039 +
4040 + $globalsetting[$key][$typo[$k]] = $customizer_options[$key][$k];
4041 + unset($customizer_options[$key][$k]);
4042 + }
4043 + }
4044 +
4045 + // Set other props with typography
4046 + foreach($props as $pk => $pv){
4047 +
4048 + if(!isset($customizer_options[$pv])){
4049 + continue;
4050 + }
4051 +
4052 + $globalsetting[$key][$pk] = $customizer_options[$pv];
4053 + unset($customizer_options[$pv]);
4054 +
4055 + }
4056 +
4057 + // Are we to save ?
4058 + if(!empty($globalsetting[$key])){
4059 + update_option($option_setting.$screen, $globalsetting[$key]);
4060 + }else{
4061 + delete_option($option_setting.$screen);
4062 + }
4063 +
4064 + }
4065 +
4066 + }
4067 + }
4068 +
4069 + $customizer_data = apply_filters( 'pagelayer_customizer_options', $customizer_data, $customizer_options);
4070 +
4071 + update_option('pagelayer_customizer_mods', $customizer_data);
4072 +}
4073 +
4074 +// Get customizer options values
4075 +// return array to set customizer block attributes
4076 +function pagelayer_get_customizer_options(){
4077 + global $pagelayer, $post;
4078 +
4079 + $options = array();
4080 +
4081 + // To load customizer params array
4082 + if(empty($pagelayer->customizer_params)){
4083 + pagelayer_load_shortcodes();
4084 + }
4085 +
4086 + // Get values form options
4087 + foreach($pagelayer->customizer_params as $ck => $cv){
4088 +
4089 + if( empty($cv['option']) ){
4090 + continue;
4091 + }
4092 +
4093 + $screens = array('desktop' => '');
4094 +
4095 + if( !empty($cv['screen']) ){
4096 + $screens = $pagelayer->screens;
4097 + }
4098 +
4099 + foreach($screens as $sk => $sv){
4100 +
4101 + $screen = (!empty($sv) ? '_'.$sv : '');
4102 + $screen_key = $cv['option'].$screen;
4103 + $screen_ck = $ck.$screen;
4104 +
4105 + // Get option value
4106 + $c_option = get_option($screen_key);
4107 +
4108 + if(empty($c_option)){
4109 + continue;
4110 + }
4111 +
4112 + $options[$screen_ck] = $c_option;
4113 + }
4114 + }
4115 +
4116 +
4117 + // Get values from pagelayer mods
4118 + $mods = get_option('pagelayer_customizer_mods');
4119 +
4120 + if(!empty($mods)){
4121 + $options = array_merge($options, $mods);
4122 + }
4123 +
4124 + $posttype=array(
4125 + '' => '',
4126 + 'current' => $post->post_type
4127 + );
4128 +
4129 + $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4130 +
4131 + foreach($posttype as $type => $tv){
4132 +
4133 + foreach($pagelayer->css_settings as $set => $params){
4134 +
4135 + foreach($pagelayer->screens as $sk => $sv){
4136 +
4137 + $screen = (!empty($sv) ? '_'.$sv : '');
4138 + $postprefix = (!empty($tv) ? '_'.$tv : '');
4139 + $postk = (!empty($type) ? $type.'_' : '');
4140 + $post_key_set = $postk.$set;
4141 +
4142 + // Create key as per customizer shortcode
4143 + $key = $post_key_set.$screen;
4144 +
4145 + // Create option keys for the global and current post type
4146 + $setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$postprefix : $params['key'].$postprefix;
4147 +
4148 + $props = array(
4149 + 'color' => $post_key_set.'_color'.$screen,
4150 + 'padding' => $post_key_set.'_padding'.$screen,
4151 + 'margin' => $post_key_set.'_margin'.$screen,
4152 + 'background-color' => $post_key_set.'_background_color'.$screen,
4153 + );
4154 +
4155 + $optarray = get_option($setting.$screen);
4156 +
4157 + if(!empty($optarray)){
4158 +
4159 + // Get Typography
4160 + foreach($typo as $tk => $tv){
4161 +
4162 + $tval = '';
4163 +
4164 + if(isset($optarray[$tv])){
4165 + $tval = $optarray[$tv];
4166 + }
4167 +
4168 + $options[$key][$tk] = $tval;
4169 + }
4170 +
4171 + foreach($props as $pk => $pv){
4172 + if(empty( $optarray[$pk])){
4173 + continue;
4174 + }
4175 +
4176 + $options[$pv] = $optarray[$pk];
4177 + }
4178 + }
4179 + }
4180 + }
4181 + }
4182 +
4183 + $options = apply_filters( 'pagelayer_get_customizer_options', $options);
4184 +
4185 + return $options;
4186 +}
4187 +
4188 +// Sanitize style props for colors and fons
4189 +function pagelayer_load_global_palette(){
4190 + global $pagelayer;
4191 +
4192 + // Default global colors
4193 + $pagelayer->global_colors = array(
4194 + 'primary' => array(
4195 + 'title' => 'Primary',
4196 + 'value' => '#007bff',
4197 + ),
4198 + 'secondary' => array(
4199 + 'title' => 'Secondary',
4200 + 'value' => '#6c757d',
4201 + ),
4202 + 'text' => array(
4203 + 'title' => 'Text',
4204 + 'value' => '#1d1d1d',
4205 + ),
4206 + 'accent' => array(
4207 + 'title' => 'Accent',
4208 + 'value' => '#61ce70',
4209 + ),
4210 + );
4211 +
4212 + // Global color
4213 + $global_color = get_option('pagelayer_global_colors');
4214 +
4215 + if(!empty($global_color)){
4216 + $pagelayer->global_colors = json_decode($global_color, true);
4217 + }
4218 +
4219 + // Default global fonts
4220 + $pagelayer->global_fonts = array(
4221 + 'primary' => array(
4222 + 'title' => 'Primary',
4223 + 'value' => $pagelayer->default_font_styles(),
4224 + ),
4225 + 'secondary' => array(
4226 + 'title' => 'Secondary',
4227 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Roboto')),
4228 + ),
4229 + 'text' => array(
4230 + 'title' => 'Text',
4231 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Montserrat')),
4232 + ),
4233 + 'accent' => array(
4234 + 'title' => 'Accent',
4235 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Poppins')),
4236 + ),
4237 + );
4238 +
4239 + // Global font
4240 + $global_font = get_option('pagelayer_global_fonts');
4241 +
4242 + if(!empty($global_font)){
4243 + $pagelayer->global_fonts = json_decode($global_font, true);
4244 + }
4245 +}
4246 +
4247 +// Get global colors
4248 +function pagelayer_sanitize_global_color($val){
4249 + global $pagelayer;
4250 +
4251 + if(!is_string($val) || $val[0] != '$'){
4252 + return $val;
4253 + }
4254 +
4255 + $gkey = substr($val, 1);
4256 + $gcolor = isset($pagelayer->global_colors[$gkey]) ? $gkey : 'primary';
4257 + $val = 'var(--pagelayer-color-'.$gcolor.')';
4258 +
4259 + return $val;
4260 +}
4261 +
4262 +// Sanitize style props for colors and fons
4263 +function pagelayer_sanitize_global_style($val, $val_desk = '', $mode = 'desktop'){
4264 + global $pagelayer;
4265 +
4266 + // Sanitize Global background color
4267 + if( !empty($val['background-color']) && $val['background-color'][0] == '$'){
4268 + $val['background-color'] = pagelayer_sanitize_global_color($val['background-color']);
4269 + }
4270 +
4271 + // Sanitize Global color
4272 + if( !empty($val['color']) && $val['color'][0] == '$'){
4273 + $val['color'] = pagelayer_sanitize_global_color($val['color']);
4274 + }
4275 +
4276 + // Not set global for typo
4277 + if(empty($val['global-font']) && empty($val_desk['global-font'])){
4278 + return $val;
4279 + }
4280 +
4281 + $_desk_global = false;
4282 +
4283 + if(empty($val['global-font'])){
4284 + $font_key = @$val_desk['global-font'];
4285 + $_desk_global = true;
4286 + }else{
4287 + $font_key = $val['global-font'];
4288 + unset($val['global-font']);
4289 + }
4290 +
4291 + if(empty($font_key)){
4292 + return $val;
4293 + }
4294 +
4295 + if(!isset($pagelayer->global_fonts[$font_key])){
4296 + $font_key = 'primary';
4297 + }
4298 +
4299 + $_val = [];
4300 +
4301 + // Apply global typo
4302 + foreach($pagelayer->typo_props as $typo_key){
4303 +
4304 + if(!empty($val[$typo_key])){
4305 + continue;
4306 + }
4307 +
4308 + $global_val = $pagelayer->global_fonts[$font_key]['value'];
4309 +
4310 + 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') ){
4311 + continue;
4312 + }
4313 +
4314 + $_val[$typo_key] = 'var(--pagelayer-font-'.$font_key.'-'.$typo_key.')';
4315 + }
4316 +
4317 + // This array contains other values like margin padding so we merge the array
4318 + $val = empty($val) ? array() : $val;
4319 + $val = array_merge($val, $_val);
4320 + return $val;
4321 +}
4322 +
4323 +//Check an empty array
4324 +function pagelayer_is_empty_array($arr){
4325 +
4326 + foreach ($arr as $key => $value) {
4327 + if (trim($value) !== '') return false;
4328 + }
4329 +
4330 + return true;
4331 +}
4332 +
4333 +// Pagelayer load font family
4334 +function pagelayer_load_font_family($font, $font_weight='', $font_style=''){
4335 + global $pagelayer;
4336 +
4337 + // Load global fonts
4338 + if(strripos($font, 'var(') !== false){
4339 + $matches = [];
4340 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-family\))/i', $font, $matches);
4341 + $font_key = @$matches[2];
4342 + $font = @$pagelayer->global_fonts[$font_key]['value']['font-family'];
4343 + }
4344 +
4345 + // Load global fonts weight
4346 + if(!empty($font_weight) && strripos($font_weight, 'var(') !== false){
4347 + $matches = [];
4348 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-weight\))/i', $font_weight, $matches);
4349 + $font_key = @$matches[2];
4350 +
4351 + // Convert in string
4352 + $font_weight = @$pagelayer->global_fonts[$font_key]['value']['font-weight'];
4353 + }
4354 +
4355 + // Load global fonts style
4356 + if(!empty($font_style) && strripos($font_style, 'var(') !== false){
4357 + $matches = [];
4358 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-style\))/i', $font_style, $matches);
4359 + $font_key = @$matches[2];
4360 +
4361 + // Convert in string
4362 + $font_style = @$pagelayer->global_fonts[$font_key]['value']['font-style'];
4363 + }
4364 +
4365 + if(empty($font)){
4366 + return;
4367 + }
4368 +
4369 + $font_weights = array('400' =>'400');
4370 + $font_style = empty($font_style) ? 'normal' : $font_style;
4371 + $font_style = in_array($font_style, ['italic', 'oblique']) ? 'i' : '';
4372 +
4373 + if(!empty($font_weight)){
4374 +
4375 + if(!is_array($font_weight)){
4376 + $font_weight = array($font_weight);
4377 + }
4378 +
4379 + foreach($font_weight as $weight){
4380 + $font_weights[$weight.$font_style] = $weight.$font_style;
4381 + }
4382 + }
4383 +
4384 + foreach($font_weights as $fkey => $fvalue){
4385 + $pagelayer->runtime_fonts[$font][$fkey] = $fvalue;
4386 + }
4387 +
4388 +}
4389 +
4390 +function pagelayer_update_plugin_notice(){
4391 + if(defined('SOFTACULOUS_PLUGIN_UPDATE_NOTICE')){
4392 + return;
4393 + }
4394 +
4395 + $to_update_plugins = apply_filters('softaculous_plugin_update_notice', []);
4396 +
4397 + if(empty($to_update_plugins)){
4398 + return;
4399 + }
4400 +
4401 + /* 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 */
4402 + $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>';
4403 +
4404 + define('SOFTACULOUS_PLUGIN_UPDATE_NOTICE', true); // To make sure other plugins don't return a Notice
4405 + echo '<div class="notice notice-info is-dismissible" id="pagelayer-plugin-update-notice">
4406 + <p>'.$msg. '</p>
4407 + </div>';
4408 +
4409 + wp_register_script('pagelayer-update-notice', '', ['jquery'], '', true);
4410 + wp_enqueue_script('pagelayer-update-notice');
4411 + wp_add_inline_script('pagelayer-update-notice', 'jQuery("#pagelayer-plugin-update-notice").on("click", function(e){
4412 + let target = jQuery(e.target);
4413 +
4414 + if(!target.hasClass("notice-dismiss")){
4415 + return;
4416 + }
4417 +
4418 + var data;
4419 +
4420 + // Hide it
4421 + jQuery("#pagelayer-plugin-update-notice").hide();
4422 +
4423 + // Save this preference
4424 + jQuery.post("'.admin_url('admin-ajax.php?action=pagelayer_close_update_notice').'&pagelayer_nonce='.wp_create_nonce('pagelayer_promo_nonce').'", data, function(response) {
4425 + //alert(response);
4426 + });
4427 + });');
4428 +}
4429 +
4430 +function pagelayer_update_plugin_notice_filter($plugins = []){
4431 + $plugins['pagelayer/pagelayer.php'] = 'Pagelayer';
4432 + return $plugins;
4433 +}
4434 +
4435 +function pagelayer_is_comment_mode(){
4436 +
4437 + if(file_exists(PAGELAYER_DIR.'/comment_mode.php') && !empty($_REQUEST['cmode'])){
4438 + include_once(PAGELAYER_DIR.'/comment_mode.php');
4439 + return true;
4440 + }
4441 +
4442 + return false;
4443 +}
4444 +
4445 +function pagelayer_esc_crlf($value){
4446 +
4447 + // Remove CRLF to prevent header injection
4448 + $value = str_replace(array("\r", "\n", "%0a", "%0d"), '', $value);
4449 +
4450 + // Trim spaces
4451 + $value = trim($value);
4452 +
4453 + return $value;
1464 4454 }