--- a/siteorigin-panels/compat/acf-widgets.php
+++ b/siteorigin-panels/compat/acf-widgets.php
@@ -42,6 +42,7 @@
*/
public function store_acf_widget_fields_instance( $the_widget, $instance ) {
if ( ! empty( $instance['acf'] ) ) {
+ $fields = array();
$field_groups = acf_get_field_groups( array(
'widget' => $the_widget->id_base,
) );
@@ -70,15 +71,15 @@
$fields = acf_get_store( 'so_fields' );
$instance = acf_get_store( 'so_widget_instance' );
- if ( ! empty( $fields ) ) {
+ if ( ! empty( $fields ) && ! empty( $instance ) ) {
foreach ( $fields->data as $field ) {
if (
- $widget_field['type'] != 'repeater' ||
+ $widget_field['type'] != 'repeater' &&
$widget_field['type'] != 'checkbox'
) {
if (
$field['key'] == $widget_field['key'] &&
- ! empty( $instance->data[ $field['key'] ] )
+ array_key_exists( $field['key'], $instance->data )
) {
return $instance->data[ $field['key'] ];
}
@@ -87,6 +88,8 @@
}
}
}
+
+ return $value;
}
/**
@@ -112,7 +115,7 @@
}
}
- if ( $fields != '' ) {
+ if ( $fields !== '' && $fields !== null ) {
return $fields;
}
}
--- a/siteorigin-panels/compat/events-manager.php
+++ b/siteorigin-panels/compat/events-manager.php
@@ -7,52 +7,117 @@
return;
}
-$em_pb_removed = false;
+class SiteOrigin_Panels_Compat_Events_Manager {
+ private $is_pb_removed = false;
+ private $is_duplicating = false;
-/**
- * Disable Page Builder for Events Manager post types.
- *
- * This function checks if the current post is an Events Manager post type
- * and if Page Builder is enabled for it. If both conditions are met, it
- * disables Page Builder for the content. This is done to prevent Page Builder
- * from interfering with the Events Manager content, and vice versa.
- *
- * `loop_start` is used due to when the Events Manager plugin sets up its
- * content replacement.
- *
- * @return void
- */
-function siteorigin_panels_event_manager_loop_start() {
- $em_post_types = array( 'event-recurring', 'event' );
-
- // Is the current post an $em_post_types post?
- $post_type = get_post_type();
- if ( ! in_array( $post_type, $em_post_types ) ) {
- return;
- }
-
- // Is Page Builder enabled for Events Manager post types?
- $pb_post_types = siteorigin_panels_setting( 'post-types' );
- if ( empty( $pb_post_types ) || ! array_intersect( $em_post_types, $pb_post_types ) ) {
- return;
+ public static function single() {
+ static $single;
+
+ return empty( $single ) ? $single = new self() : $single;
+ }
+
+ public function __construct() {
+ add_action( 'loop_start', array( $this, 'loop_start' ) );
+ add_action( 'loop_end', array( $this, 'loop_end' ) );
+
+ add_action( 'em_event_duplicate_pre', array( $this, 'duplicate_pre' ) );
+ add_filter( 'em_event_get_event_meta', array( $this, 'filter_duplicate_meta' ) );
+ add_filter( 'em_event_duplicate', array( $this, 'duplicate_copy_panels_data' ), 10, 2 );
}
- global $em_pb_removed;
- $em_pb_removed = true;
+ /**
+ * Disable Page Builder for Events Manager post types.
+ *
+ * @return void
+ */
+ public function loop_start() {
+ $em_post_types = array( 'event-recurring', 'event' );
+
+ $post_type = get_post_type();
+ if ( ! in_array( $post_type, $em_post_types ) ) {
+ return;
+ }
+
+ $pb_post_types = siteorigin_panels_setting( 'post-types' );
+ if ( empty( $pb_post_types ) || ! array_intersect( $em_post_types, $pb_post_types ) ) {
+ return;
+ }
- add_filter( 'siteorigin_panels_filter_content_enabled', '__return_false' );
-}
-add_action( 'loop_start', 'siteorigin_panels_event_manager_loop_start' );
+ $this->is_pb_removed = true;
+ add_filter( 'siteorigin_panels_filter_content_enabled', '__return_false' );
+ }
-/**
- * Re-enable Page Builder for `the_content` filter if it
- * was disabled at the start of the loop.
- */
-function siteorigin_panels_event_manager_loop_end() {
- global $em_pb_removed;
+ /**
+ * Re-enable Page Builder for `the_content` filter if it
+ * was disabled at the start of the loop.
+ *
+ * @return void
+ */
+ public function loop_end() {
+ if ( $this->is_pb_removed ) {
+ remove_filter( 'siteorigin_panels_filter_content_enabled', '__return_false' );
+ $this->is_pb_removed = false;
+ }
+ }
- if ( $em_pb_removed ) {
- remove_filter( 'siteorigin_panels_filter_content_enabled', '__return_false' );
+ /**
+ * Flag Events Manager duplication so we can avoid unsafe SQL inserts for panels_data.
+ *
+ * @return void
+ */
+ public function duplicate_pre() {
+ $this->is_duplicating = true;
+ }
+
+ /**
+ * Remove Page Builder data from Events Manager's raw SQL duplication payload.
+ *
+ * @param array $event_meta Event post meta.
+ *
+ * @return array
+ */
+ public function filter_duplicate_meta( $event_meta ) {
+ if ( ! $this->is_duplicating || ! is_array( $event_meta ) ) {
+ return $event_meta;
+ }
+
+ unset( $event_meta['panels_data'] );
+
+ return $event_meta;
+ }
+
+ /**
+ * Copy Page Builder data to the duplicated event using safe WordPress APIs.
+ *
+ * @param mixed $duplicated_event The duplicated event object, or false on failure.
+ * @param mixed $source_event The original source event object.
+ *
+ * @return mixed
+ */
+ public function duplicate_copy_panels_data( $duplicated_event, $source_event ) {
+ $this->is_duplicating = false;
+
+ if (
+ empty( $duplicated_event ) ||
+ ! is_object( $duplicated_event ) ||
+ ! is_object( $source_event ) ||
+ empty( $duplicated_event->post_id ) ||
+ empty( $source_event->post_id )
+ ) {
+ return $duplicated_event;
+ }
+
+ $source_panels_data = get_post_meta( (int) $source_event->post_id, 'panels_data', true );
+ if ( empty( $source_panels_data ) ) {
+ return $duplicated_event;
+ }
+
+ $source_panels_data = map_deep( $source_panels_data, array( 'SiteOrigin_Panels_Admin', 'double_slash_string' ) );
+ update_post_meta( (int) $duplicated_event->post_id, 'panels_data', $source_panels_data );
+
+ return $duplicated_event;
}
}
-add_action( 'loop_end', 'siteorigin_panels_event_manager_loop_end' );
+
+SiteOrigin_Panels_Compat_Events_Manager::single();
--- a/siteorigin-panels/compat/layout-block.php
+++ b/siteorigin-panels/compat/layout-block.php
@@ -42,7 +42,26 @@
}
public function enqueue_layout_block_editor_assets() {
- if ( SiteOrigin_Panels_Admin::is_block_editor() || is_customize_preview() ) {
+ $is_block_editor = SiteOrigin_Panels_Admin::is_block_editor();
+
+ if ( $is_block_editor || is_customize_preview() ) {
+ if ( $is_block_editor && function_exists( 'aioseo' ) ) {
+ $aioseo = aioseo();
+ if (
+ is_object( $aioseo ) &&
+ isset( $aioseo->standalone ) &&
+ is_object( $aioseo->standalone ) &&
+ isset( $aioseo->standalone->pageBuilderIntegrations ) &&
+ is_array( $aioseo->standalone->pageBuilderIntegrations ) &&
+ isset( $aioseo->standalone->pageBuilderIntegrations['siteorigin'] ) &&
+ is_object( $aioseo->standalone->pageBuilderIntegrations['siteorigin'] )
+ ) {
+ remove_action(
+ 'siteorigin_panel_enqueue_admin_scripts',
+ array( $aioseo->standalone->pageBuilderIntegrations['siteorigin'], 'enqueue' )
+ );
+ }
+ }
$panels_admin = SiteOrigin_Panels_Admin::single();
$panels_admin->enqueue_admin_scripts();
$panels_admin->enqueue_admin_styles();
--- a/siteorigin-panels/inc/installer/inc/admin.php
+++ b/siteorigin-panels/inc/installer/inc/admin.php
@@ -155,6 +155,32 @@
die();
}
+ $task = sanitize_key( $_POST['task'] );
+ $type = sanitize_key( $_POST['type'] );
+
+ $capability = '';
+ if ( $type === 'plugins' ) {
+ if ( $task === 'install' ) {
+ $capability = 'install_plugins';
+ } elseif ( $task === 'update' ) {
+ $capability = 'update_plugins';
+ } elseif ( $task === 'activate' ) {
+ $capability = 'activate_plugins';
+ }
+ } elseif ( $type === 'themes' ) {
+ if ( $task === 'install' ) {
+ $capability = 'install_themes';
+ } elseif ( $task === 'update' ) {
+ $capability = 'update_themes';
+ } elseif ( $task === 'activate' ) {
+ $capability = 'switch_themes';
+ }
+ }
+
+ if ( empty( $capability ) || ! current_user_can( $capability ) ) {
+ die();
+ }
+
// SO Premium won't have a version.
if (
empty( $_POST['version'] ) &&
@@ -165,14 +191,14 @@
$slug = sanitize_file_name( $_POST['slug'] );
- $product_url = 'https://wordpress.org/' . urlencode( $_POST['type'] ) . '/download/' . urlencode( $slug ) . '.' . urlencode( $_POST['version'] ) . '.zip';
+ $product_url = 'https://wordpress.org/' . urlencode( $type ) . '/download/' . urlencode( $slug ) . '.' . urlencode( $_POST['version'] ) . '.zip';
// check_ajax_referer( 'so_installer_manage' );
if ( ! class_exists( 'WP_Upgrader' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
}
$upgrader = new WP_Upgrader();
- if ( $_POST['type'] == 'plugins' ) {
- if ( $_POST['task'] == 'install' || $_POST['task'] == 'update' ) {
+ if ( $type === 'plugins' ) {
+ if ( $task === 'install' || $task === 'update' ) {
$upgrader->run( array(
'package' => esc_url( $product_url ),
'destination' => WP_PLUGIN_DIR,
@@ -186,14 +212,14 @@
$clear = true;
} elseif (
- $_POST['task'] == 'activate' &&
+ $task === 'activate' &&
! is_wp_error( validate_plugin( $slug . '/' . $slug . '.php' ) )
) {
activate_plugin( $slug . '/' . $slug . '.php' );
$clear = true;
}
- } elseif ( $_POST['type'] == 'themes' ) {
- if ( $_POST['task'] == 'install' || $_POST['task'] == 'update' ) {
+ } elseif ( $type === 'themes' ) {
+ if ( $task === 'install' || $task === 'update' ) {
$upgrader->run( array(
'package' => esc_url( $product_url ),
'destination' => get_theme_root(),
@@ -202,7 +228,7 @@
'abort_if_destination_exists' => false,
) );
$clear = true;
- } elseif ( $_POST['task'] == 'activate' ) {
+ } elseif ( $task === 'activate' ) {
switch_theme( $slug );
$clear = true;
}
--- a/siteorigin-panels/inc/renderer-legacy.php
+++ b/siteorigin-panels/inc/renderer-legacy.php
@@ -111,10 +111,14 @@
'padding' => 0,
), $panels_mobile_width );
- // Hide empty cells on mobile
- $css->add_row_css( $post_id, false, ' .panel-grid-cell-empty', array(
- 'display' => 'none',
- ), $panels_mobile_width );
+ // Hide empty columns on mobile unless "Display Empty Columns With Background" is enabled.
+ if ( ! siteorigin_panels_setting( 'display-empty-rows-with-background' ) ) {
+ foreach ( $layout_data as $ri => $row ) {
+ $css->add_row_css( $post_id, $ri, ' .panel-grid-cell-empty', array(
+ 'display' => 'none',
+ ), $panels_mobile_width );
+ }
+ }
// Hide empty cells on mobile
$css->add_row_css( $post_id, false, ' .panel-grid-cell-mobile-last', array(
--- a/siteorigin-panels/inc/renderer.php
+++ b/siteorigin-panels/inc/renderer.php
@@ -17,6 +17,61 @@
}
/**
+ * Determine whether the current row contains no widgets.
+ *
+ * @param array $row The row data.
+ *
+ * @return bool
+ */
+ private function is_empty_row( $row ) {
+ if ( empty( $row['cells'] ) || ! is_array( $row['cells'] ) ) {
+ return true;
+ }
+
+ foreach ( $row['cells'] as $cell ) {
+ if ( ! empty( $cell['widgets'] ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine whether the current row has a background style set.
+ *
+ * @param array $row The row data.
+ *
+ * @return bool
+ */
+ private function row_has_background( $row ) {
+ if ( empty( $row['style'] ) || ! is_array( $row['style'] ) ) {
+ return false;
+ }
+
+ return (
+ ! empty( $row['style']['background'] ) ||
+ ! empty( $row['style']['background_image_attachment'] ) ||
+ ! empty( $row['style']['background_image_attachment_fallback'] )
+ );
+ }
+
+ /**
+ * Determine whether an empty row with a background should remain visible.
+ *
+ * @param array $row The row data.
+ *
+ * @return bool
+ */
+ private function should_display_empty_background_row( $row ) {
+ return (
+ siteorigin_panels_setting( 'display-empty-rows-with-background' ) &&
+ $this->is_empty_row( $row ) &&
+ $this->row_has_background( $row )
+ );
+ }
+
+ /**
* Add CSS that needs to go inline.
*/
public function add_inline_css( $post_id, $css ) {
@@ -91,6 +146,7 @@
// Filter the bottom margin for this row with the arguments
$panels_margin_bottom = apply_filters( 'siteorigin_panels_css_row_margin_bottom', $settings['margin-bottom'] . 'px', $row, $ri, $panels_data, $post_id );
$panels_mobile_margin_bottom = apply_filters( 'siteorigin_panels_css_row_mobile_margin_bottom', $settings['row-mobile-margin-bottom'] . 'px', $row, $ri, $panels_data, $post_id );
+ $display_empty_background_row = $this->should_display_empty_background_row( $row );
if ( SiteOrigin_Panels_Styles::single()->has_overlay( $row ) ) {
$css->add_row_css( $post_id, $ri, array(
@@ -420,10 +476,14 @@
'padding' => 0,
), $panels_mobile_width );
- // Hide empty cells on mobile
- $css->add_row_css( $post_id, false, ' .panel-grid-cell-empty', array(
- 'display' => 'none',
- ), $panels_mobile_width );
+ // Hide empty columns on mobile unless "Display Empty Columns With Background" is enabled.
+ if ( ! siteorigin_panels_setting( 'display-empty-rows-with-background' ) ) {
+ foreach ( $layout_data as $ri => $row ) {
+ $css->add_row_css( $post_id, $ri, ' .panel-grid-cell-empty', array(
+ 'display' => 'none',
+ ), $panels_mobile_width );
+ }
+ }
// Hide empty cells on mobile
$css->add_row_css( $post_id, false, ' .panel-grid-cell-mobile-last', array(
@@ -505,7 +565,7 @@
if ( empty( $post_id ) ) {
$post_id = get_the_ID();
- if ( class_exists( 'WooCommerce' ) && is_shop() ) {
+ if ( SiteOrigin_Panels::should_use_woocommerce_shop_page_id() ) {
$post_id = wc_get_page_id( 'shop' );
}
}
@@ -761,7 +821,7 @@
if ( empty( $post_id ) ) {
$post_id = get_the_ID();
- if ( class_exists( 'WooCommerce' ) && is_shop() ) {
+ if ( SiteOrigin_Panels::should_use_woocommerce_shop_page_id() ) {
$post_id = wc_get_page_id( 'shop' );
}
}
@@ -1081,6 +1141,9 @@
$row_classes = array( 'panel-grid' );
$row_classes[] = ! empty( $row_style_wrapper ) ? 'panel-has-style' : 'panel-no-style';
+ if ( $this->should_display_empty_background_row( $row ) ) {
+ $row_classes[] = 'panel-empty-row-has-background';
+ }
if ( SiteOrigin_Panels_Styles::single()->has_overlay( $row ) ) {
$row_classes[] = 'panel-has-overlay';
--- a/siteorigin-panels/inc/settings.php
+++ b/siteorigin-panels/inc/settings.php
@@ -174,6 +174,7 @@
$defaults['mobile-cell-margin'] = $mobile_cell_margin;
$defaults['widget-mobile-margin-bottom'] = '';
$defaults['margin-bottom-last-row'] = false;
+ $defaults['display-empty-rows-with-background'] = false;
$defaults['margin-sides'] = 30;
$defaults['full-width-container'] = 'body';
$defaults['output-css-header'] = 'auto';
@@ -502,6 +503,12 @@
'description' => __( 'Allow margin below the last row.', 'siteorigin-panels' ),
);
+ $fields['layout']['fields']['display-empty-rows-with-background'] = array(
+ 'type' => 'checkbox',
+ 'label' => __( 'Display Empty Columns With Background', 'siteorigin-panels' ),
+ 'description' => __( 'Display empty columns when a column background color or image is set.', 'siteorigin-panels' ),
+ );
+
$fields['layout']['fields']['mobile-cell-margin'] = array(
'type' => 'number',
'unit' => 'px',
--- a/siteorigin-panels/inc/widgets/post-loop.php
+++ b/siteorigin-panels/inc/widgets/post-loop.php
@@ -560,12 +560,35 @@
* @return bool
*/
public function validate_template_file( $filename ) {
- return validate_file( $filename ) == 0 &&
- substr( $filename, -4 ) == '.php' &&
+ return self::is_valid_template_name( $filename ) &&
self::locate_template( $filename ) != '';
}
/**
+ * Check if a template name is safe to resolve.
+ *
+ * @param mixed $template_name Template to validate.
+ *
+ * @return bool
+ */
+ private static function is_valid_template_name( $template_name ) {
+ if ( ! is_string( $template_name ) ) {
+ return false;
+ }
+
+ $template_name = trim( wp_normalize_path( $template_name ) );
+
+ if ( '' === $template_name || false !== strpos( $template_name, "