Skip to content

Commit 22bb08f

Browse files
committed
Merge branch 'release/2.32.0'
2 parents 6c990ce + 6bcb333 commit 22bb08f

File tree

14 files changed

+183
-73
lines changed

14 files changed

+183
-73
lines changed

changelog.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
== Changelog ==
22

3+
= 2.32.0 – 13 June 2025 =
4+
* Background Image: Added Alt Text setting for improved accessibility.
5+
* Layout Block: Added additional PanelsData check and prevented potential JavaScript null quirk.
6+
* Layout Builder: Fixed potential object.keys issue when removing all blocks.
7+
* Polylang: Prevented undefined sync warning.
8+
* Improved security with enhanced parameter sanitization across admin features.
9+
* Code Quality: Updated WordPress PHP Coding Standards compliance.
10+
311
= 2.31.8 – 12 May 2025 =
412
* Border Styles: Improved handling of multiple border values.
513
* Multi-select: Fixed type error in field handling.

compat/js/siteorigin-panels-layout-block.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function (_wp$element$Component) {
8181
key: "initializeState",
8282
value: function initializeState(props) {
8383
var newState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
84-
var hasPanelsData = _typeof(props.panelsData) === 'object' && Object.keys(props.panelsData).length > 0;
84+
var hasPanelsData = props.panelsData && _typeof(props.panelsData) === 'object' && Object.keys(props.panelsData).length > 0;
8585
var isDefaultModeEdit = window.soPanelsBlockEditorAdmin.defaultMode === 'edit';
8686
var editMode = hasPanelsData === true ? isDefaultModeEdit : true;
8787
this.initialState = {
@@ -238,8 +238,13 @@ function (_wp$element$Component) {
238238
return true;
239239
}
240240

241-
if (!newPanelsData || !oldPanelsData || _typeof(newPanelsData) !== 'object' && _typeof(oldPanelsData) !== 'object') {
241+
if (!newPanelsData || !oldPanelsData) {
242242
return newPanelsData === oldPanelsData;
243+
} // If neither newPanelsData nor oldPanelsData are objects, assume they're not the same.
244+
245+
246+
if (_typeof(newPanelsData) !== 'object' || _typeof(oldPanelsData) !== 'object') {
247+
return false;
243248
}
244249

245250
var keys = Object.keys(newPanelsData);
@@ -412,7 +417,7 @@ wp.blocks.registerBlockType('siteorigin-panels/layout-block', {
412417
toggleSelection = _ref.toggleSelection;
413418

414419
var onLayoutBlockContentChange = function onLayoutBlockContentChange(newPanelsData) {
415-
if (_typeof(newPanelsData.widgets) === 'object' && Object.keys(newPanelsData.widgets).length > 0) {
420+
if (newPanelsData.widgets !== null && _typeof(newPanelsData.widgets) === 'object' && Object.keys(newPanelsData.widgets).length > 0) {
416421
// Send panelsData to server for sanitization.
417422
var isNewWPBlockEditor = jQuery('.widgets-php').length;
418423

compat/js/siteorigin-panels-layout-block.jsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ class SiteOriginPanelsLayoutBlock extends wp.element.Component {
1111
}
1212

1313
initializeState(props, newState = true) {
14-
const hasPanelsData = typeof props.panelsData === 'object' && Object.keys( props.panelsData ).length > 0;
14+
const hasPanelsData = props.panelsData &&
15+
typeof props.panelsData === 'object' &&
16+
Object.keys( props.panelsData ).length > 0;
17+
1518
const isDefaultModeEdit = window.soPanelsBlockEditorAdmin.defaultMode === 'edit';
1619
const editMode = hasPanelsData === true ? isDefaultModeEdit : true;
1720

@@ -169,15 +172,16 @@ class SiteOriginPanelsLayoutBlock extends wp.element.Component {
169172
return true;
170173
}
171174

175+
if ( ! newPanelsData || ! oldPanelsData ) {
176+
return newPanelsData === oldPanelsData;
177+
}
178+
179+
// If neither newPanelsData nor oldPanelsData are objects, assume they're not the same.
172180
if (
173-
! newPanelsData ||
174-
! oldPanelsData ||
175-
(
176-
typeof newPanelsData !== 'object' &&
177-
typeof oldPanelsData !== 'object'
178-
)
181+
typeof( newPanelsData ) !== 'object' ||
182+
typeof( oldPanelsData ) !== 'object'
179183
) {
180-
return newPanelsData === oldPanelsData;
184+
return false;
181185
}
182186

183187
var keys = Object.keys( newPanelsData );
@@ -370,8 +374,9 @@ wp.blocks.registerBlockType( 'siteorigin-panels/layout-block', {
370374
let onLayoutBlockContentChange = ( newPanelsData ) => {
371375

372376
if (
373-
typeof newPanelsData.widgets === 'object' &&
374-
Object.keys( newPanelsData.widgets ).length > 0
377+
newPanelsData.widgets !== null &&
378+
typeof newPanelsData.widgets === 'object' &&
379+
Object.keys( newPanelsData.widgets ).length > 0
375380
) {
376381
// Send panelsData to server for sanitization.
377382
var isNewWPBlockEditor = jQuery( '.widgets-php' ).length;

compat/polylang.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ private function get_pll_language_cache( $post_id ) {
4242
}
4343

4444
$language_data = maybe_unserialize( $terms[0]->description );
45-
$sync_data = $language_data['sync'];
4645

4746
// If there's no sync data, there's nothing to sync.
4847
if ( empty( $language_data['sync'] ) || ! is_array( $language_data['sync'] ) ) {
4948
return $this->pll_language_cache[ $post_id ] = array();
5049
}
5150

51+
$sync_data = $language_data['sync'];
5252
unset( $language_data['sync'] );
5353

5454
return $this->pll_language_cache[ $post_id ] = array(

compat/seopress.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
function siteorigin_panels_seopress_compat( $content ) {
3-
$id = empty( $_GET['post'] ) ? $_GET['post_id'] : $_GET['post'];
3+
$id = empty( $_GET['post'] ) ? (int) $_GET['post_id'] : (int) $_GET['post'];
44
if ( ! empty( $id ) ) {
55
$page_builder_data = get_post_meta( $id, 'panels_data', true );
66
if ( ! empty( $page_builder_data ) ) {

css/front-flex.less

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,12 @@ body.siteorigin-panels-before-js:not(.siteorigin-panels-css-container) {
105105
padding-left: 1000px !important;
106106
}
107107
}
108+
109+
.so-sr-only {
110+
height :1px;
111+
left: -10000px;
112+
overflow: hidden;
113+
position: absolute;
114+
top: auto;
115+
width: 1px;
116+
}

inc/admin-layouts.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,9 @@ public function action_get_prebuilt_layouts() {
234234
// Get any layouts that the current user could edit.
235235
header( 'content-type: application/json' );
236236

237-
$type = ! empty( $_REQUEST['type'] ) ? $_REQUEST['type'] : 'directory-siteorigin';
238-
$search = ! empty( $_REQUEST['search'] ) ? trim( strtolower( $_REQUEST['search'] ) ) : '';
239-
$page_num = ! empty( $_REQUEST['page'] ) ? (int) $_REQUEST['page'] : 1;
237+
$type = ! empty( $_REQUEST['type'] ) ? sanitize_key( $_REQUEST['type'] ) : 'directory-siteorigin';
238+
$search = ! empty( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : '';
239+
$page_num = ! empty( $_REQUEST['page'] ) ? intval( $_REQUEST['page'] ) : 1;
240240

241241
$return = array(
242242
'title' => '',
@@ -426,11 +426,14 @@ public function decode_panels_data( $data, $file = null ) {
426426
* Ajax handler to get an individual prebuilt layout
427427
*/
428428
public function action_get_prebuilt_layout() {
429-
if ( empty( $_REQUEST['type'] ) ) {
429+
$type = isset( $_REQUEST['type'] ) ? sanitize_key( $_REQUEST['type'] ) : '';
430+
$layout_id = isset( $_REQUEST['lid'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['lid'] ) ) : '';
431+
432+
if ( empty( $type ) ) {
430433
wp_die();
431434
}
432435

433-
if ( ! isset( $_REQUEST['lid'] ) ) {
436+
if ( empty( $layout_id ) ) {
434437
wp_die();
435438
}
436439

@@ -442,20 +445,20 @@ public function action_get_prebuilt_layout() {
442445
$panels_data = array();
443446
$raw_panels_data = false;
444447

445-
if ( $_REQUEST['type'] == 'prebuilt' ) {
448+
if ( $type == 'prebuilt' ) {
446449
$layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() );
447450

448451
if (
449-
! is_numeric( $_REQUEST['lid'] ) &&
450-
empty( $layouts[ $_REQUEST['lid'] ] )
452+
! is_numeric( $layout_id ) &&
453+
empty( $layouts[ $layout_id ] )
451454
) {
452455
wp_send_json_error( array(
453456
'error' => true,
454457
'message' => __( 'Missing layout ID or no such layout exists', 'siteorigin-panels' ),
455458
) );
456459
}
457460

458-
$layout = $layouts[ $_REQUEST['lid'] ];
461+
$layout = $layouts[ $layout_id ];
459462

460463
// Fix the format of this layout
461464
if ( ! empty( $layout[ 'filename' ] ) ) {
@@ -470,7 +473,7 @@ public function action_get_prebuilt_layout() {
470473
}
471474

472475
// A theme or plugin could use this to change the data in the layout
473-
$panels_data = apply_filters( 'siteorigin_panels_prebuilt_layout', $layout, $_REQUEST['lid'] );
476+
$panels_data = apply_filters( 'siteorigin_panels_prebuilt_layout', $layout, $layout_id );
474477

475478
// Remove all the layout specific attributes
476479
if ( isset( $panels_data['name'] ) ) {
@@ -486,14 +489,14 @@ public function action_get_prebuilt_layout() {
486489
}
487490

488491
$raw_panels_data = true;
489-
} elseif ( substr( $_REQUEST['type'], 0, 10 ) == 'directory-' ) {
490-
$directory_id = str_replace( 'directory-', '', $_REQUEST['type'] );
492+
} elseif ( substr( $type, 0, 10 ) == 'directory-' ) {
493+
$directory_id = str_replace( 'directory-', '', $type );
491494

492495
$directories = $this->get_directories();
493496
$directory = ! empty( $directories[ $directory_id ] ) ? $directories[ $directory_id ] : false;
494497

495498
if ( ! empty( $directory ) ) {
496-
$url = $directory[ 'url' ] . 'layout/' . urlencode( $_REQUEST[ 'lid' ] ) . '/?action=download';
499+
$url = $directory[ 'url' ] . 'layout/' . urlencode( $layout_id ) . '/?action=download';
497500

498501
if ( ! empty( $directory[ 'args' ] ) && is_array( $directory[ 'args' ] ) ) {
499502
$url = add_query_arg( $directory[ 'args' ], $url );
@@ -512,8 +515,8 @@ public function action_get_prebuilt_layout() {
512515
}
513516
}
514517
$raw_panels_data = true;
515-
} elseif ( current_user_can( 'edit_post', $_REQUEST['lid'] ) ) {
516-
$panels_data = get_post_meta( $_REQUEST['lid'], 'panels_data', true );
518+
} elseif ( current_user_can( 'edit_post', $layout_id ) ) {
519+
$panels_data = get_post_meta( $layout_id, 'panels_data', true );
517520

518521
// Clear id and timestamp for SO widgets to prevent 'newer content version' notification in widget forms.
519522
foreach ( $panels_data['widgets'] as &$widget ) {

inc/admin-widgets-bundle.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ public function render_page() {
3535
if ( isset( $_GET[ sanitize_key( 'plugin' ) ] ) && ( isset( $_GET[ sanitize_key( 'siteorigin-pa-install' ) ] ) && 'install-plugin' == $_GET[ sanitize_key( 'siteorigin-pa-install' ) ] ) && current_user_can( 'install_plugins' ) ) {
3636
check_admin_referer( 'siteorigin-pa-install' );
3737

38-
$plugin['name'] = $_GET['plugin_name']; // Plugin name
39-
$plugin['slug'] = $_GET['plugin']; // Plugin slug
38+
$plugin['name'] = sanitize_text_field( wp_unslash( $_GET['plugin_name'] ) ); // Plugin name
39+
$plugin['slug'] = sanitize_text_field( wp_unslash( $_GET['plugin'] ) ); // Plugin slug
4040

4141
if ( ! empty( $_GET['plugin_source'] ) ) {
42-
$plugin['source'] = $_GET['plugin_source'];
42+
$plugin['source'] = esc_url_raw( $_GET['plugin_source'] );
4343
} else {
4444
$plugin['source'] = false;
4545
}

inc/styles-admin.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function action_style_form() {
3030
);
3131
}
3232

33-
$type = $_REQUEST['type'];
33+
$type = isset( $_REQUEST['type'] ) ? sanitize_key( $_REQUEST['type'] ) : '';
3434

3535
if ( ! in_array( $type, array( 'row', 'cell', 'widget' ) ) ) {
3636
wp_die(
@@ -40,12 +40,12 @@ public function action_style_form() {
4040
);
4141
}
4242

43-
$post_id = empty( $_REQUEST['postId'] ) ? 0 : $_REQUEST['postId'];
43+
$post_id = empty( $_REQUEST['postId'] ) ? 0 : (int) $_REQUEST['postId'];
4444
$args = ! empty( $_POST['args'] ) ? json_decode( stripslashes( $_POST['args'] ), true ) : array();
4545

4646
$current = apply_filters(
4747
'siteorigin_panels_general_current_styles',
48-
isset( $_REQUEST['style'] ) ? $_REQUEST['style'] : array(),
48+
isset( $_REQUEST['style'] ) ? wp_unslash( $_REQUEST['style'] ) : array(),
4949
$post_id,
5050
$type,
5151
$args

inc/styles.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public function __construct() {
5656
add_filter( 'siteorigin_panels_inside_cell_before', array( $this, 'add_parallax' ), 10, 2 );
5757
add_filter( 'siteorigin_panels_inside_widget_before', array( $this, 'add_parallax' ), 10, 2 );
5858
}
59+
60+
// Background Alt Text.
61+
add_action( 'siteorigin_panels_inside_row_before', array( $this, 'maybe_add_background_alt' ), 10, 2 );
62+
add_action( 'siteorigin_panels_inside_cell_before', array( $this, 'maybe_add_background_alt' ), 10, 2 );
63+
add_action( 'siteorigin_panels_inside_widget_before', array( $this, 'maybe_add_background_alt' ), 10, 2 );
5964
}
6065

6166
public static function single() {
@@ -234,6 +239,14 @@ public static function get_general_style_fields( $id, $label ) {
234239
'priority' => 6,
235240
);
236241

242+
$fields['background_image_alt'] = array(
243+
'name' => __( 'Background Image Alt Text', 'siteorigin-panels' ),
244+
'type' => 'text',
245+
'group' => 'design',
246+
'priority' => 7,
247+
'description' => __( 'Leave empty for decorative images.', 'siteorigin-panels' ),
248+
);
249+
237250
$fields['background_display'] = array(
238251
'name' => __( 'Background Image Display', 'siteorigin-panels' ),
239252
'type' => 'select',
@@ -1404,4 +1417,56 @@ public static function get_attachment_image_src( $image, $size = 'full' ) {
14041417
return ! empty( $matches ) ? $matches : false;
14051418
}
14061419
}
1420+
1421+
/**
1422+
* If the current context has a background image and alt text set,
1423+
* add the alt text inside and before the context's HTML.
1424+
*
1425+
* @param string $before The HTML before the context.
1426+
* @param array $context An array containing the the current context's data.
1427+
*
1428+
* @return string The modified HTML with the background alt text added if applicable.
1429+
*
1430+
*/
1431+
public function maybe_add_background_alt( $before, $context ) {
1432+
if ( empty( $context['style'] ) ) {
1433+
return $before;
1434+
}
1435+
1436+
// Does this context have a background image set?
1437+
if (
1438+
empty( $context['style']['background_image_attachment'] ) &&
1439+
empty( $context['style']['background_image_attachment_fallback'] )
1440+
) {
1441+
return $before;
1442+
}
1443+
1444+
// Is alt text set?
1445+
if ( empty( $context['style']['background_image_alt'] ) ) {
1446+
return $before;
1447+
}
1448+
1449+
// Work out the current context type.
1450+
switch( current_filter() ) {
1451+
case 'siteorigin_panels_inside_row_before':
1452+
$context_type = 'row';
1453+
break;
1454+
case 'siteorigin_panels_inside_cell_before':
1455+
$context_type = 'cell';
1456+
break;
1457+
case 'siteorigin_panels_inside_widget_before':
1458+
$context_type = 'widget';
1459+
break;
1460+
default:
1461+
$context_type = '';
1462+
break;
1463+
}
1464+
1465+
// Add the background alt text.
1466+
$before .= "<div class='so-panels-background-alt so-sr-only $context_type'>";
1467+
$before .= esc_html( $context['style']['background_image_alt'] );
1468+
$before .= '</div>';
1469+
1470+
return $before;
1471+
}
14071472
}

0 commit comments

Comments
 (0)