--- a/kadence-blocks/dist/admin-kadence-home.asset.php
+++ b/kadence-blocks/dist/admin-kadence-home.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-helpers', 'kadence-icons', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '44fa2c6de7858a3c61f2');
+<?php return array('dependencies' => array('kadence-helpers', 'kadence-icons', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => 'e39da594dafbcb032b2b');
--- a/kadence-blocks/dist/blocks-advanced-form.asset.php
+++ b/kadence-blocks/dist/blocks-advanced-form.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => 'e1d8915ac755b07a61b9');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => 'a3d1893cd525772d5ca9');
--- a/kadence-blocks/dist/blocks-advancedbtn.asset.php
+++ b/kadence-blocks/dist/blocks-advancedbtn.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => 'e82eab9e4ca79cca6cd3');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => '8f2d08eb833d1d15045c');
--- a/kadence-blocks/dist/blocks-advancedgallery.asset.php
+++ b/kadence-blocks/dist/blocks-advancedgallery.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => 'da84c01a425471ed9284');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => '584373c3e262800665a3');
--- a/kadence-blocks/dist/blocks-icon.asset.php
+++ b/kadence-blocks/dist/blocks-icon.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'cb556cca8dc90b4a51bf');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'aa90acbc257519a23902');
--- a/kadence-blocks/dist/blocks-iconlist.asset.php
+++ b/kadence-blocks/dist/blocks-iconlist.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text'), 'version' => 'eae421f3af453c0bd768');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives', 'wp-rich-text'), 'version' => 'bf90f5657451d88a91a9');
--- a/kadence-blocks/dist/blocks-image.asset.php
+++ b/kadence-blocks/dist/blocks-image.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => '132a5ff1e5913ff01091');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => '128a7de120d7f086b5a8');
--- a/kadence-blocks/dist/blocks-infobox.asset.php
+++ b/kadence-blocks/dist/blocks-infobox.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => '1f1f05027e0ab4b346d8');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => '9f020740662547988705');
--- a/kadence-blocks/dist/blocks-navigation-link.asset.php
+++ b/kadence-blocks/dist/blocks-navigation-link.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-primitives', 'wp-url'), 'version' => 'd6b4d0bddd7b124289af');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-primitives', 'wp-url'), 'version' => 'a25f5b7dc123edb04c1c');
--- a/kadence-blocks/dist/blocks-navigation.asset.php
+++ b/kadence-blocks/dist/blocks-navigation.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '93379a1b518cc2735609');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '6d0f613d7a295a60b94a');
--- a/kadence-blocks/dist/blocks-rowlayout.asset.php
+++ b/kadence-blocks/dist/blocks-rowlayout.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '63265c32adeecde4f472');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '98cf969061c4ae2cdd6f');
--- a/kadence-blocks/dist/blocks-tabs.asset.php
+++ b/kadence-blocks/dist/blocks-tabs.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => '8303b3c4f68307fb52c7');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => 'd75f1e1eac88dfb0f465');
--- a/kadence-blocks/dist/blocks-testimonials.asset.php
+++ b/kadence-blocks/dist/blocks-testimonials.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '277ba97e1cf18144b576');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '7351abaf8775da439210');
--- a/kadence-blocks/dist/components.asset.php
+++ b/kadence-blocks/dist/components.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-is-shallow-equal', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '35bce45beb7c1055ef09');
+<?php return array('dependencies' => array('kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-is-shallow-equal', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-url'), 'version' => '219fc03434baa50b89bd');
--- a/kadence-blocks/dist/extension-admin-notices.asset.php
+++ b/kadence-blocks/dist/extension-admin-notices.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('wp-i18n'), 'version' => '2c2150318b76f0d6b36e');
--- a/kadence-blocks/dist/extension-block-css.asset.php
+++ b/kadence-blocks/dist/extension-block-css.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-helpers', 'lodash', 'react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '4d32c505cbfba39f6327');
+<?php return array('dependencies' => array('kadence-helpers', 'lodash', 'react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '930435564e82224b835d');
--- a/kadence-blocks/dist/kadence-optimizer.asset.php
+++ b/kadence-blocks/dist/kadence-optimizer.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('wp-api-fetch', 'wp-data', 'wp-hooks', 'wp-i18n', 'wp-url'), 'version' => 'a1a86e071a9e95ed3f75');
--- a/kadence-blocks/dist/lazy-loader.asset.php
+++ b/kadence-blocks/dist/lazy-loader.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array(), 'version' => 'e94909e980c4ad30dcd6');
--- a/kadence-blocks/dist/plugin-kadence-control.asset.php
+++ b/kadence-blocks/dist/plugin-kadence-control.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins'), 'version' => '6759d531f0efd68f51cb');
+<?php return array('dependencies' => array('kadence-components', 'kadence-helpers', 'kadence-icons', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins', 'wp-preferences', 'wp-primitives', 'wp-url'), 'version' => 'ee78b4e6297d371c77b0');
--- a/kadence-blocks/dist/post-saved-event.asset.php
+++ b/kadence-blocks/dist/post-saved-event.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('wp-data', 'wp-hooks'), 'version' => '5e79f2705c72de17d440');
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-abstract-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-abstract-block.php
@@ -97,7 +97,7 @@
'captcha',
'submit',
];
-
+
/**
* Allow us to enable merged defaults on blocks individually.
* Considered setting this as a property within each block, but it's easier to see an exhaustive list here.
@@ -531,7 +531,7 @@
/**
* Retuurn if this block should register itself. (can override for things like blocks in two plugins)
- *
+ *
* @return boolean
*/
public function should_register() {
@@ -546,4 +546,29 @@
protected function get_pro_version() {
return defined( 'KBP_VERSION' ) ? KBP_VERSION : null;
}
+
+ /**
+ * Build escaped HTML attributes to be placed in an HTML tag.
+ *
+ * @param array<string, string|int|float|bool> $attributes The html attributes to render to a tag.
+ *
+ * @return string
+ */
+ protected function build_escaped_html_attributes( array $attributes ): string {
+ $html = '';
+
+ foreach ( $attributes as $key => $value ) {
+ if ( is_bool( $value ) ) {
+ if ( $value ) {
+ $html .= sprintf( ' %s', esc_attr( $key ) );
+ }
+
+ continue;
+ }
+
+ $html .= sprintf( ' %s="%s"', esc_attr( $key ), esc_attr( $value ) );
+ }
+
+ return $html;
+ }
}
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-advancedgallery-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-advancedgallery-block.php
@@ -412,7 +412,7 @@
$css->add_property( 'color', $css->render_color( $attributes['arrowCustomColor'] ) );
}
- if ( ! empty( $attributes['arrowCustomColorHover'] ) && $is_carousel ) {
+ if ( ! empty( $attributes['arrowCustomColorHover'] ) && $is_carousel ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .splide .splide__arrow:hover' );
$css->add_property( 'color', $css->render_color( $attributes['arrowCustomColorHover'] ) );
}
@@ -464,49 +464,49 @@
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button' );
$css->add_property( 'color', $css->render_color( $attributes['arrowCustomColor'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorHover'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:hover' );
$css->add_property( 'color', $css->render_color( $attributes['arrowCustomColorHover'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorActive'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:active' );
$css->add_property( 'color', $css->render_color( $attributes['arrowCustomColorActive'] ) );
}
-
+
// Pause button background styles
if ( ! empty( $attributes['arrowCustomColorBackground'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button' );
$css->add_property( 'background-color', $css->render_color( $attributes['arrowCustomColorBackground'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorBackgroundHover'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:hover' );
$css->add_property( 'background-color', $css->render_color( $attributes['arrowCustomColorBackgroundHover'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorBackgroundActive'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:active' );
$css->add_property( 'background-color', $css->render_color( $attributes['arrowCustomColorBackgroundActive'] ) );
}
-
+
// Pause button border styles
if ( ! empty( $attributes['arrowCustomColorBorder'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button' );
$css->add_property( 'border-color', $css->render_color( $attributes['arrowCustomColorBorder'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorBorderHover'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:hover' );
$css->add_property( 'border-color', $css->render_color( $attributes['arrowCustomColorBorderHover'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomColorBorderActive'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button:active' );
$css->add_property( 'border-color', $css->render_color( $attributes['arrowCustomColorBorderActive'] ) );
}
-
+
if ( ! empty( $attributes['arrowCustomBorderWidth'] ) ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .kb-gallery-pause-button' );
$css->add_property( 'border-width', $attributes['arrowCustomBorderWidth'] . 'px' );
@@ -560,13 +560,13 @@
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .splide__pagination__page' );
$css->add_property( 'border-color', $css->render_color( $attributes['dotCustomColorBorder'] ) );
}
-
+
if ( ! empty( $attributes['dotCustomColorBorderHover'] ) && $is_carousel && 'custom' === $dot_style ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .splide__pagination__page:hover' );
$css->add_property( 'border-color', $css->render_color( $attributes['dotCustomColorBorderHover'] ) );
}
- if ( ! empty( $attributes['dotCustomColorBorderActive'] ) && $is_carousel && 'custom' === $dot_style ) {
+ if ( ! empty( $attributes['dotCustomColorBorderActive'] ) && $is_carousel && 'custom' === $dot_style ) {
$css->set_selector( '.kb-gallery-id-' . $unique_id . ' .splide__pagination__page.is-active' );
$css->add_property( 'border-color', $css->render_color( $attributes['dotCustomColorBorderActive'] ) );
}
@@ -582,7 +582,7 @@
return $css->css_output();
}
-
+
/**
* Parse images from saved HTML content.
*
@@ -749,7 +749,7 @@
switch ( $type ) {
case 'carousel':
$content .= '<div class="' . esc_attr( implode( ' ', $gallery_classes ) ) . '" data-image-filter="' . esc_attr( $image_filter ) . '" data-lightbox-caption="' . ( $lightbox_cap ? 'true' : 'false' ) . '">';
- $content .= '<div class="kt-blocks-carousel splide kt-carousel-container-dotstyle-' . esc_attr( $dot_style ) . ' kt-carousel-arrowstyle-' . esc_attr( $arrow_style ) . ' kt-carousel-dotstyle-' . esc_attr( $dot_style ) . ' kb-slider-group-' . esc_attr( 'center' !== $arrow_position && 'outside-top' !== $arrow_position && 'outside-bottom' !== $arrow_position ? 'arrows' : 'arrow' ) . ' kb-slider-arrow-position-' . esc_attr( $arrow_position ) . '" data-columns-xxl="' . esc_attr( $columns_xxl ) . '" data-columns-xl="' . esc_attr( $columns_xl ) . '" data-columns-md="' . esc_attr( $columns_md ) . '" data-columns-sm="' . esc_attr( $columns_sm ) . '" data-columns-xs="' . esc_attr( $columns_xs ) . '" data-columns-ss="' . esc_attr( $columns_ss ) . '" data-slider-anim-speed="' . esc_attr( $trans_speed ) . '" data-slider-scroll="' . esc_attr( $slides_sc ) . '" data-slider-arrows="' . esc_attr( 'none' === $arrow_style ? 'false' : 'true' ) . '" data-slider-dots="' . esc_attr( 'none' === $dot_style ? 'false' : 'true' ) . '" data-slider-hover-pause="false" data-slider-auto="' . esc_attr( $autoplay ) . '" data-slider-speed="' . esc_attr( $auto_speed ) . '" data-slider-gap="' . esc_attr( $gap . $gap_unit ) . '" data-slider-gap-tablet="' . esc_attr( $tablet_gap . $gap_unit ) . '" data-slider-gap-mobile="' . esc_attr( $mobile_gap . $gap_unit ) . '" data-show-pause-button="' . esc_attr( $show_pause_button ? 'true' : 'false' ) . '" aria-label="' . esc_attr( __( 'Photo Gallery Carousel', 'kadence-blocks' ) ) . '">';
+ $content .= '<div class="kt-blocks-carousel splide kt-carousel-container-dotstyle-' . esc_attr( $dot_style ) . ' kt-carousel-arrowstyle-' . esc_attr( $arrow_style ) . ' kt-carousel-dotstyle-' . esc_attr( $dot_style ) . ' kb-slider-group-' . esc_attr( 'center' !== $arrow_position && 'outside-top' !== $arrow_position && 'outside-bottom' !== $arrow_position ? 'arrows' : 'arrow' ) . ' kb-slider-arrow-position-' . esc_attr( $arrow_position ) . '" data-columns-xxl="' . esc_attr( $columns_xxl ) . '" data-columns-xl="' . esc_attr( $columns_xl ) . '" data-columns-md="' . esc_attr( $columns_md ) . '" data-columns-sm="' . esc_attr( $columns_sm ) . '" data-columns-xs="' . esc_attr( $columns_xs ) . '" data-columns-ss="' . esc_attr( $columns_ss ) . '" data-slider-anim-speed="' . esc_attr( $trans_speed ) . '" data-slider-scroll="' . esc_attr( $slides_sc ) . '" data-slider-arrows="' . esc_attr( 'none' === $arrow_style ? 'false' : 'true' ) . '" data-slider-dots="' . esc_attr( 'none' === $dot_style ? 'false' : 'true' ) . '" data-slider-hover-pause="false" data-slider-auto="' . esc_attr( $autoplay ) . '" data-slider-speed="' . esc_attr( $auto_speed ) . '" data-slider-gap="' . esc_attr( $gap . $gap_unit ) . '" data-slider-gap-tablet="' . esc_attr( $tablet_gap . $gap_unit ) . '" data-slider-gap-mobile="' . esc_attr( $mobile_gap . $gap_unit ) . '" data-show-pause-button="' . esc_attr( $show_pause_button ? 'true' : 'false' ) . '" data-slider-label="' . esc_attr( __( 'Photo Gallery Carousel', 'kadence-blocks' ) ) . '">';
$content .= '<div class="splide__track">';
$content .= '<ul class="kt-blocks-carousel-init kb-gallery-carousel splide__list">';
@@ -864,12 +864,12 @@
break;
}
$content = sprintf( '<div %1$s>%2$s</div>', $wrapper_attributes, $content );
-
+
return $content;
}
/**
* Render mosaic gallery layout.
- *
+ *
* This function can be used by Kadence Blocks Pro for dynamic content.
* It creates a mosaic pattern layout for gallery images with specific grid classes.
*
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-column-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-column-block.php
@@ -923,6 +923,14 @@
}
return $css->css_output();
}
+
+ public function build_html( $attributes, $unique_id, $content, $block_instance ) {
+ $html = parent::build_html( $attributes, $unique_id, $content, $block_instance );
+
+ // Do not remove: The Optimizer uses this.
+ return apply_filters( 'kadence_blocks_column_html', $html, $attributes, $unique_id, $block_instance );
+ }
+
/**
* Set the vertical align.
*
@@ -980,4 +988,5 @@
}
}
}
+
Kadence_Blocks_Column_Block::get_instance();
--- a/kadence-blocks/includes/blocks/class-kadence-blocks-row-layout-block.php
+++ b/kadence-blocks/includes/blocks/class-kadence-blocks-row-layout-block.php
@@ -1282,15 +1282,15 @@
$css->add_property( 'display', 'none !important' );
}
$css->set_media_state( 'desktop' );
-
+
// Background Slider Pause Button Styles.
if ( isset( $attributes['backgroundSettingTab'] ) && 'slider' === $attributes['backgroundSettingTab'] ) {
$arrow_style = ! empty( $attributes['backgroundSliderSettings'][0]['arrowStyle'] ) ? $attributes['backgroundSliderSettings'][0]['arrowStyle'] : 'none';
$show_pause_button = isset( $attributes['backgroundSliderSettings'][0]['showPauseButton'] ) ? $attributes['backgroundSliderSettings'][0]['showPauseButton'] : false;
-
+
if ( $show_pause_button ) {
$css->set_selector( $base_selector . ' .kb-blocks-bg-slider .kb-gallery-pause-button' );
-
+
// Set styles based on arrow style.
switch ( $arrow_style ) {
case 'blackonlight':
@@ -1317,7 +1317,7 @@
}
}
}
-
+
if ( isset( $attributes['kadenceBlockCSS'] ) && ! empty( $attributes['kadenceBlockCSS'] ) ) {
$css->add_css_string( str_replace( 'selector', $base_selector, $attributes['kadenceBlockCSS'] ) );
}
@@ -1506,14 +1506,31 @@
$style_args['background-repeat'] = $attributes['bgImgRepeat'];
}
}
- $style_output = array();
+
+ $style_output = [];
+
foreach ( $style_args as $sub_key => $value ) {
$style_output[] = $sub_key . ':' . esc_attr( $value ) . ';';
}
- $output .= '<li class="splide__slide kb-bg-slide-contain">';
- $output .= '<div class="kb-bg-slide kb-bg-slide-' . esc_attr( $key ) . '" style="' . esc_attr( implode( ' ', $style_output ) ) . '">';
- $output .= '</div>';
- $output .= '</li>';
+
+ $attrs = [
+ 'class' => 'kb-bg-slide kb-bg-slide-' . $key,
+ 'style' => implode( ' ', $style_output ),
+ ];
+
+ /**
+ * DO NOT REMOVE: The optimizer uses this.
+ *
+ * @param array<string, mixed> $attrs The HTML attributes.
+ * @param array<string, mixed> $attributes The row block attributes.
+ */
+ $attrs = apply_filters( 'kadence_blocks_row_slider_attrs', $attrs, $attributes );
+
+ $output .= sprintf(
+ '<li class="splide__slide kb-bg-slide-contain"><div %s></div></li>',
+ $this->build_escaped_html_attributes( $attrs )
+ );
+
if ( $attributes['backgroundSliderCount'] == $item ) {
break;
}
@@ -1607,6 +1624,15 @@
if ( isset( $video_args['muted'] ) && $video_args['muted'] == 'false' ) {
unset( $video_args['muted'] );
}
+
+ /**
+ * DO NOT REMOVE: The optimizer uses this.
+ *
+ * @param array<string, mixed> $video_args The HTML attributes.
+ * @param array<string, mixed> $attributes The row block attributes.
+ */
+ $video_args = apply_filters( 'kadence_blocks_row_video_attrs', $video_args, $attributes );
+
$video_html_attributes = array();
foreach ( $video_args as $key => $value ) {
if ( empty( $value ) ) {
@@ -1770,6 +1796,14 @@
}
}
}
+
+ /**
+ * DO NOT REMOVE: The Optimizer uses this.
+ *
+ * @param array<string, mixed> $wrapper_args The wrapper div HTML attributes.
+ * @param array<string, mixed> $attributes The current block attributes.
+ */
+ $wrapper_args = apply_filters( 'kadence_blocks_row_wrapper_args', $wrapper_args, $attributes );
$wrapper_attributes = get_block_wrapper_attributes( $wrapper_args );
$inner_wrapper_attributes = implode( ' ', $inner_wrap_attributes );
$content = sprintf( '<%1$s %2$s>%3$s<div %4$s>%5$s</div></%1$s>', $html_tag, $wrapper_attributes, $extra_content, $inner_wrapper_attributes, $content );
--- a/kadence-blocks/includes/class-kadence-blocks-prebuilt-library-rest-api.php
+++ b/kadence-blocks/includes/class-kadence-blocks-prebuilt-library-rest-api.php
@@ -846,14 +846,26 @@
* @param string $content The content to process.
*/
public function process_cpt( $content, $cpt_blocks, $style ) {
+ $valid_cpt_block_names = [
+ 'kadence/header',
+ 'kadence/navigation',
+ 'kadence/advanced-form',
+ 'kadence/query',
+ 'kadence/query-card',
+ ];
+ $valid_cpt_block_post_types = [
+ 'kadence_header',
+ 'kadence_navigation',
+ 'kadence_form',
+ 'kadence_query',
+ 'kadence_query_card',
+ ];
+
+
foreach ( $cpt_blocks as $cpt_block_name => $cpt_block_content ) {
- switch ( $cpt_block_name ) {
- case 'kadence/header':
- case 'kadence/navigation':
- case 'kadence/advanced-form':
- case 'kadence/query':
- case 'kadence/query-card':
- foreach ( $cpt_block_content as $cpt_key => $cpt_data ) {
+ if ( in_array( $cpt_block_name, $valid_cpt_block_names ) ) {
+ foreach ( $cpt_block_content as $cpt_key => $cpt_data ) {
+ if ( in_array( $cpt_data['post_type'], $valid_cpt_block_post_types ) ) {
$old_id = $cpt_data['ID'];
$id_map = [];
if ( ! empty( $cpt_data['inner_posts'] ) && is_array( $cpt_data['inner_posts'] ) ) {
@@ -872,7 +884,7 @@
$content = $this->update_block_ids( $content, $new_id_map );
}
}
- break;
+ }
}
}
return $content;
@@ -922,7 +934,7 @@
'post_type' => $cpt_data['post_type'],
'post_title' => $title,
'post_content' => '',
- 'post_status' => 'publish',
+ 'post_status' => current_user_can( 'publish_posts' ) ? 'publish' : 'pending',
],
true
);
--- a/kadence-blocks/includes/resources/Admin/Admin_Provider.php
+++ b/kadence-blocks/includes/resources/Admin/Admin_Provider.php
@@ -0,0 +1,39 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksAdmin;
+
+use KadenceWPKadenceBlocksAssetAsset;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider;
+
+/**
+ * Dashboard / wp-admin container definitions and hooks.
+ */
+final class Admin_Provider extends Provider {
+
+ public const HANDLE_POST_SAVED_EVENT = 'post-saved-event';
+
+ public function register(): void {
+ add_action(
+ 'admin_init',
+ function (): void {
+ $this->register_post_saved_event();
+ }
+ );
+ }
+
+ /**
+ * Register the post-saved-event action that fires when a Gutenberg post is saved.
+ *
+ * @return void
+ */
+ private function register_post_saved_event(): void {
+ add_action(
+ 'enqueue_block_editor_assets',
+ function (): void {
+ $asset = $this->container->get( Asset::class );
+
+ $asset->enqueue_script( self::HANDLE_POST_SAVED_EVENT, 'dist/post-saved-event' );
+ }
+ );
+ }
+}
--- a/kadence-blocks/includes/resources/App.php
+++ b/kadence-blocks/includes/resources/App.php
@@ -4,9 +4,14 @@
use InvalidArgumentException;
use KadenceWPKadenceBlocksAdbarDot;
+use KadenceWPKadenceBlocksAdminAdmin_Provider;
+use KadenceWPKadenceBlocksAssetAsset_Provider;
use KadenceWPKadenceBlocksCacheCache_Provider;
+use KadenceWPKadenceBlocksDatabaseDatabase_Provider;
use KadenceWPKadenceBlocksHealthHealth_Provider;
use KadenceWPKadenceBlocksImage_DownloaderImage_Downloader_Provider;
+use KadenceWPKadenceBlocksLogLog_Provider;
+use KadenceWPKadenceBlocksOptimizerOptimizer_Provider;
use KadenceWPKadenceBlocksShutdownShutdown_Provider;
use KadenceWPKadenceBlocksStellarWPContainerContractContainerInterface;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsContainer;
@@ -19,12 +24,12 @@
*/
final class App {
- private static $instance;
+ private static self $instance;
/**
* @var Container
*/
- private $container;
+ private Container $container;
/**
* Add any custom providers here.
@@ -33,14 +38,22 @@
*
* @var class-string<Providable>
*/
- private $providers = array(
+ private $providers = [
+ Log_Provider::class,
+ Database_Provider::class,
+ Asset_Provider::class,
Uplink_Provider::class,
Health_Provider::class,
+ Admin_Provider::class,
Image_Downloader_Provider::class,
+ Optimizer_Provider::class,
Cache_Provider::class,
Shutdown_Provider::class,
- );
+ ];
+ /**
+ * @param Container $container
+ */
private function __construct(
Container $container
) {
@@ -53,7 +66,7 @@
* @param Container|null $container
*
* @return self
- * @throws InvalidArgumentException
+ * @throws InvalidArgumentException If no container is provided.
*/
public static function instance( ?Container $container = null ): App {
if ( ! isset( self::$instance ) ) {
@@ -91,5 +104,4 @@
public function __sleep(): array {
throw new RuntimeException( 'method not implemented' );
}
-
}
--- a/kadence-blocks/includes/resources/Asset/Asset.php
+++ b/kadence-blocks/includes/resources/Asset/Asset.php
@@ -0,0 +1,61 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksAsset;
+
+final class Asset {
+
+ /**
+ * @var string The URL to the plugin's main folder.
+ */
+ private string $plugin_url;
+
+ /**
+ * @param string $plugin_url The URL to the plugin's main folder.
+ */
+ public function __construct( string $plugin_url ) {
+ $this->plugin_url = $plugin_url;
+ }
+
+ /**
+ * Get an asset URL.
+ *
+ * @param string $path A relative path to an asset.
+ *
+ * @return string The full URL to the asset.
+ */
+ public function get_url( string $path = '' ): string {
+ return $this->plugin_url . $path;
+ }
+
+ /**
+ * Get the asset meta.
+ *
+ * @param string $path The filepath without extension, e.g. dist/hello.
+ *
+ * @return array{dependencies?: string[], version?: string };
+ */
+ public function get_meta( string $path = '' ): array {
+ return kadence_blocks_get_asset_file( $path );
+ }
+
+ /**
+ * Enqueue a script.
+ *
+ * @param string $name The name of the script.
+ * @param string $path The relative path to the script, without extension, e.g. build/settings.
+ * @param string $ext The file extension without a period.
+ *
+ * @return void
+ */
+ public function enqueue_script( string $name, string $path, string $ext = 'js' ): void {
+ $meta = $this->get_meta( $path );
+
+ wp_enqueue_script(
+ $name,
+ $this->get_url( "$path.$ext" ),
+ $meta['dependencies'] ?? [],
+ $meta['version'] ?? '',
+ true
+ );
+ }
+}
--- a/kadence-blocks/includes/resources/Asset/Asset_Provider.php
+++ b/kadence-blocks/includes/resources/Asset/Asset_Provider.php
@@ -0,0 +1,17 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksAsset;
+
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider;
+
+final class Asset_Provider extends Provider {
+
+ /**
+ * @inheritDoc
+ */
+ public function register(): void {
+ $this->container->when( Asset::class )
+ ->needs( '$plugin_url' )
+ ->give( static fn(): string => KADENCE_BLOCKS_URL );
+ }
+}
--- a/kadence-blocks/includes/resources/Database/Database_Provider.php
+++ b/kadence-blocks/includes/resources/Database/Database_Provider.php
@@ -0,0 +1,53 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksDatabase;
+
+use KadenceWPKadenceBlocksOptimizerDatabaseOptimizer_Table;
+use KadenceWPKadenceBlocksOptimizerDatabaseViewport_Hash_Table;
+use KadenceWPKadenceBlocksPsrLogLoggerInterface;
+use KadenceWPKadenceBlocksStellarWPDBDatabaseExceptionsDatabaseQueryException;
+use KadenceWPKadenceBlocksStellarWPDBDB;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider;
+use KadenceWPKadenceBlocksStellarWPSchemaConfig;
+use KadenceWPKadenceBlocksStellarWPSchemaRegister;
+
+final class Database_Provider extends Provider {
+
+ public const SCHEMA_TABLES = 'kadence_blocks.database.schema_tables';
+
+ /**
+ * Configure the stellarwp/schema library.
+ *
+ * @throws DatabaseQueryException If we failed to create or update the database tables.
+ *
+ * @return void
+ */
+ public function register(): void {
+ Config::set_container( $this->container );
+ Config::set_db( DB::class );
+
+ // Add all schema tables to be registered here.
+ $this->container->setVar(
+ self::SCHEMA_TABLES,
+ [
+ Viewport_Hash_Table::class,
+ Optimizer_Table::class,
+ ]
+ );
+
+ try {
+ Register::tables( $this->container->getVar( self::SCHEMA_TABLES ) );
+ } catch ( DatabaseQueryException $e ) {
+ $this->container->get( LoggerInterface::class )->emergency(
+ 'Unable to create or update database tables',
+ [
+ 'query_errors' => $e->getQueryErrors(),
+ 'query' => $e->getQuery(),
+ 'exception' => $e,
+ ]
+ );
+
+ throw $e;
+ }
+ }
+}
--- a/kadence-blocks/includes/resources/Database/Query.php
+++ b/kadence-blocks/includes/resources/Database/Query.php
@@ -0,0 +1,75 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksDatabase;
+
+use KadenceWPKadenceBlocksStellarWPDBDB;
+use KadenceWPKadenceBlocksStellarWPDBQueryBuilderQueryBuilder;
+
+/**
+ * A query wrapper for the StellarWP DB instance using a specific
+ * table.
+ *
+ * This class should be extended and configured for a specific table
+ * via a Provider.
+ */
+abstract class Query {
+
+ /**
+ * The WordPress table name without a prefix.
+ *
+ * @var string
+ */
+ protected string $table;
+
+ /**
+ * @param string $table The WordPress table name without a prefix.
+ */
+ public function __construct( string $table ) {
+ $this->table = $table;
+ }
+
+ /**
+ * Get the current table being used.
+ *
+ * @return string
+ */
+ public function table(): string {
+ return $this->table;
+ }
+
+ /**
+ * Get the table name with prefix.
+ *
+ * @return string
+ */
+ public function table_with_prefix(): string {
+ global $wpdb;
+
+ return $wpdb->prefix . $this->table;
+ }
+
+ /**
+ * Get the query builder for this table.
+ *
+ * @return QueryBuilder
+ */
+ public function qb(): QueryBuilder {
+ return DB::table( $this->table );
+ }
+
+ /**
+ * Wrapper for wpdb::get_var
+ *
+ * @see wpdb::get_var()
+ *
+ * @param string|null $query Optional. SQL query. Defaults to null, use the result from the
+ * previous query.
+ * @param int $x Optional. Column of value to return. Indexed from 0. Default 0.
+ * @param int $y Optional. Row of value to return. Indexed from 0. Default 0.
+ *
+ * @return string|null
+ */
+ public function get_var( ?string $query = null, int $x = 0, int $y = 0 ): ?string {
+ return DB::get_var( $query, $x, $y );
+ }
+}
--- a/kadence-blocks/includes/resources/Image_Downloader/Image_Downloader_Provider.php
+++ b/kadence-blocks/includes/resources/Image_Downloader/Image_Downloader_Provider.php
@@ -3,18 +3,11 @@
namespace KadenceWPKadenceBlocksImage_Downloader;
use KadenceWPKadenceBlocksHasher;
-use KadenceWPKadenceBlocksMonologHandlerAbstractHandler;
-use KadenceWPKadenceBlocksMonologHandlerErrorLogHandler;
-use KadenceWPKadenceBlocksMonologHandlerNullHandler;
-use KadenceWPKadenceBlocksMonologLogger;
-use KadenceWPKadenceBlocksPsrLogLoggerInterface;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoImageDownloaderFileNameProcessor;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoImageDownloaderImageDownloader;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoImageDownloaderSanitizationContractsSanitizer;
use KadenceWPKadenceBlocksStellarWPProphecyMonorepoImageDownloaderSanitizationSanitizersWPFileNameSanitizer;
-use KadenceWPKadenceBlocksStellarWPProphecyMonorepoLogFormattersColoredLineFormatter;
-use KadenceWPKadenceBlocksStellarWPProphecyMonorepoLogLogLevel;
use KadenceWPKadenceBlocksSymfonyComponentHttpClientHttpClient;
use KadenceWPKadenceBlocksSymfonyComponentStringSluggerAsciiSlugger;
use KadenceWPKadenceBlocksSymfonyComponentStringSluggerSluggerInterface;
@@ -30,58 +23,18 @@
$this->container->bind( HttpClientInterface::class, HttpClient::create() );
$this->register_hasher();
- $this->register_logging();
$this->register_cache_primer();
$this->register_image_downloader();
}
private function register_hasher(): void {
$this->container->when( Hasher::class )
- ->needs( '$algo' )
- ->give( static function (): string {
- return PHP_VERSION_ID >= 80100 ? 'xxh128' : 'md5';
- } );
- }
-
- private function register_logging(): void {
- // Enable logging to the error log if WP_DEBUG is enabled and error_log is not listed in the php.ini/fpm disable_functions directive.
- if ( defined( 'WP_DEBUG' ) && WP_DEBUG && function_exists( 'error_log' ) ) {
- /**
- * Filter the log level to use when debugging.
- *
- * @param string $log_level One of: debug, info, notice, warning, error, critical, alert, emergency
- */
- $log_level = apply_filters( 'kadence_blocks_image_download_log_level', 'debug' );
-
- $this->container->when( ColoredLineFormatter::class )
- ->needs( '$dateFormat' )
- ->give( 'd/M/Y:H:i:s O' );
-
- $this->container->when( AbstractHandler::class )
- ->needs( '$level' )
- ->give( LogLevel::fromName( $log_level ) );
-
- $this->container->when( ErrorLogHandler::class )
- ->needs( '$level' )
- ->give( LogLevel::fromName( $log_level ) );
-
- $this->container->bind( LoggerInterface::class, function () {
- $logger = new Logger( 'kadence' );
- $handler = $this->container->get( ErrorLogHandler::class );
- $handler->setFormatter( $this->container->get( ColoredLineFormatter::class ) );
- $logger->pushHandler( $handler );
-
- return $logger;
- } );
- } else {
- // Disable logging.
- $this->container->bind( LoggerInterface::class, static function () {
- $logger = new Logger( 'null' );
- $logger->pushHandler( new NullHandler() );
-
- return $logger;
- } );
- }
+ ->needs( '$algo' )
+ ->give(
+ static function (): string {
+ return PHP_VERSION_ID >= 80100 ? 'xxh128' : 'md5';
+ }
+ );
}
private function register_cache_primer(): void {
@@ -102,12 +55,12 @@
$cache_duration = absint( apply_filters( 'kadence_blocks_cache_primer_cache_duration', HOUR_IN_SECONDS ) );
$this->container->when( Cache_Primer::class )
- ->needs( '$batch_size' )
- ->give( $batch_size );
+ ->needs( '$batch_size' )
+ ->give( $batch_size );
$this->container->when( Cache_Primer::class )
- ->needs( '$cache_duration' )
- ->give( $cache_duration );
+ ->needs( '$cache_duration' )
+ ->give( $cache_duration );
$this->container->singleton( Cache_Primer::class, Cache_Primer::class );
}
@@ -121,13 +74,15 @@
// Configure the allowed file extensions that are allowed to be processed.
$this->container->when( FileNameProcessor::class )
- ->needs( '$allowed_extensions' )
- ->give( [
- 'jpg' => true,
- 'jpeg' => true,
- 'webp' => true,
- 'png' => true,
- ] );
+ ->needs( '$allowed_extensions' )
+ ->give(
+ [
+ 'jpg' => true,
+ 'jpeg' => true,
+ 'webp' => true,
+ 'png' => true,
+ ]
+ );
/**
* Filter how many concurrent download requests we will open at once before we attempt to save
@@ -140,8 +95,7 @@
$batch_size = absint( apply_filters( 'kadence_blocks_image_download_batch_size', 200 ) );
$this->container->when( ImageDownloader::class )
- ->needs( '$batch_size' )
- ->give( $batch_size );
+ ->needs( '$batch_size' )
+ ->give( $batch_size );
}
-
}
--- a/kadence-blocks/includes/resources/Log/Log_Provider.php
+++ b/kadence-blocks/includes/resources/Log/Log_Provider.php
@@ -0,0 +1,74 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksLog;
+
+use KadenceWPKadenceBlocksMonologHandlerAbstractHandler;
+use KadenceWPKadenceBlocksMonologHandlerErrorLogHandler;
+use KadenceWPKadenceBlocksMonologHandlerNullHandler;
+use KadenceWPKadenceBlocksMonologLogger;
+use KadenceWPKadenceBlocksPsrLogLoggerInterface;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoLogFormattersColoredLineFormatter;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoLogLogLevel;
+
+final class Log_Provider extends Provider {
+
+ /**
+ * @inheritDoc
+ */
+ public function register(): void {
+ // Enable logging to the error log if WP_DEBUG is enabled and error_log is not listed in the php.ini/fpm disable_functions directive.
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG && function_exists( 'error_log' ) ) {
+ /**
+ * Filter the log level to use when debugging.
+ *
+ * @param string $log_level One of: debug, info, notice, warning, error, critical, alert, emergency
+ */
+ $log_level = apply_filters( 'kadence_blocks_image_download_log_level', 'debug' );
+
+ $this->container->when( ColoredLineFormatter::class )
+ ->needs( '$dateFormat' )
+ ->give( 'd/M/Y:H:i:s O' );
+
+ $this->container->when( AbstractHandler::class )
+ ->needs( '$level' )
+ ->give( LogLevel::fromName( $log_level ) );
+
+ $this->container->when( ErrorLogHandler::class )
+ ->needs( '$level' )
+ ->give( LogLevel::fromName( $log_level ) );
+
+ $this->container->bind(
+ LoggerInterface::class,
+ function () {
+ $logger = new Logger( 'kadence' );
+ $handler = $this->container->get( ErrorLogHandler::class );
+ $handler->setFormatter( $this->container->get( ColoredLineFormatter::class ) );
+ $logger->pushHandler( $handler );
+
+ // Prefix logs.
+ $logger->pushProcessor(
+ static function ( array $record ): array {
+ $record['message'] = '[Kadence Blocks]: ' . $record['message'];
+
+ return $record;
+ }
+ );
+
+ return $logger;
+ }
+ );
+ } else {
+ // Disable logging.
+ $this->container->bind(
+ LoggerInterface::class,
+ static function () {
+ $logger = new Logger( 'null' );
+ $logger->pushHandler( new NullHandler() );
+
+ return $logger;
+ }
+ );
+ }
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Analysis_Registry.php
+++ b/kadence-blocks/includes/resources/Optimizer/Analysis_Registry.php
@@ -0,0 +1,145 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizer;
+
+use InvalidArgumentException;
+use KadenceWPKadenceBlocksOptimizerPathPath;
+use KadenceWPKadenceBlocksOptimizerPathPath_Factory;
+use KadenceWPKadenceBlocksOptimizerResponseWebsiteAnalysis;
+use KadenceWPKadenceBlocksOptimizerStoreContractsStore;
+
+/**
+ * A registry to fetch custom data from the WebsiteAnalysis DTO.
+ *
+ * @phpstan-type HtmlClassHeightMap array<string, float> Class attribute => height in pixels.
+ */
+class Analysis_Registry {
+
+ private const SECTIONS = 'sections';
+
+ /**
+ * Memoization cache.
+ *
+ * @var array<string, mixed>
+ */
+ private array $cache = [];
+ private ?Path $path = null;
+ private ?WebsiteAnalysis $analysis = null;
+ private Store $store;
+ private bool $is_mobile;
+
+ public function __construct(
+ Store $store,
+ Path_Factory $path_factory,
+ bool $is_mobile
+ ) {
+ $this->store = $store;
+ $this->is_mobile = $is_mobile;
+
+ try {
+ $this->path = $path_factory->make();
+ } catch ( InvalidArgumentException $e ) {
+ $this->path = null;
+ }
+ }
+
+ /**
+ * Flush the memoization cache.
+ *
+ * @return void
+ */
+ public function flush(): void {
+ $this->analysis = null;
+ $this->cache = [];
+ }
+
+ /**
+ * Build the WebsiteAnalysis DTO based on the current path.
+ *
+ * @return WebsiteAnalysis|null
+ */
+ public function get_analysis(): ?WebsiteAnalysis {
+ if ( $this->analysis !== null ) {
+ return $this->analysis;
+ }
+
+ if ( ! $this->path ) {
+ return null;
+ }
+
+ $this->analysis = $this->store->get( $this->path );
+
+ return $this->analysis;
+ }
+
+ /**
+ * Check if this request is optimized.
+ *
+ * @return bool
+ */
+ public function is_optimized(): bool {
+ return $this->get_analysis() instanceof WebsiteAnalysis;
+ }
+
+ /**
+ * Get the above the fold background images for the current viewport.
+ *
+ * @return string[]
+ */
+ public function get_background_images(): array {
+ $analysis = $this->get_analysis();
+
+ return $analysis
+ ? ( $this->is_mobile ? $analysis->mobile->backgroundImages : $analysis->desktop->backgroundImages )
+ : [];
+ }
+
+ /**
+ * Get the list of the above the fold image URLs.
+ *
+ * @return string[]
+ */
+ public function get_critical_images(): array {
+ $analysis = $this->get_analysis();
+
+ return $analysis
+ ? ( $this->is_mobile ? $analysis->mobile->criticalImages : $analysis->desktop->criticalImages )
+ : [];
+ }
+
+ /**
+ * For the current viewport, get all the "below the fold" sections that have a height
+ * greater than 0.
+ *
+ * @return HtmlClassHeightMap
+ */
+ public function get_valid_sections(): array {
+ if ( isset( $this->cache[ self::SECTIONS ] ) ) {
+ return $this->cache[ self::SECTIONS ];
+ }
+
+ $analysis = $this->get_analysis();
+
+ if ( ! $analysis ) {
+ $this->cache[ self::SECTIONS ] = [];
+
+ return [];
+ }
+
+ $sections = $this->is_mobile
+ ? $analysis->mobile->getBelowTheFoldSections()
+ : $analysis->desktop->getBelowTheFoldSections();
+
+ $results = [];
+
+ foreach ( $sections as $section ) {
+ if ( $section->height > 0 ) {
+ $results[ $section->className ] = $section->height;
+ }
+ }
+
+ $this->cache[ self::SECTIONS ] = $results;
+
+ return $this->cache[ self::SECTIONS ];
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Asset/Provider.php
+++ b/kadence-blocks/includes/resources/Optimizer/Asset/Provider.php
@@ -0,0 +1,25 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerAsset;
+
+use KadenceWPKadenceBlocksOptimizerAsset_Loader;
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider as Provider_Contract;
+
+final class Provider extends Provider_Contract {
+
+ public function register(): void {
+ $this->container->singleton( Asset_Loader::class, Asset_Loader::class );
+
+ add_action(
+ 'enqueue_block_editor_assets',
+ $this->container->callback( Asset_Loader::class, 'enqueue_block_editor_scripts' ),
+ 20,
+ 0
+ );
+
+ add_action(
+ 'admin_enqueue_scripts',
+ $this->container->callback( Asset_Loader::class, 'enqueue_post_list_table' )
+ );
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Asset_Loader.php
+++ b/kadence-blocks/includes/resources/Optimizer/Asset_Loader.php
@@ -0,0 +1,97 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizer;
+
+use KadenceWPKadenceBlocksAssetAsset;
+use KadenceWPKadenceBlocksOptimizerNonceNonce;
+use KadenceWPKadenceBlocksOptimizerTranslationText_Repository;
+
+final class Asset_Loader {
+
+ public const OPTIMIZER_SCRIPT_HANDLE = 'kadence-blocks-admin-optimizer';
+ public const POST_LIST_STYLE_HANDLE = 'kadence-blocks-post-list-table';
+
+ private Asset $asset;
+ private Text_Repository $text_repository;
+ private Nonce $nonce;
+
+ /**
+ * @param Asset $asset
+ * @param Text_Repository $text_repository
+ * @param Nonce $nonce
+ */
+ public function __construct(
+ Asset $asset,
+ Text_Repository $text_repository,
+ Nonce $nonce
+ ) {
+ $this->asset = $asset;
+ $this->text_repository = $text_repository;
+ $this->nonce = $nonce;
+ }
+
+ /**
+ * Enqueue Optimizer scripts when editing a post in Gutenberg.
+ *
+ * @action enqueue_block_editor_assets
+ *
+ * @return void
+ */
+ public function enqueue_block_editor_scripts(): void {
+ $this->enqueue_optimizer_js();
+ }
+
+ /**
+ * Enqueue Optimizer scripts and the post list table column styles.
+ *
+ * @action admin_enqueue_scripts
+ *
+ * @param string $hook The current admin screen.
+ *
+ * @return void
+ */
+ public function enqueue_post_list_table( string $hook ): void {
+ if ( 'edit.php' !== $hook ) {
+ return;
+ }
+
+ wp_register_style(
+ self::POST_LIST_STYLE_HANDLE,
+ $this->asset->get_url( 'includes/assets/css/post-list-table.min.css' ),
+ [],
+ KADENCE_BLOCKS_VERSION
+ );
+
+ wp_enqueue_style( self::POST_LIST_STYLE_HANDLE );
+
+ $this->enqueue_optimizer_js();
+ }
+
+ /**
+ * Enqueue the Optimizer scripts.
+ *
+ * @action admin_enqueue_scripts
+ * @action enqueue_block_editor_assets
+ *
+ * @return void
+ */
+ private function enqueue_optimizer_js(): void {
+ $this->asset->enqueue_script( self::OPTIMIZER_SCRIPT_HANDLE, 'dist/kadence-optimizer' );
+
+ // Add post list table optimizer status translations.
+ wp_localize_script(
+ self::OPTIMIZER_SCRIPT_HANDLE,
+ 'kbOptimizerL10n',
+ $this->text_repository->all()
+ );
+
+ // TODO: probably move this into a new script.
+ wp_localize_script(
+ self::OPTIMIZER_SCRIPT_HANDLE,
+ 'kbOptimizer',
+ [
+ 'token' => $this->nonce->create(),
+ ]
+ );
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Database/Optimizer_Query.php
+++ b/kadence-blocks/includes/resources/Optimizer/Database/Optimizer_Query.php
@@ -0,0 +1,15 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerDatabase;
+
+use KadenceWPKadenceBlocksDatabaseQuery;
+use KadenceWPKadenceBlocksOptimizerOptimizer_Provider;
+
+/**
+ * A query builder wrapper for the Optimizer table.
+ *
+ * @see Optimizer_Provider::register_optimizer_query()
+ */
+final class Optimizer_Query extends Query {
+
+}
--- a/kadence-blocks/includes/resources/Optimizer/Database/Optimizer_Table.php
+++ b/kadence-blocks/includes/resources/Optimizer/Database/Optimizer_Table.php
@@ -0,0 +1,66 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerDatabase;
+
+use KadenceWPKadenceBlocksStellarWPDBDatabaseExceptionsDatabaseQueryException;
+use KadenceWPKadenceBlocksStellarWPSchemaTablesContractsTable;
+
+/**
+ * The optimizer database table to store optimizer data.
+ */
+final class Optimizer_Table extends Table {
+
+ public const SCHEMA_VERSION = '2.0.4';
+
+ /**
+ * @var string The base table name.
+ */
+ protected static $base_table_name = 'kb_optimizer';
+
+ /**
+ * @var string The organizational group this table belongs to.
+ */
+ protected static $group = 'kb';
+
+ /**
+ * @var string|null The slug used to identify the custom table.
+ */
+ protected static $schema_slug = 'optimizer';
+
+ /**
+ * @var string The field that uniquely identifies a row in the table.
+ */
+ protected static $uid_column = 'path_hash';
+
+ /**
+ * Overload the update method to first drop the database as this is a temporary table.
+ *
+ * @throws DatabaseQueryException If any of the queries fail.
+ *
+ * @return string[]
+ */
+ public function update() {
+ if ( $this->exists() ) {
+ $this->drop();
+ }
+
+ return parent::update();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function get_definition(): string {
+ global $wpdb;
+ $table_name = self::table_name();
+ $charset_collate = $wpdb->get_charset_collate();
+
+ return "
+ CREATE TABLE `$table_name` (
+ path_hash CHAR(64) PRIMARY KEY COMMENT 'SHA-256 hash of the relative path of the URL used as a unique key for fast lookups',
+ path TEXT NOT NULL COMMENT 'The relative path of the URL, stored for reference and debugging',
+ analysis LONGTEXT NOT NULL COMMENT 'Serialized or JSON-encoded analysis data associated with the path'
+ ) {$charset_collate};
+ ";
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Database/Provider.php
+++ b/kadence-blocks/includes/resources/Optimizer/Database/Provider.php
@@ -0,0 +1,29 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerDatabase;
+
+use KadenceWPKadenceBlocksStellarWPProphecyMonorepoContainerContractsProvider as Provider_Contract;
+
+final class Provider extends Provider_Contract {
+
+ public function register(): void {
+ $this->register_optimizer_query();
+ $this->register_viewport_query();
+ }
+
+ private function register_optimizer_query(): void {
+ $this->container->singleton( Optimizer_Query::class, Optimizer_Query::class );
+
+ $this->container->when( Optimizer_Query::class )
+ ->needs( '$table' )
+ ->give( static fn(): string => Optimizer_Table::table_name( false ) );
+ }
+
+ private function register_viewport_query(): void {
+ $this->container->singleton( Viewport_Query::class, Viewport_Query::class );
+
+ $this->container->when( Viewport_Query::class )
+ ->needs( '$table' )
+ ->give( static fn(): string => Viewport_Hash_Table::table_name( false ) );
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Database/Viewport_Hash_Table.php
+++ b/kadence-blocks/includes/resources/Optimizer/Database/Viewport_Hash_Table.php
@@ -0,0 +1,72 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerDatabase;
+
+use KadenceWPKadenceBlocksOptimizerEnumsViewport;
+use KadenceWPKadenceBlocksStellarWPDBDatabaseExceptionsDatabaseQueryException;
+use KadenceWPKadenceBlocksStellarWPSchemaTablesContractsTable;
+
+/**
+ * Database table to store viewport-specific HTML hashes for each URL path.
+ */
+final class Viewport_Hash_Table extends Table {
+
+ public const SCHEMA_VERSION = '1.0.4';
+
+ /**
+ * @var string The base table name.
+ */
+ protected static $base_table_name = 'kb_optimizer_viewport_hashes';
+
+ /**
+ * @var string The organizational group this table belongs to.
+ */
+ protected static $group = 'kb';
+
+ /**
+ * @var string|null The slug used to identify the custom table.
+ */
+ protected static $schema_slug = 'viewport_hashes';
+
+ /**
+ * @var string[] The fields that uniquely identify a row in the table (composite key).
+ */
+ protected static $uid_column = 'path_hash';
+
+ /**
+ * Overload the update method to drop the database first (optional, like your Optimizer_Table).
+ *
+ * @throws DatabaseQueryException If any of the queries fail.
+ *
+ * @return string[]
+ */
+ public function update() {
+ if ( $this->exists() ) {
+ $this->drop();
+ }
+
+ return parent::update();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function get_definition(): string {
+ global $wpdb;
+ $table_name = self::table_name();
+ $charset_collate = $wpdb->get_charset_collate();
+
+ // Build a 'desktop', 'mobile' ENUM input.
+ $enum_values = sprintf( "'%s', '%s'", Viewport::desktop()->value(), Viewport::mobile()->value() );
+
+ return "
+ CREATE TABLE `$table_name` (
+ path_hash CHAR(64) NOT NULL COMMENT 'SHA-256 hash of the path (via $wp->request), identifies the URL',
+ viewport ENUM($enum_values) NOT NULL COMMENT 'The viewport/device identifier',
+ html_hash VARCHAR(512) NOT NULL COMMENT 'A composite of hashes separated by | in a key:SHA-256 value of the HTML markup for this viewport',
+ PRIMARY KEY (path_hash, viewport),
+ KEY idx_html_hash (html_hash)
+ ) {$charset_collate};
+ ";
+ }
+}
--- a/kadence-blocks/includes/resources/Optimizer/Database/Viewport_Query.php
+++ b/kadence-blocks/includes/resources/Optimizer/Database/Viewport_Query.php
@@ -0,0 +1,15 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerDatabase;
+
+use KadenceWPKadenceBlocksDatabaseQuery;
+use KadenceWPKadenceBlocksOptimizerOptimizer_Provider;
+
+/**
+ * A query builder wrapper for the Viewport_Hash table.
+ *
+ * @see Optimizer_Provider::register_viewport_query()
+ */
+final class Viewport_Query extends Query {
+
+}
--- a/kadence-blocks/includes/resources/Optimizer/Enums/Viewport.php
+++ b/kadence-blocks/includes/resources/Optimizer/Enums/Viewport.php
@@ -0,0 +1,47 @@
+<?php declare( strict_types=1 );
+
+namespace KadenceWPKadenceBlocksOptimizerEnums;
+
+/**
+ * A pseudo Viewport enum.
+ */
+final class Viewport {
+
+ public const DESKTOP = 'desktop';
+ public const MOBILE = 'mobile';
+
+ /**
+ * T