- style="
- $v ) {
- esc_attr_e( $p . ':' . $v . ';' );
+ $style_attr .= $p . ':' . $v . ';';
}
?>
- "
+ style=""
>
diff --git a/inc/widgets/post-loop-helper.php b/inc/widgets/post-loop-helper.php
index 01a092a0..a7c9b3fe 100644
--- a/inc/widgets/post-loop-helper.php
+++ b/inc/widgets/post-loop-helper.php
@@ -47,13 +47,13 @@ public function __construct( $templates ) {
),
'more' => array(
'type' => 'checkbox',
- 'label' => __( 'More link', 'so-widgets-bundle' ),
+ 'label' => __( 'More link', 'siteorigin-panels' ),
'description' => __( 'If the template supports it, cut posts and display the more link.', 'siteorigin-panels' ),
'default' => false,
),
'posts' => array(
'type' => 'posts',
- 'label' => __( 'Posts query', 'so-widgets-bundle' ),
+ 'label' => __( 'Posts query', 'siteorigin-panels' ),
'hide' => true,
),
)
diff --git a/js/seo-compat.js b/js/seo-compat.js
index a4492473..dad6c93b 100644
--- a/js/seo-compat.js
+++ b/js/seo-compat.js
@@ -1,71 +1,9 @@
-/* global jQuery, YoastSEO, _, panelsOptions */
+/* global jQuery, YoastSEO,rankMathEditor, _, panelsOptions */
jQuery( function( $ ) {
-
- var SiteOriginSeoCompat = function() {
-
- if ( typeof YoastSEO !== 'undefined' ) {
- YoastSEO.app.registerPlugin( 'SiteOriginSeoCompat', { status: 'ready' } );
- YoastSEO.app.registerModification( 'content', this.contentModification, 'SiteOriginSeoCompat', 5 );
- }
-
- if ( typeof rankMathEditor !== 'undefined' ) {
- wp.hooks.addFilter( 'rank_math_content', 'SiteOriginSeoCompat', this.rankMath );
- }
-
- };
-
- function isBlockEditorPanelsEnabled() {
- return typeof window.soPanelsBuilderView !== 'undefined' && $( '.block-editor-page' ).length;
- }
-
- function isClassicEditorPanelsEnabled() {
- return $( '#so-panels-panels.attached-to-editor' ).is( ':visible' );
- }
-
- SiteOriginSeoCompat.prototype.rankMath = function( data ) {
- if ( ! data ) {
- return data;
- }
-
- if ( isClassicEditorPanelsEnabled() && ! isBlockEditorPanelsEnabled() ) {
- data = SiteOriginSeoCompat.prototype.contentModification( data );
- return data;
- }
-
- if ( ! isBlockEditorPanelsEnabled() ) {
- return data;
- }
-
- const soBlock = data.match(
- //g
- );
-
- // Replace any found SO Layout blocks with the rendered contents.
- if ( soBlock ) {
- soBlock.forEach( function( block ) {
- data = data.replace( block, SiteOriginSeoCompat.prototype.contentModification( block ) );
- } );
- }
-
- return data;
- }
-
- SiteOriginSeoCompat.prototype.contentModification = function( data ) {
- const isBlockEditor = isBlockEditorPanelsEnabled();
- // Check if the editor has Page Builder Enabled before proceeding.
- if (
- window.soPanelsBuilderView === undefined ||
- (
- ! isClassicEditorPanelsEnabled() &&
- ! isBlockEditor
- )
- ) {
- return;
- }
-
- var whitelist = [
+ const SiteOriginSeoCompat = () => ({
+ allowedTags: [
'p', 'a', 'img', 'caption', 'br',
'blockquote', 'cite',
'em', 'strong', 'i', 'b',
@@ -73,10 +11,113 @@ jQuery( function( $ ) {
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'ul', 'ol', 'li',
'table', 'tr', 'th', 'td'
- ].join( ',' );
+ ].join( ',' ),
+
+ isClassicEditorPanelsEnabled: function() {
+ return $( '#so-panels-panels.attached-to-editor' ).is( ':visible' );
+ },
+
+ isBlockEditorPanelsEnabled: function() {
+ return typeof window.soPanelsBuilderView !== 'undefined' && $( '.block-editor-page' ).length
+ },
+
+ /**
+ * Find SiteOrigin layout blocks in the provided data.
+ *
+ * This function searches the provided data for SiteOrigin
+ * layout blocks using a regular expression.
+ * It matches block with the siteorigin-panels/layout-block name.
+ *
+ * @param {string} data - The data to search for SiteOrigin layout blocks.
+ *
+ * @returns {Array|null} An array of matched SiteOrigin layout blocks, or null if no matches are found.
+ */
+ findSoLayoutBlocks: function( data ) {
+ return data.match(
+ //g
+ );
+ },
+
+ /**
+ * Determine how to process the modified content based on context.
+ *
+ * This function is called by SEO plugins after a content modification.
+ * It determines if the content is from the Classic Editor or the Block
+ * Editor, and then processes it accordingly.
+ *
+ * @param {string} data - The content data to be modified.
+ *
+ * @returns {string} The modified content data.
+ */
+ seoContentChange: function( data ) {
+ if ( ! data ) {
+ return data;
+ }
+
+ const isClassicEditor = this.isClassicEditorPanelsEnabled();
+ const isBlockEditor = this.isBlockEditorPanelsEnabled();
+
+ // If Page Builder isn't set up for this page,
+ // return the data as is.
+ if (
+ ! isClassicEditor &&
+ ! isBlockEditor
+ ) {
+ return data;
+ }
+
+ // Is this the Classic Editor?
+ if (
+ isClassicEditor &&
+ ! isBlockEditor
+ ) {
+ return this.contentModification( data );
+ }
+
+ // The current context has to be the Block Editor.
+ return this.processBlocks( data );
+ },
+
+ /**
+ * Process SiteOrigin layout blocks in the content.
+ *
+ * This function searches for SiteOrigin layout blocks in the
+ * content and replaces them with the rendered contents. This allows SEO
+ * plugins to correctly analyze the content of the SO Layout blocks.
+ *
+ * @param {string} data - The content data to be processed.
+ *
+ * @returns {string} The processed content data.
+ */
+ processBlocks: function( data ) {
+ const soBlocks = this.findSoLayoutBlocks( data );
+
+ // If there are no SO Layout blocks, return the data as is.
+ if ( ! soBlocks ) {
+ return data;
+ }
- var extractContent = function( data ) {
- var $data = $( data );
+ // Replace any found SO Layout blocks with the rendered contents.
+ soBlocks.forEach( function( block ) {
+ data = data.replace( block, this.contentModification( block ) );
+ }, this );
+
+ return data;
+ },
+
+ /**
+ * Extract text from the provided data.
+ *
+ * This function extracts content from the provided data by removing
+ * elements that have no content analysis value and filtering out
+ * everything else that's not in the allowed tags list.
+ *
+ * @param {string} data - The content data to be extracted.
+ *
+ * @returns {string} The extracted content data.
+ */
+ extractContent: function( data ) {
+ const $data = $( data );
if ( $data.find( '.so-panel' ).length === 0 ) {
// Skip this for empty pages
@@ -86,34 +127,58 @@ jQuery( function( $ ) {
// Remove elements that have no content analysis value.
$data.find( 'iframe, script, style, link' ).remove();
- $data.find( "*") .not( whitelist ).each( function() {
- var content = $( this ).contents();
- $( this ).replaceWith( content );
+ // Filter out everything else that's not in the allowed tags list.
+ $data.find( '*' ) .not( this.allowedTags ).each( function() {
+ const $$ = $( this );
+
+ $$.replaceWith(
+ $$.contents()
+ );
} );
return $data.html();
- };
+ },
+
+ /**
+ * Modify the content for SEO analysis.
+ *
+ * This function modifies the content for SEO analysis by
+ * extracting content from the SiteOrigin Panels Builder view.
+ *
+ * @param {string} data - The content data to be modified.
+ *
+ * @returns {string} The modified content data.
+ */
+ contentModification: function( data ) {
+ if ( ! Array.isArray( window.soPanelsBuilderView ) ) {
+ return this.extractContent( window.soPanelsBuilderView.contentPreview );
+ }
- if ( ! Array.isArray( window.soPanelsBuilderView ) ) {
- data = extractContent( window.soPanelsBuilderView.contentPreview );
- } else {
data = null;
window.soPanelsBuilderView.forEach( function( panel ) {
- data += extractContent( panel.contentPreview );
- } );
- }
+ data += this.extractContent( panel.contentPreview );
+ }, this );
+
+ return data;
+ },
- return data;
- };
+ init: function() {
+ if ( typeof YoastSEO !== 'undefined' ) {
+ YoastSEO.app.registerPlugin( 'SiteOriginSeoCompat', { status: 'ready' } );
+ YoastSEO.app.registerModification( 'content', this.seoContentChange.bind( this ), 'SiteOriginSeoCompat', 5 );
+ }
- if ( typeof rankMathEditor !== 'undefined' ) {
- new SiteOriginSeoCompat();
- } else {
- $( window ).on(
- 'YoastSEO:ready',
- function () {
- new SiteOriginSeoCompat();
+ if ( typeof rankMathEditor !== 'undefined' ) {
+ wp.hooks.addFilter( 'rank_math_content', 'SiteOriginSeoCompat', this.seoContentChange.bind( this ) );
}
- );
+ }
+ } );
+
+ if ( typeof rankMathEditor !== 'undefined' ) {
+ SiteOriginSeoCompat().init();
}
+
+ $( window ).on( 'YoastSEO:ready', () => {
+ SiteOriginSeoCompat().init();
+ } );
} );
diff --git a/js/siteorigin-panels/dialog/row.js b/js/siteorigin-panels/dialog/row.js
index 5927274f..012f2a7f 100644
--- a/js/siteorigin-panels/dialog/row.js
+++ b/js/siteorigin-panels/dialog/row.js
@@ -49,6 +49,7 @@ module.exports = panels.view.dialog.extend({
// Changing the row.
'click .row-set-form .so-row-field': 'changeCellTotal',
+ 'change .row-set-form .so-row-field': 'changeCellTotal',
'click .cell-resize-sizing span': 'changeCellRatio',
'click .cell-resize-direction ': 'changeSizeDirection',
@@ -184,10 +185,6 @@ module.exports = panels.view.dialog.extend({
this.$( 'input[name="cells"].so-row-field' ).val( this.model.get( 'cells' ).length );
}
- this.$( 'input.so-row-field' ).on( 'keyup', function() {
- $(this).trigger('change');
- });
-
return this;
},
diff --git a/readme.txt b/readme.txt
index 64f0da47..0fb120a7 100644
--- a/readme.txt
+++ b/readme.txt
@@ -121,6 +121,13 @@ SiteOrigin offers a single premium plugin that enhances and extends Page Builder
== Changelog ==
+= 2.31.4 – 02 February 2025 =
+* Added Compatibility for Events Manager.
+* AIOSEO: Loaded plugin widgets.
+* Refactored SEO plugin compatibility and restored Yoast Block Editor content parsing.
+* Layout Directory: Resolved incorrect empty search after search query.
+* WPML: Adjusted editor labels for the WPML 4.7 release.
+
= 2.31.3 – 20 December 2024 =
* WPML: Improved compatibility by excluding `panels_data` field from automatic translation handling.
* Prebuilt Layouts: Added text/html to accepted mime types for layout exports.
diff --git a/siteorigin-panels.php b/siteorigin-panels.php
index c9fba450..ca213b10 100644
--- a/siteorigin-panels.php
+++ b/siteorigin-panels.php
@@ -464,7 +464,7 @@ private function get_localized_word_count( $text ) {
// From the core `wp_trim_words` function to get localized word count.
$text = wp_strip_all_tags( $text );
- if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
+ if ( strpos( _x( 'words', 'Word count type. Do not translate!', 'siteorigin-panels' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$words_array = $words_array[0];
diff --git a/tpl/js-templates.php b/tpl/js-templates.php
index 5dbbf4a1..640e97e8 100644
--- a/tpl/js-templates.php
+++ b/tpl/js-templates.php
@@ -110,8 +110,8 @@
foreach ( $row_colors as $id => $color ) {
$name = ! empty( $color['name'] ) ? sanitize_title( $color['name'] ) : $id;
?>
-
@@ -450,7 +450,9 @@ class="button-secondary dashicons so-mode"
foreach ( $tabs as $id => $tab ) {
?>
-
+
+
+
- title
+ title
- title
- url
+ title
+ url
- title
+ title
- title
+ title
- title
- content
+ title
+ content
- title
+ title
- title
- caption
- url
+ title
+ caption
+ url
- title
+ title
- title
- text
+ title
+ text
- title
+ title
- title
+ title
- title
- url
+ title
+ url
- title
+ title
- title
+ title
- title
+ title
- title
+ title
- title
- text
+ title
+ text
-
-
- title
-
-
- title
- content_text
- content_layout
-
-
-
-
- title
-
-
- title
- content_text
- content_layout
-
-
-
-
- text
- url
- attributes>title
-
-
-
-
- title
- settings>default_subject
- settings>subject_prefix
- settings>success_message
- settings>submit_text
-
-
- label
- description
- required>missing_message
-
-
-
-
- title
- sub_title
- button>text
- button>destination_url
- attributes>title
-
-
-
-
- title
- text
-
-
-
-
- title
- text
- icon_title
- more_text
- more_url
-
-
-
-
- headline>text
- headline>destination_url
- sub_headline>text
- sub_headline>destination_url
-
-
-
-
- content
-
-
- button>text
- button>url
- button>attributes>title
-
-
-
-
- title
- url
-
-
-
-
- title
- alt
- url
-
-
-
-
- title
- alt
- url
-
-
-
-
- title
-
-
-
-
- title
-
-
- title
- subtitle
- image_title
- image_alt
- per
- button_text
- url
-
-
- features>text
- features>hover_text
-
-
-
-
- title
-
-
- title
- url
-
-
-
-
- url
-
-
-
-
- title
-
-
- url
- icon_title
-
-
-
-
- title
-
-
- title
- content_text
- content_layout
-
-
-
-
- title
- label
-
-
-
-
- title
-
-
- text
- url
-
-
-
-
- title
-
-
-
-
- title
-
-
- title
- subtitle
- image_title
- image_alt
- per
- button
- url
-
-
- text
- hover
-
-
panels_data