PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 2.2.0
Page Builder: Pagelayer – Drag and Drop website builder v2.2.0
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 +3025 -226 1.0.32.2.0 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,11 +742,14 @@
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'),
536 747 'position_styles' => __pl('position_styles'),
537 748 'animation_styles' => __pl('animation_styles'),
749 + 'motion_effects' => __pl('Motion Effects'),
538 750 'responsive_styles' => __pl('responsive_styles'),
751 + 'attributes' => __pl('attributes'),
539 752 'custom_styles' => __pl('custom_styles'),
540 753 ];
541 754
542 755 if(!empty($params['skip_props_cat'])){
@@ -575,26 +788,133 @@
575 788 }
576 789 }
577 790 }
578 791 //die();*/
579 -
792 +
793 + $params = apply_filters( 'pagelayer_shortcode_params', $params, $tag );
794 +
580 795 // Insert the shortcode
581 796 $pagelayer->shortcodes[$tag] = $params;
582 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 +}
583 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);
584 867 }
585 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 +
586 886 // Returns the Image values
587 -function pagelayer_image($id){
887 +function pagelayer_image($id = 0){
588 888
589 889 global $pagelayer;
590 890
591 891 $ret = [];
592 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 +
593 913 // External image ?
594 914 if(pagelayer_is_external_img($id)){
595 915
596 - $ret['full-url'] = $id;
916 + $ret['url'] = $id;
597 917
598 918 // Attachment
599 919 }elseif(!empty($id)){
600 920
@@ -622,30 +942,43 @@
622 942 $title = esc_attr($image->post_title);
623 943 $alt = get_post_meta($id, '_wp_attachment_image_alt', true);
624 944 $alt = empty($alt) ? $image->post_excerpt : $alt;
625 945 $alt = empty($alt) ? $image->post_title : $alt;
626 - $alt = empty($alt) ? '' : trim(strip_tags($alt));
946 + $alt = empty($alt) ? '' : esc_attr(trim(strip_tags($alt)));
627 947 $link = get_attachment_link($id);
628 948 $caption = wp_get_attachment_caption($id);
629 - $caption = !empty($caption) ? $caption : '';
949 + $caption = !empty($caption) ? esc_attr($caption) : '';
630 950
631 - $ret['alt'] = $alt;
632 - $ret['title'] = $title;
633 - $ret['link'] = $link;
634 - $ret['caption'] = $caption;
951 + }
635 952
636 - }
953 + }
637 954
955 + // First preference to full url
956 + if(!empty($ret['full-url'])){
957 + $ret['url'] = $ret['full-url'];
638 958 }
639 959
640 960 // No image
641 - if(empty($ret['full-url'])){
642 - $ret['full-url'] = PAGELAYER_URL.'/images/default-image.png';
961 + if(empty($ret['url'])){
962 + $ret['url'] = PAGELAYER_URL.'/images/default-image.png';
643 963 }
644 964
645 - $ret['url'] = $ret['full-url'];
965 + // Extract filename without extension
966 + $filename = pathinfo($ret['url'], PATHINFO_FILENAME);
646 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 +
647 976 $ret = apply_filters('pagelayer_image', $ret);
977 +
978 + if(pagelayer_is_default_img($ret['url'])){
979 + $ret['no-image-set'] = 1;
980 + }
648 981
649 982 return $ret;
650 983
651 984 }
@@ -664,8 +997,19 @@
664 997 return false;
665 998
666 999 }
667 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 +
668 1012 // Returns the attachment url
669 1013 function pagelayer_attachment($id){
670 1014
671 1015 $ret = [];
@@ -694,15 +1038,20 @@
694 1038 }
695 1039
696 1040 // Convert the regular URL of a Video to a Embed URL
697 1041 // Todo : Check
698 -function pagelayer_video_url($source){
1042 +function pagelayer_video_url($source, $no_url = false){
699 1043
1044 + global $pagelayer;
1045 +
700 1046 if (!empty($source)) {
701 - $source = filter_var($source, FILTER_SANITIZE_URL);
1047 +
1048 + $source = esc_url( $source );
702 1049 $source = str_replace('&amp;', '&', $source);
703 1050 $url = parse_url($source);
704 1051 $videoSite ='';
1052 + $videoId ='';
1053 + $vid_atts = [];
705 1054
706 1055 $youtubeRegExp = '/youtube\.com|youtu\.be/is';
707 1056 $vimeoRegExp = '/vimeo\.com/is';
708 1057
@@ -714,8 +1063,10 @@
714 1063
715 1064 switch ($videoSite) {
716 1065 case 'youtube':
717 1066
1067 + $pagelayer->append_yt_api = true;
1068 +
718 1069 if (preg_match('/youtube\.com/is', $source)) {
719 1070
720 1071 if (preg_match('/watch/is', $source)) {
721 1072 parse_str($url['query'], $parameters);
@@ -738,38 +1089,44 @@
738 1089 $videoId = $path[1];
739 1090 }
740 1091
741 1092 }
1093 +
1094 + $vid_atts['type'] = 'youtube';
1095 + $vid_atts['src'] = '//www.youtube.com/embed/'.$videoId;
1096 + $vid_atts['id'] = $videoId;
742 1097
743 - return '//youtube.com/embed/'.$videoId;
744 -
745 1098 break;
746 1099 case 'vimeo':
747 1100
748 1101 if (preg_match('/player\.vimeo\.com/is', $url['host']) && preg_match('/video/is', $url['path'])) {
749 - $path = explode('/', $url['path']);
750 -
751 - if (isset($path[2]) && !empty($path[2])) {
752 - $videoId = $path[2];
753 - }
754 -
1102 + $path = explode('video/', $source);
755 1103 } else if (preg_match('/vimeo\.com/is', $url['host'])) {
756 - $path = explode('/', $url['path']);
757 -
758 - if (isset($path[1]) && !empty($path[1])) {
759 - $videoId = $path[1];
760 - }
761 -
1104 + $path = explode('.com/', $source);
762 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;
763 1114
764 - return '//player.vimeo.com/video/'.$videoId;
765 -
766 1115 break;
767 1116 default:
1117 +
1118 + $vid_atts['type'] = 'local';
1119 + $vid_atts['src'] = $source;
1120 + $vid_atts['id'] = $videoId;
768 1121
769 - return $source;
770 -
771 1122 }
1123 +
1124 + if(!$no_url){
1125 + return $vid_atts['src'];
1126 + }
1127 +
1128 + return $vid_atts;
772 1129
773 1130 }
774 1131 }
775 1132
@@ -776,8 +1133,12 @@
776 1133
777 1134 // As per the JS specification
778 1135 function pagelayer_escapeHTML($str){
779 1136
1137 + if(is_null($str)){
1138 + return '';
1139 + }
1140 +
780 1141 $replace = [
781 1142 ']' => '&#93;',
782 1143 '[' => '&#91;',
783 1144 //'=' => '&#61;',
@@ -784,9 +1145,10 @@
784 1145 '<' => '&lt;',
785 1146 '>' => '&gt;',
786 1147 '"' => '&quot;',
787 1148 //'&' => '&amp;',
788 - '\'' => '&#39;'
1149 + '\'' => '&#39;',
1150 + '\\' => '&#92;'
789 1151 ];
790 1152
791 1153 $str = str_replace(array_keys($replace), array_values($replace), $str);
792 1154
@@ -794,8 +1156,13 @@
794 1156 }
795 1157
796 1158 // As per the JS specification
797 1159 function pagelayer_unescapeHTML($str){
1160 +
1161 + if(is_null($str)){
1162 + return '';
1163 + }
1164 +
798 1165 $replace = [
799 1166 '#93' => ']',
800 1167 '#91' => '[',
801 1168 //'#61' => '=',
@@ -802,9 +1169,10 @@
802 1169 'lt' => '<',
803 1170 'gt' => '>',
804 1171 'quot' => '"',
805 1172 //'amp' => '&',
806 - '#39' => '\''
1173 + '#39' => '\'',
1174 + '#92' => '\\'
807 1175 ];
808 1176
809 1177 foreach($replace as $k => $v){
810 1178 $str = str_replace('&'.$k.';', $v, $str);
@@ -811,8 +1179,693 @@
811 1179 }
812 1180 return $str;
813 1181 }
814 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 +
815 1868 // Show promo notice on dashboard
816 1869 function pagelayer_show_promo(){
817 1870
818 1871 global $pagelayer_promo_opts;
@@ -818,62 +1871,70 @@
818 1871 global $pagelayer_promo_opts;
819 1872 $opts = $pagelayer_promo_opts;
820 1873
821 1874 echo '<style>
822 -.pagelayer_promo_button {
823 -background-color: #4CAF50; /* Green */
824 -border: none;
825 -color: white;
826 -padding: 6px 10px;
827 -text-align: center;
828 -text-decoration: none;
829 -display: inline-block;
830 -font-size: 13px;
831 -margin: 4px 2px;
832 --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;
833 1886 transition-duration: 0.4s;
834 -cursor: pointer;
1887 +cursor: pointer !important;
835 1888 }
836 -.pagelayer_promo_button:focus,
837 -.pagelayer_promo_button:hover{
838 -border: none;
839 -color: white;
840 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
841 -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;
842 1894 }
843 -.pagelayer_promo_buy {
844 -color: white;
845 -padding: 8px 12px;
846 -font-size: 14px;
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;
847 1900 }
848 -.pagelayer_promo_button1 {
849 -color: white;
850 -background-color: #4CAF50;
851 -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;
852 1905 }
853 -.pagelayer_promo_button1:hover {
854 -border:3px solid #4CAF50;
1906 +#pagelayer_promo a.pagelayer_promo_button1:hover {
1907 +border:3px solid #4CAF50 !important;
855 1908 }
856 -.pagelayer_promo_button2 {
857 -color: white;
858 -background-color: #0085ba;
1909 +#pagelayer_promo a.pagelayer_promo_button2 {
1910 +color: white !important;
1911 +background-color: #0085ba !important;
859 1912 }
860 -.pagelayer_promo_button3 {
861 -color: white;
862 -background-color: #365899;
1913 +#pagelayer_promo a.pagelayer_promo_button3 {
1914 +color: white !important;
1915 +background-color: #365899 !important;
863 1916 }
864 -.pagelayer_promo_button4 {
865 -color: white;
866 -background-color: rgb(66, 184, 221);
1917 +#pagelayer_promo a.pagelayer_promo_button4 {
1918 +color: white !important;
1919 +background-color: rgb(66, 184, 221) !important;
867 1920 }
868 -.pagelayer_promo-close{
869 -float:right;
870 -text-decoration:none;
871 -margin: 5px 10px 0px 0px;
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;
872 1926 }
873 -.pagelayer_promo-close:hover{
874 -color: red;
1927 +#pagelayer_promo a.pagelayer_promo-close:hover{
1928 +color: red !important;
875 1929 }
1930 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close) {
1931 +color: #4CAF50 !important;
1932 +text-decoration: underline !important;
1933 +}
1934 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close):hover {
1935 +color: #3e8e41 !important;
1936 +}
876 1937 </style>
877 1938 <script type="application/javascript">
878 1939 jQuery(document).ready(function(){
879 1940 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
@@ -920,8 +1981,13 @@
920 1981
921 1982 global $pagelayer_promo_opts;
922 1983
923 1984 // There must be an interval
1985 + if(!current_user_can('activate_plugins')){
1986 + return false;
1987 + }
1988 +
1989 + // There must be an interval
924 1990 if(empty($opts['interval'])){
925 1991 return false;
926 1992 }
927 1993
@@ -948,10 +2014,66 @@
948 2014 }
949 2015
950 2016 }
951 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 +
952 2073 // Bread Crumbs with links
953 2074 function pagelayer_get_breadcrumb(){
2075 + global $post;
954 2076
955 2077 // Home page
956 2078 $ret = '<a href="'.home_url().'" rel="nofollow">{{home}}</a>';
957 2079
@@ -962,9 +2084,9 @@
962 2084 // Is search query
963 2085 }elseif(is_search()){
964 2086 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><b class="pagelayer-breadcrumb-prefix" if="{{search_prefix}}">{{search_prefix}}</b>';
965 2087 $ret .= '<em>';
966 - $ret .= get_search_query();
2088 + $ret .= '<span>'.get_search_query().'</span>';
967 2089 $ret .= '</em>';
968 2090
969 2091 //Is category or single post
970 2092 }elseif(is_category() || is_single()){
@@ -979,18 +2101,35 @@
979 2101 if(is_single()) {
980 2102 /* if (empty($categories)){
981 2103 $ret .= ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ';
982 2104 } */
983 - $ret .= get_the_title();
2105 + $ret .= '<span>'.get_the_title().'</span>';
984 2106 }
985 2107
986 2108 // Is page
987 - }elseif(is_page()){
988 - $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
989 - $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 + }
990 2129 }else{
991 2130 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
992 - $ret .= wp_title('', false );
2131 + $ret .= '<span>'.wp_title('', false ).'</span>';
993 2132 }
994 2133
995 2134 // wooCommerce Integration left
996 2135
@@ -1064,10 +2203,24 @@
1064 2203
1065 2204 return $ret;
1066 2205 }
1067 2206
2207 +function pagelayer_remove_excerpt_more($more){
2208 + return '';
2209 +}
2210 +
1068 2211 function pagelayer_posts($params, $args = []){
2212 + global $post, $wp_query;
1069 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 +
1070 2223 // If args is empty
1071 2224 if(empty($args)){
1072 2225 $args = array(
1073 2226 'post_type' => $params['post_type'],
@@ -1073,52 +2226,98 @@
1073 2226 'post_type' => $params['post_type'],
1074 2227 'posts_per_page' => $params['posts_per_page'],
1075 2228 'order' => $params['order']
1076 2229 );
1077 -
1078 - if($params['exc_length']){
1079 - $exc_length = (int) $params['exc_length'];
1080 - add_filter( 'excerpt_length', function($length) use($exc_length){
1081 - return $exc_length;
1082 - }, 999 );
2230 +
2231 + if(!empty($params['paged'])){
2232 + $args['paged'] = $params['paged'];
1083 2233 }
1084 2234
1085 - if($params['term']){
2235 + if (!empty($params['term']) || !empty($params['exc_term'])) {
2236 + $tax_query = [];
1086 2237
1087 - $term = explode(':', $params['term']);
1088 - //pagelayer_print($term);
1089 - if($term[0] == 'category'){
1090 - $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 + }
1091 2264 }
1092 - if($term[0] == 'post_tag'){
1093 - $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 + }
1094 2295 }
1095 - }
1096 2296
1097 - if($params['exc_term']){
1098 -
1099 - $term = explode(':', $params['exc_term']);
1100 - //pagelayer_print($term);
1101 - if($term[0] == 'category'){
1102 - $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);
1103 2300 }
1104 - if($term[0] == 'post_tag'){
1105 - $args['tag__not_in'] = $term[2];
1106 - }
1107 2301 }
1108 2302
1109 - if($params['author_name']){
2303 + if(!empty($params['author_name'])){
1110 2304 $author_name = explode(':', $params['author_name']);
1111 2305 $args['author_name'] = $author_name[0];
1112 2306 }
1113 2307
1114 - if($params['exc_author']){
2308 + if(!empty($params['exc_author'])){
1115 2309 $exc_author = explode(':', $params['exc_author']);
1116 2310 $args['author'] = '-'.$exc_author[1];
1117 2311 }
1118 2312
1119 - if(!empty($params['offset'])){
1120 - $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 + }
1121 2320 }
1122 2321
1123 2322 if(!empty($params['ignore_sticky'])){
1124 2323 $args['ignore_sticky_posts'] = $params['ignore_sticky'];
@@ -1127,9 +2326,9 @@
1127 2326 if(!empty($params['orderby'])){
1128 2327 $args['orderby'] = $params['orderby'];
1129 2328 }
1130 2329
1131 - if($params['by_period']){
2330 + if(!empty($params['by_period'])){
1132 2331 $date_arg = array();
1133 2332 switch($params['by_period']){
1134 2333 case 'last_day':
1135 2334 $date_arg['day'] = date('j')-1;
@@ -1154,15 +2353,25 @@
1154 2353 }
1155 2354 }
1156 2355 //pagelayer_print($args);
1157 2356
2357 + // Only see published posts
2358 + $args['post_status'] = 'publish';
2359 +
1158 2360 $postsquery = new WP_Query($args);
1159 2361 $data = '';
1160 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 +
1161 2367 if(!$postsquery->have_posts()){
1162 - return '<h3>Something went wrong, Please give proper inputs!</h3>';
2368 + return '<h3>No posts found!</h3>';
1163 2369 }
1164 2370
2371 + // To reset the post when the $wp_query->post is empty
2372 + $orig_post = $post;
2373 +
1165 2374 while($postsquery->have_posts()) : $postsquery->the_post();
1166 2375 $data .= '<div class="pagelayer-wposts-col">
1167 2376 <div class="pagelayer-wposts-post">
1168 2377 <div class="pagelayer-wposts-featured">';
@@ -1167,10 +2376,10 @@
1167 2376 <div class="pagelayer-wposts-post">
1168 2377 <div class="pagelayer-wposts-featured">';
1169 2378 $data .= '<a href="'. get_the_permalink() .'">';
1170 2379
1171 - if(isset($params['show_thumb']) && has_post_thumbnail( $postsquery->ID )){
1172 - $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>';
1173 2382 }
1174 2383 /* if($params['show_thumb'] && has_post_thumbnail( $postsquery->ID )){
1175 2384 $data .= get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']);
1176 2385 } */
@@ -1176,32 +2385,38 @@
1176 2385 } */
1177 2386 $data .= '</a></div>
1178 2387 <div class="pagelayer-wposts-content">';
1179 2388
1180 - if(isset($params['show_title'])){
2389 + if(!empty($params['show_title'])){
1181 2390 $data .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark"><div class="pagelayer-wposts-title">'. get_the_title().'</div></a>';
1182 2391 }
1183 2392
1184 2393 $data .= '<div class="pagelayer-wposts-meta">';
1185 2394 $sep = '';
1186 - if(isset($params['meta_sep'])){
1187 - $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> ';
1188 2397 }
1189 - if(isset($params['author'])){
2398 + if(!empty($params['author'])){
1190 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;
1191 2400 }
1192 - if(isset($params['date'])){
1193 - $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;
1194 2403 }
1195 - if(isset($params['category'])){
2404 +
2405 + if(!empty($params['category'])){
1196 2406 $category = get_the_category();
1197 2407 $singlecategory = '';
1198 2408 foreach( $category as $cat ){
1199 2409 $singlecategory .= '<a href="' . get_tag_link( $cat->term_id ) . '">'. $cat->name .'</a>';
1200 2410 }
1201 - $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 +
1202 2416 }
1203 - if(isset($params['tags'])){
2417 +
2418 + if(!empty($params['tags'])){
1204 2419 $tags = get_the_tags();
1205 2420 $singletag = '';
1206 2421 if(!empty($tags)){
1207 2422 foreach( $tags as $tag ){
@@ -1212,26 +2427,46 @@
1212 2427 }
1213 2428 }
1214 2429
1215 2430 }
1216 - if(isset($params['comments'])){
1217 - $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;
1218 2434 }
1219 2435
1220 2436 $data .= '</div>';
1221 2437
1222 - if(isset($params['show_content'])){
2438 + if(!empty($params['show_content'])){
1223 2439 $data .= '<div class="pagelayer-wposts-excerpt">';
1224 - if($params['show_content'] == 'excerpt'){
1225 - $data .= do_shortcode(get_the_excerpt());
1226 - }elseif($params['show_content'] == 'full'){
1227 - $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());
1228 2447 }
1229 2448 $data .= '</div>';
1230 2449 }
1231 2450
1232 - if(isset($params['show_more'])){
1233 - $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>';
1234 2469 }
1235 2470
1236 2471 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
1237 2472 $data .= '</div></div></div>';
@@ -1236,8 +2471,15 @@
1236 2471 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
1237 2472 $data .= '</div></div></div>';
1238 2473 endwhile;
1239 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 +
1240 2482 return $data;
1241 2483 }
1242 2484
1243 2485 // Get Post Revision
@@ -1336,9 +2578,9 @@
1336 2578 $data .= '<div class="pagelayer-posts-slider-excerpt">';
1337 2579 if($params['post']['show_excerpt'] == "true"){
1338 2580 if(has_excerpt()){
1339 2581 $excerpt = get_the_excerpt();
1340 - $data .= do_shortcode($excerpt);
2582 + $data .= pagelayer_the_content($excerpt);
1341 2583 }
1342 2584 }
1343 2585 $data .= '</div>';
1344 2586 $data .= '<a class="pagelayer-posts-slider-link" href="'. get_the_permalink().'">Read More</a>';
@@ -1371,8 +2613,12 @@
1371 2613
1372 2614 // Groups
1373 2615 }else{
1374 2616
2617 + if(array_key_exists('hide_drop', $opt_array[$x]) && !empty($opt_array[$x]['hide_drop'])){
2618 + continue;
2619 + }
2620 +
1375 2621 // If Label is there, then its a normal option
1376 2622 if(array_key_exists('label', $opt_array[$x])){
1377 2623 $options .= pagelayer_sel_option($x, $opt_array[$x]['label'], $selected);
1378 2624
@@ -1436,9 +2682,9 @@
1436 2682
1437 2683 function pagelayer_get_post_author(){
1438 2684
1439 2685 $args = [
1440 - 'who' => 'authors',
2686 + 'capability' => array( 'edit_posts' ),
1441 2687 'fields' => [
1442 2688 'ID',
1443 2689 'display_name',
1444 2690 'user_nicename',
@@ -1443,8 +2689,14 @@
1443 2689 'display_name',
1444 2690 'user_nicename',
1445 2691 ]
1446 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 + }
1447 2699
1448 2700 $authors = new \WP_User_Query( $args );
1449 2701
1450 2702 $ret = array();
@@ -1520,10 +2772,67 @@
1520 2772
1521 2773 return $query_args;
1522 2774 }
1523 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 +
1524 2831 function pagelayer_export_content($content){
1525 -
2832 +
2833 + global $pagelayer;
2834 +
1526 2835 // Just call do_shortcode so we can get list of media files to export
1527 2836 //do_shortcode($content);
1528 2837
1529 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
@@ -1529,8 +2838,46 @@
1529 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
1530 2839
1531 2840 $content = preg_replace('/http(s?):\/\/'.preg_quote($theme_url, '/').'/is', '{{theme_url}}', $content);
1532 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 +
1533 2880 // Apply a filter
1534 2881 $content = apply_filters('pagelayer_export_content', $content);
1535 2882
1536 2883 return $content;
@@ -1536,22 +2883,262 @@
1536 2883 return $content;
1537 2884
1538 2885 }
1539 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 +
1540 3106 // Insert a post which is a Pagelayer Post
1541 -function pagelayer_insert_content($post, &$ret){
3107 +function pagelayer_sanitize_content($content){
1542 3108
3109 + if(empty($content)){
3110 + return $content;
3111 + }
3112 +
1543 3113 // Replace Vars
1544 3114 $template_vars = pagelayer_template_vars();
3115 +
1545 3116 foreach($template_vars as $key => $val){
1546 - $post['post_content'] = str_replace($key, $val, $post['post_content']);
3117 + $content = str_replace($key, $val, $content);
1547 3118 }
1548 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']);
1549 3134 //pagelayer_print($post);die();
1550 3135
1551 3136 // Add slashes for safe insert
1552 3137 $post['post_content'] = wp_slash($post['post_content']);
1553 3138
3139 + $post = apply_filters('pagelayer_pre_insert_content', $post);
3140 +
1554 3141 // Now insert / update the post
1555 3142 $ret = wp_insert_post($post);
1556 3143
1557 3144 // Did we save the post ?
@@ -1580,76 +3167,1288 @@
1580 3167 return $stored_icons;
1581 3168
1582 3169 }
1583 3170
1584 -// Install the Pro version
1585 -function pagelayer_install_pro(){
3171 +// Prevent pro activate text for installer
3172 +function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
1586 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(){
1587 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){
1588 3618
1589 - // Include the necessary stuff
1590 - include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
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 +}
1591 3640
1592 - // Includes necessary for Plugin_Upgrader and Plugin_Installer_Skin
1593 - include_once( ABSPATH . 'wp-admin/includes/file.php' );
1594 - include_once( ABSPATH . 'wp-admin/includes/misc.php' );
1595 - include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
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 +}
1596 3655
1597 - // Filter to prevent the activate text
1598 - add_filter('install_plugin_complete_actions', 'pagelayer_install_plugin_complete_actions', 10, 3);
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 +}
1599 3669
1600 - $upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( ) );
1601 - $installed = $upgrader->install(PAGELAYER_API.'download.php?version=latest&license='.$pagelayer->license['license']);
3670 +// Get checkboxes of post categories.
3671 +function pagelayer_post_cats($post, &$cat_name = ''){
1602 3672
1603 - if ( !is_wp_error( $installed ) && $installed ) {
1604 - echo 'Activating Pagelayer Pro !';
1605 - $activate = activate_plugin(PAGELAYER_PRO_BASE);
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>';
1606 3690
1607 - if ( is_null($activate) ) {
1608 - echo '<div id="message" class="updated"><p>'. __('Done! Pagelayer Pro is now installed and activated.', 'pagelayer'). '</p></div><br />';
1609 - echo '<br><br><b>Done! Pagelayer Pro is now installed and activated.</b>';
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;
1610 3739 }
3740 + $users = get_users( array( 'role__in' => $roles__in ) );
1611 3741 }
1612 3742
1613 - return $installed;
3743 + $user_array = [];
1614 3744
3745 + foreach ( $users as $user ) {
3746 + $user_array[$user->ID] = $user->display_name;
3747 + }
3748 +
3749 + return $user_array;
1615 3750 }
1616 3751
1617 -// Prevent pro activate text for installer
1618 -function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
3752 +function pagelayer_parent_post_prop($post){
3753 + global $wp_post_types;
1619 3754
1620 - if($plugin_file == PAGELAYER_PRO_BASE){
1621 - return array();
3755 + if(!empty($wp_post_types[$post->post_type]->hierarchical)){
3756 + return pagelayer_list_parents(0, 0, $post, []);
3757 + }else{
3758 + return [];
1622 3759 }
3760 +}
3761 +
3762 +function pagelayer_list_parents( $parent = 0, $level = 0, $post = null, $values=array('')){
1623 3763
1624 - return $install_actions;
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;
1625 3795 }
1626 3796
1627 -// Load license data
1628 -function pagelayer_load_license(){
3797 +// Maybe explode
3798 +function pagelayer_maybe_explode( $separator = ',', $string = '', $limit = PHP_INT_MAX ){
1629 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){
1630 3957 global $pagelayer;
1631 3958
1632 - // Load license
1633 - $pagelayer->license = get_option('pagelayer_license');
3959 + // To load customizer params array
3960 + if(empty($pagelayer->customizer_params)){
3961 + pagelayer_load_shortcodes();
3962 + }
1634 3963
1635 - // Update license details as well
1636 - if(!empty($pagelayer->license) && (time() - @$pagelayer->license['last_update']) >= 86400){
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){
1637 4003
1638 - $resp = wp_remote_get(PAGELAYER_API.'license.php?license='.$pagelayer->license['license']);
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){
1639 4088
1640 - // Did we get a response ?
1641 - if(is_array($resp)){
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){
1642 4100
1643 - $tosave = json_decode($resp['body'], true);
4101 + $screen = (!empty($sv) ? '_'.$sv : '');
4102 + $screen_key = $cv['option'].$screen;
4103 + $screen_ck = $ck.$screen;
1644 4104
1645 - // Is it the license ?
1646 - if(!empty($tosave['license'])){
1647 - $tosave['last_update'] = time();
1648 - update_option('pagelayer_license', $tosave);
4105 + // Get option value
4106 + $c_option = get_option($screen_key);
4107 +
4108 + if(empty($c_option)){
4109 + continue;
1649 4110 }
1650 4111
4112 + $options[$screen_ck] = $c_option;
1651 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.')';
1652 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']);
1653 4269 }
1654 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;
1655 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;
4454 +}