initial
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles block functionality.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Block class.
|
||||
*/
|
||||
class GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = '';
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
static::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to check.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) static::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block CSS.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
*/
|
||||
public static function get_css( $attributes ) {
|
||||
$id = $attributes['uniqueId'] ?? '';
|
||||
|
||||
if ( ! $id ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Store this block ID in memory.
|
||||
static::store_block_id( $id );
|
||||
|
||||
return apply_filters(
|
||||
'generateblocks_block_css',
|
||||
$attributes['css'] ?? '',
|
||||
[
|
||||
'attributes' => $attributes ?? [],
|
||||
'block_name' => static::$block_name ?? '',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Button Container block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Button Container related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Button_Container {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
private static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Keep track of CSS we want to output once per block type.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $singular_css_added = false;
|
||||
|
||||
/**
|
||||
* Block defaults.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [
|
||||
'alignment' => '',
|
||||
'alignmentTablet' => '',
|
||||
'alignmentMobile' => '',
|
||||
'marginTop' => '',
|
||||
'marginRight' => '',
|
||||
'marginBottom' => '',
|
||||
'marginLeft' => '',
|
||||
'marginTopTablet' => '',
|
||||
'marginRightTablet' => '',
|
||||
'marginBottomTablet' => '',
|
||||
'marginLeftTablet' => '',
|
||||
'marginTopMobile' => '',
|
||||
'marginRightMobile' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginLeftMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'stack' => false,
|
||||
'stackTablet' => false,
|
||||
'stackMobile' => false,
|
||||
'fillHorizontalSpace' => false,
|
||||
'fillHorizontalSpaceTablet' => false,
|
||||
'fillHorizontalSpaceMobile' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
self::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) self::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile our CSS data based on our block attributes.
|
||||
*
|
||||
* @param array $attributes Our block attributes.
|
||||
*/
|
||||
public static function get_css_data( $attributes ) {
|
||||
$css = new GenerateBlocks_Dynamic_CSS();
|
||||
$desktop_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_only_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$mobile_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$css_data = [];
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['buttonContainer']
|
||||
);
|
||||
|
||||
$id = $attributes['uniqueId'];
|
||||
$blockVersion = ! empty( $settings['blockVersion'] ) ? $settings['blockVersion'] : 1;
|
||||
|
||||
// Only add this CSS once.
|
||||
if ( ! self::$singular_css_added ) {
|
||||
$css->set_selector( '.gb-button-wrapper' );
|
||||
$css->add_property( 'display', 'flex' );
|
||||
$css->add_property( 'flex-wrap', 'wrap' );
|
||||
$css->add_property( 'align-items', 'flex-start' );
|
||||
$css->add_property( 'justify-content', 'flex-start' );
|
||||
$css->add_property( 'clear', 'both' );
|
||||
|
||||
do_action(
|
||||
'generateblocks_block_one_time_css_data',
|
||||
'button-container',
|
||||
$settings,
|
||||
$css
|
||||
);
|
||||
|
||||
self::$singular_css_added = true;
|
||||
}
|
||||
|
||||
// Map deprecated settings.
|
||||
$settings = GenerateBlocks_Map_Deprecated_Attributes::map_attributes( $settings );
|
||||
|
||||
$css->set_selector( '.gb-button-wrapper-' . $id );
|
||||
$css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignment'] ) );
|
||||
generateblocks_add_spacing_css( $css, $settings );
|
||||
|
||||
$stack_desktop = $desktop_css;
|
||||
$stack_tablet_only = $tablet_only_css;
|
||||
|
||||
if ( $blockVersion < 2 ) {
|
||||
$stack_desktop = $css;
|
||||
$stack_tablet_only = $tablet_css;
|
||||
}
|
||||
|
||||
if ( $settings['stack'] ) {
|
||||
$stack_desktop->set_selector( '.gb-button-wrapper-' . $id );
|
||||
$stack_desktop->add_property( 'flex-direction', 'column' );
|
||||
$stack_desktop->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['alignment'] ) );
|
||||
}
|
||||
|
||||
if ( $settings['fillHorizontalSpace'] ) {
|
||||
$stack_desktop->set_selector( '.gb-button-wrapper-' . $id . ' > .gb-button' );
|
||||
$stack_desktop->add_property( 'flex', '1' );
|
||||
}
|
||||
|
||||
if ( $settings['stack'] && $settings['fillHorizontalSpace'] ) {
|
||||
$stack_desktop->add_property( 'width', '100%' );
|
||||
$stack_desktop->add_property( 'box-sizing', 'border-box' );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-button-wrapper-' . $id );
|
||||
$tablet_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
generateblocks_add_spacing_css( $tablet_css, $settings, 'Tablet' );
|
||||
|
||||
if ( $settings['stackTablet'] ) {
|
||||
$stack_tablet_only->set_selector( '.gb-button-wrapper-' . $id );
|
||||
$stack_tablet_only->add_property( 'flex-direction', 'column' );
|
||||
$stack_tablet_only->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
}
|
||||
|
||||
if ( $settings['fillHorizontalSpaceTablet'] ) {
|
||||
$stack_tablet_only->set_selector( '.gb-button-wrapper-' . $id . ' > .gb-button' );
|
||||
$stack_tablet_only->add_property( 'flex', '1' );
|
||||
}
|
||||
|
||||
if ( $settings['stackTablet'] && $settings['fillHorizontalSpaceTablet'] ) {
|
||||
$stack_tablet_only->add_property( 'width', '100%' );
|
||||
$stack_tablet_only->add_property( 'box-sizing', 'border-box' );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-button-wrapper-' . $id );
|
||||
$mobile_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
generateblocks_add_spacing_css( $mobile_css, $settings, 'Mobile' );
|
||||
|
||||
if ( $settings['stackMobile'] ) {
|
||||
$mobile_css->add_property( 'flex-direction', 'column' );
|
||||
$mobile_css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
}
|
||||
|
||||
if ( $settings['fillHorizontalSpaceMobile'] ) {
|
||||
$mobile_css->set_selector( '.gb-button-wrapper-' . $id . ' > .gb-button' );
|
||||
$mobile_css->add_property( 'flex', '1' );
|
||||
}
|
||||
|
||||
if ( $settings['stackMobile'] && $settings['fillHorizontalSpaceMobile'] ) {
|
||||
$mobile_css->add_property( 'width', '100%' );
|
||||
$mobile_css->add_property( 'box-sizing', 'border-box' );
|
||||
}
|
||||
|
||||
// Store this block ID in memory.
|
||||
self::store_block_id( $id );
|
||||
|
||||
/**
|
||||
* Do generateblocks_block_css_data hook
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $name The name of our block.
|
||||
* @param array $settings The settings for the current block.
|
||||
* @param object $css Our desktop/main CSS data.
|
||||
* @param object $desktop_css Our desktop only CSS data.
|
||||
* @param object $tablet_css Our tablet CSS data.
|
||||
* @param object $tablet_only_css Our tablet only CSS data.
|
||||
* @param object $mobile_css Our mobile CSS data.
|
||||
*/
|
||||
do_action(
|
||||
'generateblocks_block_css_data',
|
||||
'button-container',
|
||||
$settings,
|
||||
$css,
|
||||
$desktop_css,
|
||||
$tablet_css,
|
||||
$tablet_only_css,
|
||||
$mobile_css
|
||||
);
|
||||
|
||||
return [
|
||||
'main' => $css->css_output(),
|
||||
'desktop' => $desktop_css->css_output(),
|
||||
'tablet' => $tablet_css->css_output(),
|
||||
'tablet_only' => $tablet_only_css->css_output(),
|
||||
'mobile' => $mobile_css->css_output(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
if ( ! isset( $attributes['isDynamic'] ) || ! $attributes['isDynamic'] ) {
|
||||
// Add styles to this block if needed.
|
||||
$content = generateblocks_maybe_add_block_css(
|
||||
$content,
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Button_Container',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
if ( isset( $block->parsed_block['innerBlocks'] ) ) {
|
||||
$button_count = apply_filters(
|
||||
'generateblocks_button_count',
|
||||
count( (array) $block->parsed_block['innerBlocks'] ),
|
||||
$attributes,
|
||||
$block
|
||||
);
|
||||
|
||||
if ( 0 === $button_count ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['buttonContainer']
|
||||
);
|
||||
|
||||
$classNames = array(
|
||||
'gb-button-wrapper',
|
||||
'gb-button-wrapper-' . $settings['uniqueId'],
|
||||
);
|
||||
|
||||
if ( ! empty( $settings['className'] ) ) {
|
||||
$classNames[] = $settings['className'];
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Button_Container',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<div %s>',
|
||||
generateblocks_attr(
|
||||
'button-container',
|
||||
array(
|
||||
'id' => isset( $settings['anchor'] ) ? $settings['anchor'] : null,
|
||||
'class' => implode( ' ', $classNames ),
|
||||
),
|
||||
$settings,
|
||||
$block
|
||||
)
|
||||
);
|
||||
|
||||
$output .= $content;
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Button block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Button related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Button {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
private static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Keep track of CSS we want to output once per block type.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $singular_css_added = false;
|
||||
|
||||
/**
|
||||
* Block defaults.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [
|
||||
'backgroundColor' => '',
|
||||
'backgroundColorHover' => '',
|
||||
'backgroundColorCurrent' => '',
|
||||
'textColor' => '',
|
||||
'textColorHover' => '',
|
||||
'textColorCurrent' => '',
|
||||
'fontFamilyFallback' => '',
|
||||
'googleFont' => false,
|
||||
'googleFontVariants' => '',
|
||||
'icon' => '',
|
||||
'hasIcon' => false,
|
||||
'iconLocation' => 'left',
|
||||
'removeText' => false,
|
||||
'ariaLabel' => '',
|
||||
'gradient' => false,
|
||||
'gradientDirection' => '',
|
||||
'gradientColorOne' => '',
|
||||
'gradientColorOneOpacity' => '',
|
||||
'gradientColorStopOne' => '',
|
||||
'gradientColorTwo' => '',
|
||||
'gradientColorTwoOpacity' => '',
|
||||
'gradientColorStopTwo' => '',
|
||||
'hasButtonContainer' => false,
|
||||
'variantRole' => '',
|
||||
'buttonType' => 'link',
|
||||
// Deprecated attributes.
|
||||
'backgroundColorOpacity' => 1,
|
||||
'backgroundColorHoverOpacity' => 1,
|
||||
'borderColorHoverOpacity' => 1,
|
||||
'borderColorOpacity' => 1,
|
||||
'fontSize' => false,
|
||||
'fontSizeTablet' => false,
|
||||
'fontSizeMobile' => false,
|
||||
'fontSizeUnit' => 'px',
|
||||
'letterSpacing' => '',
|
||||
'letterSpacingTablet' => '',
|
||||
'letterSpacingMobile' => '',
|
||||
'fontWeight' => '',
|
||||
'textTransform' => '',
|
||||
'alignment' => '',
|
||||
'alignmentTablet' => '',
|
||||
'alignmentMobile' => '',
|
||||
'fontFamily' => '',
|
||||
'iconPaddingTop' => '',
|
||||
'iconPaddingRight' => '0.5',
|
||||
'iconPaddingBottom' => '',
|
||||
'iconPaddingLeft' => '',
|
||||
'iconPaddingTopTablet' => '',
|
||||
'iconPaddingRightTablet' => '',
|
||||
'iconPaddingBottomTablet' => '',
|
||||
'iconPaddingLeftTablet' => '',
|
||||
'iconPaddingTopMobile' => '',
|
||||
'iconPaddingRightMobile' => '',
|
||||
'iconPaddingBottomMobile' => '',
|
||||
'iconPaddingLeftMobile' => '',
|
||||
'iconPaddingUnit' => 'em',
|
||||
'iconSize' => 1,
|
||||
'iconSizeTablet' => '',
|
||||
'iconSizeMobile' => '',
|
||||
'iconSizeUnit' => 'em',
|
||||
'borderColor' => '',
|
||||
'borderColorHover' => '',
|
||||
'borderColorCurrent' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
self::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) self::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile our CSS data based on our block attributes.
|
||||
*
|
||||
* @param array $attributes Our block attributes.
|
||||
*/
|
||||
public static function get_css_data( $attributes ) {
|
||||
$css = new GenerateBlocks_Dynamic_CSS();
|
||||
$desktop_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_only_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$mobile_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$css_data = [];
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['button']
|
||||
);
|
||||
|
||||
$id = $attributes['uniqueId'];
|
||||
$blockVersion = ! empty( $settings['blockVersion'] ) ? $settings['blockVersion'] : 1;
|
||||
|
||||
// Use legacy settings if needed.
|
||||
if ( $blockVersion < 2 ) {
|
||||
$settings = GenerateBlocks_Legacy_Attributes::get_settings( '1.4.0', 'button', $settings, $attributes );
|
||||
}
|
||||
|
||||
// Map deprecated settings.
|
||||
$settings = GenerateBlocks_Map_Deprecated_Attributes::map_attributes( $settings );
|
||||
|
||||
$selector = generateblocks_get_css_selector( 'button', $attributes );
|
||||
$use_visited_selector = generateblocks_use_visited_selector( 'button', $attributes );
|
||||
$using_global_style = isset( $settings['useGlobalStyle'] ) && $settings['useGlobalStyle'];
|
||||
|
||||
// Back-compatibility for when icon held a value.
|
||||
if ( $settings['icon'] ) {
|
||||
$settings['hasIcon'] = true;
|
||||
}
|
||||
|
||||
$gradientColorStopOneValue = '';
|
||||
$gradientColorStopTwoValue = '';
|
||||
|
||||
if ( $settings['gradient'] ) {
|
||||
if ( $settings['gradientColorOne'] && '' !== $settings['gradientColorStopOne'] ) {
|
||||
$gradientColorStopOneValue = ' ' . $settings['gradientColorStopOne'] . '%';
|
||||
}
|
||||
|
||||
if ( $settings['gradientColorTwo'] && '' !== $settings['gradientColorStopTwo'] ) {
|
||||
$gradientColorStopTwoValue = ' ' . $settings['gradientColorStopTwo'] . '%';
|
||||
}
|
||||
}
|
||||
|
||||
// Only add this CSS once.
|
||||
if ( ! self::$singular_css_added ) {
|
||||
// Singular CSS is no longer supported since 1.9.0.
|
||||
do_action(
|
||||
'generateblocks_block_one_time_css_data',
|
||||
'button',
|
||||
$settings,
|
||||
$css
|
||||
);
|
||||
|
||||
self::$singular_css_added = true;
|
||||
}
|
||||
|
||||
$visited_selector = $use_visited_selector
|
||||
? ', ' . $selector . ':visited'
|
||||
: '';
|
||||
|
||||
$css->set_selector( $selector . $visited_selector );
|
||||
generateblocks_add_layout_css( $css, $settings );
|
||||
generateblocks_add_sizing_css( $css, $settings );
|
||||
generateblocks_add_flex_child_css( $css, $settings );
|
||||
generateblocks_add_typography_css( $css, $settings );
|
||||
generateblocks_add_spacing_css( $css, $settings );
|
||||
generateblocks_add_border_css( $css, $settings );
|
||||
$css->add_property( 'background-color', generateblocks_hex2rgba( $settings['backgroundColor'], $settings['backgroundColorOpacity'] ) );
|
||||
$css->add_property( 'color', $settings['textColor'] );
|
||||
$css->add_property( 'text-decoration', 'none' );
|
||||
|
||||
if ( $settings['gradient'] ) {
|
||||
$css->add_property( 'background-image', 'linear-gradient(' . $settings['gradientDirection'] . 'deg, ' . generateblocks_hex2rgba( $settings['gradientColorOne'], $settings['gradientColorOneOpacity'] ) . $gradientColorStopOneValue . ', ' . generateblocks_hex2rgba( $settings['gradientColorTwo'], $settings['gradientColorTwoOpacity'] ) . $gradientColorStopTwoValue . ')' );
|
||||
}
|
||||
|
||||
if ( $blockVersion < 3 && ! $using_global_style ) {
|
||||
$css->add_property( 'display', 'inline-flex' );
|
||||
$css->add_property( 'align-items', 'center' );
|
||||
$css->add_property( 'justify-content', 'center' );
|
||||
$css->add_property( 'text-align', 'center' );
|
||||
}
|
||||
|
||||
$css->set_selector( $selector . ':hover, ' . $selector . ':active, ' . $selector . ':focus' );
|
||||
generateblocks_add_border_color_css( $css, $settings, 'Hover' );
|
||||
$css->add_property( 'background-color', generateblocks_hex2rgba( $settings['backgroundColorHover'], $settings['backgroundColorHoverOpacity'] ) );
|
||||
$css->add_property( 'color', $settings['textColorHover'] );
|
||||
|
||||
$visited_selector = $use_visited_selector
|
||||
? ', ' . $selector . '.gb-block-is-current:visited'
|
||||
: '';
|
||||
|
||||
$current_selector = sprintf(
|
||||
'%1$s.gb-block-is-current, %1$s.gb-block-is-current:hover, %1$s.gb-block-is-current:active, %1$s.gb-block-is-current:focus',
|
||||
$selector
|
||||
);
|
||||
|
||||
$css->set_selector( $current_selector . $visited_selector );
|
||||
generateblocks_add_border_color_css( $css, $settings, 'Current' );
|
||||
$css->add_property( 'background-color', $settings['backgroundColorCurrent'] );
|
||||
$css->add_property( 'color', $settings['textColorCurrent'] );
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$css->set_selector( $selector . ' .gb-icon' );
|
||||
|
||||
if ( $blockVersion < 4 ) {
|
||||
$css->add_property( 'font-size', $settings['iconSize'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
$css->add_property( 'line-height', '0' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 4 ) {
|
||||
// Need to check for blockVersion here instead of mapping as iconPaddingRight has a default.
|
||||
$css->add_property( 'padding', array( $settings['iconPaddingTop'], $settings['iconPaddingRight'], $settings['iconPaddingBottom'], $settings['iconPaddingLeft'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTop', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRight', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottom', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeft', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $blockVersion < 3 ) {
|
||||
$css->add_property( 'align-items', 'center' );
|
||||
$css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
|
||||
$css->set_selector( $selector . ' .gb-icon svg' );
|
||||
|
||||
if ( $blockVersion < 4 ) {
|
||||
$css->add_property( 'height', '1em' );
|
||||
$css->add_property( 'width', '1em' );
|
||||
}
|
||||
|
||||
$css->add_property( 'width', generateblocks_get_array_attribute_value( 'width', $settings['iconStyles'] ) );
|
||||
$css->add_property( 'height', generateblocks_get_array_attribute_value( 'height', $settings['iconStyles'] ) );
|
||||
$css->add_property( 'fill', 'currentColor' );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( $selector );
|
||||
generateblocks_add_layout_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_sizing_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_flex_child_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_typography_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_spacing_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_border_css( $tablet_css, $settings, 'Tablet' );
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$tablet_css->set_selector( $selector . ' .gb-icon' );
|
||||
|
||||
if ( $blockVersion < 4 ) {
|
||||
$tablet_css->add_property( 'font-size', $settings['iconSizeTablet'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 4 ) {
|
||||
$tablet_css->add_property( 'padding', array( $settings['iconPaddingTopTablet'], $settings['iconPaddingRightTablet'], $settings['iconPaddingBottomTablet'], $settings['iconPaddingLeftTablet'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$tablet_css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTopTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRightTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottomTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeftTablet', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( $selector . ' .gb-icon svg' );
|
||||
$tablet_css->add_property( 'width', generateblocks_get_array_attribute_value( 'widthTablet', $settings['iconStyles'] ) );
|
||||
$tablet_css->add_property( 'height', generateblocks_get_array_attribute_value( 'heightTablet', $settings['iconStyles'] ) );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( $selector );
|
||||
generateblocks_add_layout_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_sizing_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_flex_child_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_typography_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_spacing_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_border_css( $mobile_css, $settings, 'Mobile' );
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$mobile_css->set_selector( $selector . ' .gb-icon' );
|
||||
|
||||
if ( $blockVersion < 4 ) {
|
||||
$mobile_css->add_property( 'font-size', $settings['iconSizeMobile'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 4 ) {
|
||||
$mobile_css->add_property( 'padding', array( $settings['iconPaddingTopMobile'], $settings['iconPaddingRightMobile'], $settings['iconPaddingBottomMobile'], $settings['iconPaddingLeftMobile'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$mobile_css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTopMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRightMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottomMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeftMobile', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( $selector . ' .gb-icon svg' );
|
||||
$mobile_css->add_property( 'width', generateblocks_get_array_attribute_value( 'widthMobile', $settings['iconStyles'] ) );
|
||||
$mobile_css->add_property( 'height', generateblocks_get_array_attribute_value( 'heightMobile', $settings['iconStyles'] ) );
|
||||
}
|
||||
|
||||
// Store this block ID in memory.
|
||||
self::store_block_id( $id );
|
||||
|
||||
/**
|
||||
* Do generateblocks_block_css_data hook
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $name The name of our block.
|
||||
* @param array $settings The settings for the current block.
|
||||
* @param object $css Our desktop/main CSS data.
|
||||
* @param object $desktop_css Our desktop only CSS data.
|
||||
* @param object $tablet_css Our tablet CSS data.
|
||||
* @param object $tablet_only_css Our tablet only CSS data.
|
||||
* @param object $mobile_css Our mobile CSS data.
|
||||
*/
|
||||
do_action(
|
||||
'generateblocks_block_css_data',
|
||||
'button',
|
||||
$settings,
|
||||
$css,
|
||||
$desktop_css,
|
||||
$tablet_css,
|
||||
$tablet_only_css,
|
||||
$mobile_css
|
||||
);
|
||||
|
||||
return [
|
||||
'main' => $css->css_output(),
|
||||
'desktop' => $desktop_css->css_output(),
|
||||
'tablet' => $tablet_css->css_output(),
|
||||
'tablet_only' => $tablet_only_css->css_output(),
|
||||
'mobile' => $mobile_css->css_output(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
if ( ! isset( $attributes['hasUrl'] ) && strpos( trim( $content ), '<a' ) === 0 ) {
|
||||
$attributes['hasUrl'] = true;
|
||||
}
|
||||
|
||||
if ( ! isset( $attributes['useDynamicData'] ) || ! $attributes['useDynamicData'] ) {
|
||||
// Add styles to this block if needed.
|
||||
$content = generateblocks_maybe_add_block_css(
|
||||
$content,
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Button',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$allow_empty_content = false;
|
||||
|
||||
// Add an attribute showing we're working with the Button block.
|
||||
$attributes['isButton'] = true;
|
||||
|
||||
if ( empty( $attributes['dynamicContentType'] ) ) {
|
||||
$dynamic_content = GenerateBlocks_Dynamic_Content::get_static_content( $content );
|
||||
|
||||
if ( ! empty( $attributes['hasIcon'] ) && ! empty( $attributes['removeText'] ) ) {
|
||||
// Allow icon-only items to continue.
|
||||
$allow_empty_content = true;
|
||||
}
|
||||
} else {
|
||||
$dynamic_content = GenerateBlocks_Dynamic_Content::get_content( $attributes, $block );
|
||||
}
|
||||
|
||||
if ( ! $dynamic_content && '0' !== $dynamic_content && ! $allow_empty_content ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['button']
|
||||
);
|
||||
|
||||
$classNames = array(
|
||||
'gb-button',
|
||||
'gb-button-' . $settings['uniqueId'],
|
||||
);
|
||||
|
||||
if ( ! empty( $settings['className'] ) ) {
|
||||
$classNames[] = $settings['className'];
|
||||
}
|
||||
|
||||
if ( empty( $settings['hasIcon'] ) ) {
|
||||
$classNames[] = 'gb-button-text';
|
||||
}
|
||||
|
||||
$relAttributes = array();
|
||||
|
||||
if ( ! empty( $settings['relNoFollow'] ) ) {
|
||||
$relAttributes[] = 'nofollow';
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['target'] ) ) {
|
||||
$relAttributes[] = 'noopener';
|
||||
$relAttributes[] = 'noreferrer';
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['relSponsored'] ) ) {
|
||||
$relAttributes[] = 'sponsored';
|
||||
}
|
||||
|
||||
$icon_html = '';
|
||||
|
||||
// Extract our icon from the static HTML.
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$icon_html = GenerateBlocks_Dynamic_Content::get_icon_html( $content );
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Button',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
foreach ( (array) $dynamic_content as $content ) {
|
||||
$tagName = 'span';
|
||||
|
||||
$dynamic_link = GenerateBlocks_Dynamic_Content::get_dynamic_url( $attributes, $block );
|
||||
|
||||
if ( ! empty( $content['attributes']['href'] ) || $dynamic_link ) {
|
||||
$tagName = 'a';
|
||||
}
|
||||
|
||||
if ( 'button' === $settings['buttonType'] ) {
|
||||
$tagName = 'button';
|
||||
}
|
||||
|
||||
$button_attributes = array(
|
||||
'id' => ! empty( $settings['anchor'] ) ? $settings['anchor'] : null,
|
||||
'class' => implode( ' ', $classNames ),
|
||||
'href' => 'a' === $tagName ? $dynamic_link : null,
|
||||
'rel' => ! empty( $relAttributes ) ? implode( ' ', $relAttributes ) : null,
|
||||
'target' => ! empty( $settings['target'] ) ? '_blank' : null,
|
||||
'aria-label' => ! empty( $settings['ariaLabel'] ) ? $settings['ariaLabel'] : null,
|
||||
);
|
||||
|
||||
if ( isset( $content['attributes'] ) ) {
|
||||
foreach ( $content['attributes'] as $attribute => $value ) {
|
||||
if ( 'class' === $attribute ) {
|
||||
$button_attributes[ $attribute ] .= ' ' . $value;
|
||||
} else {
|
||||
$button_attributes[ $attribute ] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= sprintf(
|
||||
'<%1$s %2$s>',
|
||||
$tagName,
|
||||
generateblocks_attr(
|
||||
'dynamic-button',
|
||||
$button_attributes,
|
||||
$settings,
|
||||
$block
|
||||
)
|
||||
);
|
||||
|
||||
if ( $icon_html ) {
|
||||
if ( 'left' === $settings['iconLocation'] ) {
|
||||
$output .= $icon_html;
|
||||
}
|
||||
|
||||
$output .= '<span class="gb-button-text">';
|
||||
}
|
||||
|
||||
if ( isset( $content['content'] ) ) {
|
||||
$output .= $content['content'];
|
||||
} else {
|
||||
$output .= $content;
|
||||
}
|
||||
|
||||
if ( $icon_html ) {
|
||||
$output .= '</span>';
|
||||
|
||||
if ( 'right' === $settings['iconLocation'] ) {
|
||||
$output .= $icon_html;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= sprintf(
|
||||
'</%s>',
|
||||
$tagName
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Element block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Element extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/element';
|
||||
|
||||
/**
|
||||
* Render the Element block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
// Add styles to this block if needed.
|
||||
$block_content = generateblocks_maybe_add_block_css(
|
||||
$block_content,
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Grid block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Grid related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Grid {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
private static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Keep track of CSS we want to output once per block type.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $singular_css_added = false;
|
||||
|
||||
/**
|
||||
* Block defaults.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [
|
||||
'horizontalGap' => '',
|
||||
'verticalGap' => '',
|
||||
'verticalAlignment' => '',
|
||||
'horizontalGapTablet' => '',
|
||||
'verticalGapTablet' => '',
|
||||
'verticalAlignmentTablet' => 'inherit',
|
||||
'horizontalGapMobile' => '',
|
||||
'verticalGapMobile' => '',
|
||||
'verticalAlignmentMobile' => 'inherit',
|
||||
'horizontalAlignment' => '',
|
||||
'horizontalAlignmentTablet' => '',
|
||||
'horizontalAlignmentMobile' => '',
|
||||
'useLegacyRowGap' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
self::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) self::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile our CSS data based on our block attributes.
|
||||
*
|
||||
* @param array $attributes Our block attributes.
|
||||
*/
|
||||
public static function get_css_data( $attributes ) {
|
||||
$css = new GenerateBlocks_Dynamic_CSS();
|
||||
$desktop_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_only_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$mobile_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$css_data = [];
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['gridContainer']
|
||||
);
|
||||
|
||||
$id = $attributes['uniqueId'];
|
||||
$blockVersion = ! empty( $settings['blockVersion'] ) ? $settings['blockVersion'] : 1;
|
||||
|
||||
// Use legacy settings if needed.
|
||||
if ( $blockVersion < 2 ) {
|
||||
$settings = GenerateBlocks_Legacy_Attributes::get_settings( '1.4.0', 'grid', $settings, $attributes );
|
||||
}
|
||||
|
||||
$gap_direction = 'left';
|
||||
|
||||
if ( is_rtl() ) {
|
||||
$gap_direction = 'right';
|
||||
}
|
||||
|
||||
// Don't output horizontal gap defaults if we're using global styles.
|
||||
if ( $blockVersion < 2 && isset( $settings['useGlobalStyle'] ) && $settings['useGlobalStyle'] && isset( $settings['globalStyleId'] ) && $settings['globalStyleId'] ) {
|
||||
if ( (string) $settings['horizontalGap'] === (string) $defaults['gridContainer']['horizontalGap'] ) {
|
||||
$settings['horizontalGap'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Only add this CSS once.
|
||||
if ( ! self::$singular_css_added ) {
|
||||
do_action(
|
||||
'generateblocks_block_one_time_css_data',
|
||||
'grid',
|
||||
$settings,
|
||||
$css
|
||||
);
|
||||
|
||||
self::$singular_css_added = true;
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-grid-wrapper-' . $id );
|
||||
$css->add_property( 'display', 'flex' );
|
||||
$css->add_property( 'flex-wrap', 'wrap' );
|
||||
$css->add_property( 'align-items', $settings['verticalAlignment'] );
|
||||
$css->add_property( 'justify-content', $settings['horizontalAlignment'] );
|
||||
|
||||
if ( $blockVersion > 2 && ! $settings['useLegacyRowGap'] ) {
|
||||
$css->add_property( 'row-gap', $settings['verticalGap'], 'px' );
|
||||
}
|
||||
|
||||
if ( $settings['horizontalGap'] ) {
|
||||
$css->add_property( 'margin-' . $gap_direction, '-' . $settings['horizontalGap'] . 'px' );
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-grid-wrapper-' . $id . ' > .gb-grid-column' );
|
||||
$css->add_property( 'box-sizing', 'border-box' );
|
||||
$css->add_property( 'padding-' . $gap_direction, $settings['horizontalGap'], 'px' );
|
||||
|
||||
if ( $blockVersion < 3 || $settings['useLegacyRowGap'] ) {
|
||||
$css->add_property( 'padding-bottom', $settings['verticalGap'], 'px' );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-grid-wrapper-' . $id );
|
||||
|
||||
if ( $blockVersion > 2 && ! $settings['useLegacyRowGap'] ) {
|
||||
$tablet_css->add_property( 'row-gap', $settings['verticalGapTablet'], 'px' );
|
||||
}
|
||||
|
||||
if ( 'inherit' !== $settings['verticalAlignmentTablet'] ) {
|
||||
$tablet_css->add_property( 'align-items', $settings['verticalAlignmentTablet'] );
|
||||
}
|
||||
|
||||
if ( 'inherit' !== $settings['horizontalAlignmentTablet'] ) {
|
||||
$tablet_css->add_property( 'justify-content', $settings['horizontalAlignmentTablet'] );
|
||||
}
|
||||
|
||||
if ( $settings['horizontalGapTablet'] ) {
|
||||
$tablet_css->add_property( 'margin-' . $gap_direction, '-' . $settings['horizontalGapTablet'] . 'px' );
|
||||
} elseif ( 0 === $settings['horizontalGapTablet'] ) {
|
||||
$tablet_css->add_property( 'margin-' . $gap_direction, $settings['horizontalGapTablet'] );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-grid-wrapper-' . $id . ' > .gb-grid-column' );
|
||||
$tablet_css->add_property( 'padding-' . $gap_direction, $settings['horizontalGapTablet'], 'px' );
|
||||
|
||||
if ( $blockVersion < 3 || $settings['useLegacyRowGap'] ) {
|
||||
$tablet_css->add_property( 'padding-bottom', $settings['verticalGapTablet'], 'px' );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-grid-wrapper-' . $id );
|
||||
|
||||
if ( $blockVersion > 2 && ! $settings['useLegacyRowGap'] ) {
|
||||
$mobile_css->add_property( 'row-gap', $settings['verticalGapMobile'], 'px' );
|
||||
}
|
||||
|
||||
if ( 'inherit' !== $settings['verticalAlignmentMobile'] ) {
|
||||
$mobile_css->add_property( 'align-items', $settings['verticalAlignmentMobile'] );
|
||||
}
|
||||
|
||||
if ( 'inherit' !== $settings['horizontalAlignmentMobile'] ) {
|
||||
$mobile_css->add_property( 'justify-content', $settings['horizontalAlignmentMobile'] );
|
||||
}
|
||||
|
||||
if ( $settings['horizontalGapMobile'] ) {
|
||||
$mobile_css->add_property( 'margin-' . $gap_direction, '-' . $settings['horizontalGapMobile'] . 'px' );
|
||||
} elseif ( 0 === $settings['horizontalGapMobile'] ) {
|
||||
$mobile_css->add_property( 'margin-' . $gap_direction, $settings['horizontalGapMobile'] );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-grid-wrapper-' . $id . ' > .gb-grid-column' );
|
||||
$mobile_css->add_property( 'padding-' . $gap_direction, $settings['horizontalGapMobile'], 'px' );
|
||||
|
||||
if ( $blockVersion < 3 || $settings['useLegacyRowGap'] ) {
|
||||
$mobile_css->add_property( 'padding-bottom', $settings['verticalGapMobile'], 'px' );
|
||||
}
|
||||
|
||||
// Store this block ID in memory.
|
||||
self::store_block_id( $id );
|
||||
|
||||
/**
|
||||
* Do generateblocks_block_css_data hook
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $name The name of our block.
|
||||
* @param array $settings The settings for the current block.
|
||||
* @param object $css Our desktop/main CSS data.
|
||||
* @param object $desktop_css Our desktop only CSS data.
|
||||
* @param object $tablet_css Our tablet CSS data.
|
||||
* @param object $tablet_only_css Our tablet only CSS data.
|
||||
* @param object $mobile_css Our mobile CSS data.
|
||||
*/
|
||||
do_action(
|
||||
'generateblocks_block_css_data',
|
||||
'grid',
|
||||
$settings,
|
||||
$css,
|
||||
$desktop_css,
|
||||
$tablet_css,
|
||||
$tablet_only_css,
|
||||
$mobile_css
|
||||
);
|
||||
|
||||
return [
|
||||
'main' => $css->css_output(),
|
||||
'desktop' => $desktop_css->css_output(),
|
||||
'tablet' => $tablet_css->css_output(),
|
||||
'tablet_only' => $tablet_only_css->css_output(),
|
||||
'mobile' => $mobile_css->css_output(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
if ( ! isset( $attributes['isDynamic'] ) || ! $attributes['isDynamic'] ) {
|
||||
// Add styles to this block if needed.
|
||||
$content = generateblocks_maybe_add_block_css(
|
||||
$content,
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Grid',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['gridContainer']
|
||||
);
|
||||
|
||||
$classNames = array(
|
||||
'gb-grid-wrapper',
|
||||
'gb-grid-wrapper-' . $settings['uniqueId'],
|
||||
);
|
||||
|
||||
if ( ! empty( $settings['className'] ) ) {
|
||||
$classNames[] = $settings['className'];
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Grid',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<div %s>',
|
||||
generateblocks_attr(
|
||||
'grid-wrapper',
|
||||
array(
|
||||
'id' => isset( $settings['anchor'] ) ? $settings['anchor'] : null,
|
||||
'class' => implode( ' ', $classNames ),
|
||||
),
|
||||
$settings,
|
||||
$block
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $attributes['isQueryLoop'] ) ) {
|
||||
$output .= $content;
|
||||
} else {
|
||||
$output .= GenerateBlocks_Block_Query_Loop::render_block( $attributes, $content, $block );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,828 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Headline block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Headline related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Headline {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
private static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Keep track of CSS we want to output once per block type.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $singular_css_added = false;
|
||||
|
||||
/**
|
||||
* Block defaults.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [
|
||||
'element' => 'h2',
|
||||
'cssClasses' => '',
|
||||
'backgroundColor' => '',
|
||||
'textColor' => '',
|
||||
'linkColor' => '',
|
||||
'linkColorHover' => '',
|
||||
'highlightTextColor' => '',
|
||||
'fontFamilyFallback' => '',
|
||||
'googleFont' => false,
|
||||
'googleFontVariants' => '',
|
||||
'icon' => '',
|
||||
'hasIcon' => false,
|
||||
'iconColor' => '',
|
||||
'iconLocation' => 'inline',
|
||||
'iconLocationTablet' => '',
|
||||
'iconLocationMobile' => '',
|
||||
'removeText' => false,
|
||||
'ariaLabel' => '',
|
||||
// Deprecated attributes.
|
||||
'backgroundColorOpacity' => 1,
|
||||
'borderColorOpacity' => 1,
|
||||
'iconColorOpacity' => 1,
|
||||
'fontSize' => '',
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
'lineHeight' => '',
|
||||
'lineHeightTablet' => '',
|
||||
'lineHeightMobile' => '',
|
||||
'lineHeightUnit' => 'em',
|
||||
'letterSpacing' => '',
|
||||
'letterSpacingTablet' => '',
|
||||
'letterSpacingMobile' => '',
|
||||
'fontWeight' => '',
|
||||
'textTransform' => '',
|
||||
'alignment' => '',
|
||||
'alignmentTablet' => '',
|
||||
'alignmentMobile' => '',
|
||||
'iconVerticalAlignment' => 'center',
|
||||
'iconVerticalAlignmentTablet' => '',
|
||||
'iconVerticalAlignmentMobile' => '',
|
||||
'inlineWidth' => false,
|
||||
'inlineWidthTablet' => false,
|
||||
'inlineWidthMobile' => false,
|
||||
'fontFamily' => '',
|
||||
'borderColor' => '',
|
||||
'iconPaddingTop' => '',
|
||||
'iconPaddingRight' => '0.5',
|
||||
'iconPaddingBottom' => '',
|
||||
'iconPaddingLeft' => '',
|
||||
'iconPaddingTopTablet' => '',
|
||||
'iconPaddingRightTablet' => '',
|
||||
'iconPaddingBottomTablet' => '',
|
||||
'iconPaddingLeftTablet' => '',
|
||||
'iconPaddingTopMobile' => '',
|
||||
'iconPaddingRightMobile' => '',
|
||||
'iconPaddingBottomMobile' => '',
|
||||
'iconPaddingLeftMobile' => '',
|
||||
'iconPaddingUnit' => 'em',
|
||||
'iconSize' => 1,
|
||||
'iconSizeTablet' => '',
|
||||
'iconSizeMobile' => '',
|
||||
'iconSizeUnit' => 'em',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
self::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) self::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile our CSS data based on our block attributes.
|
||||
*
|
||||
* @param array $attributes Our block attributes.
|
||||
*/
|
||||
public static function get_css_data( $attributes ) {
|
||||
$css = new GenerateBlocks_Dynamic_CSS();
|
||||
$desktop_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_only_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$mobile_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$css_data = [];
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['headline']
|
||||
);
|
||||
|
||||
$id = $attributes['uniqueId'];
|
||||
$blockVersion = ! empty( $settings['blockVersion'] ) ? $settings['blockVersion'] : 1;
|
||||
|
||||
// Map deprecated settings.
|
||||
$settings = GenerateBlocks_Map_Deprecated_Attributes::map_attributes( $settings );
|
||||
|
||||
$selector = generateblocks_get_css_selector( 'headline', $attributes );
|
||||
|
||||
// Back-compatibility for when icon held a value.
|
||||
if ( $settings['icon'] ) {
|
||||
$settings['hasIcon'] = true;
|
||||
}
|
||||
|
||||
// Only add this CSS once.
|
||||
if ( ! self::$singular_css_added ) {
|
||||
// Singular CSS is no longer supported since 1.9.0.
|
||||
do_action(
|
||||
'generateblocks_block_one_time_css_data',
|
||||
'headline',
|
||||
$settings,
|
||||
$css
|
||||
);
|
||||
|
||||
self::$singular_css_added = true;
|
||||
}
|
||||
|
||||
if ( ! isset( $attributes['hasWrapper'] ) ) {
|
||||
$css->set_selector( $selector );
|
||||
generateblocks_add_layout_css( $css, $settings );
|
||||
generateblocks_add_sizing_css( $css, $settings );
|
||||
generateblocks_add_flex_child_css( $css, $settings );
|
||||
generateblocks_add_typography_css( $css, $settings );
|
||||
generateblocks_add_spacing_css( $css, $settings );
|
||||
generateblocks_add_border_css( $css, $settings );
|
||||
$css->add_property( 'color', $settings['textColor'] );
|
||||
$css->add_property( 'background-color', generateblocks_hex2rgba( $settings['backgroundColor'], $settings['backgroundColorOpacity'] ) );
|
||||
|
||||
if ( $blockVersion < 2 && $settings['inlineWidth'] ) {
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$css->add_property( 'display', 'inline-flex' );
|
||||
} else {
|
||||
$css->add_property( 'display', 'inline-block' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $blockVersion < 2 && $settings['hasIcon'] ) {
|
||||
if ( ! $settings['inlineWidth'] ) {
|
||||
$css->add_property( 'display', 'flex' );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'text-align', $settings['alignment'] );
|
||||
} else {
|
||||
$css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignment'] ) );
|
||||
}
|
||||
|
||||
if ( 'inline' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignment'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
|
||||
$css->set_selector( $selector . ' a' );
|
||||
$css->add_property( 'color', $settings['linkColor'] );
|
||||
|
||||
$css->set_selector( $selector . ' a:hover' );
|
||||
$css->add_property( 'color', $settings['linkColorHover'] );
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$css->set_selector( $selector . ' .gb-icon' );
|
||||
$css->add_property( 'line-height', '0' );
|
||||
$css->add_property( 'color', generateblocks_hex2rgba( $settings['iconColor'], $settings['iconColorOpacity'] ) );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 3 ) {
|
||||
// Need to check for blockVersion here instead of mapping as iconPaddingRight has a default.
|
||||
$css->add_property( 'padding', array( $settings['iconPaddingTop'], $settings['iconPaddingRight'], $settings['iconPaddingBottom'], $settings['iconPaddingLeft'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTop', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRight', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottom', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeft', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $blockVersion < 2 ) {
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'display', 'inline' );
|
||||
} else {
|
||||
$css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
}
|
||||
|
||||
$css->set_selector( $selector . ' .gb-icon svg' );
|
||||
|
||||
if ( $blockVersion < 3 ) {
|
||||
$css->add_property( 'width', $settings['iconSize'], $settings['iconSizeUnit'] );
|
||||
$css->add_property( 'height', $settings['iconSize'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
$css->add_property( 'width', generateblocks_get_array_attribute_value( 'width', $settings['iconStyles'] ) );
|
||||
$css->add_property( 'height', generateblocks_get_array_attribute_value( 'height', $settings['iconStyles'] ) );
|
||||
$css->add_property( 'fill', 'currentColor' );
|
||||
}
|
||||
|
||||
if ( $settings['highlightTextColor'] ) {
|
||||
$css->set_selector( $selector . ' .gb-highlight' );
|
||||
$css->add_property( 'color', $settings['highlightTextColor'] );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( $selector );
|
||||
generateblocks_add_layout_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_sizing_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_flex_child_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_typography_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_spacing_css( $tablet_css, $settings, 'Tablet' );
|
||||
generateblocks_add_border_css( $tablet_css, $settings, 'Tablet' );
|
||||
|
||||
if ( $blockVersion < 2 && $settings['inlineWidthTablet'] ) {
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$tablet_css->add_property( 'display', 'inline-flex' );
|
||||
} else {
|
||||
$tablet_css->add_property( 'display', 'inline-block' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
if ( $blockVersion < 2 ) {
|
||||
$tablet_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
|
||||
if ( 'inline' === $settings['iconLocationTablet'] ) {
|
||||
$tablet_css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignmentTablet'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationTablet'] ) {
|
||||
$tablet_css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( $selector . ' .gb-icon' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 3 ) {
|
||||
$tablet_css->add_property( 'padding', array( $settings['iconPaddingTopTablet'], $settings['iconPaddingRightTablet'], $settings['iconPaddingBottomTablet'], $settings['iconPaddingLeftTablet'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$tablet_css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTopTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRightTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottomTablet', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeftTablet', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $blockVersion < 2 ) {
|
||||
if ( 'above' === $settings['iconLocationTablet'] || ( 'above' === $settings['iconLocation'] && '' == $settings['iconLocationTablet'] ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
$tablet_css->add_property( 'align-self', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationTablet'] ) {
|
||||
$tablet_css->add_property( 'display', 'inline' );
|
||||
}
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( $selector . ' .gb-icon svg' );
|
||||
|
||||
if ( $blockVersion < 3 ) {
|
||||
$tablet_css->add_property( 'width', $settings['iconSizeTablet'], $settings['iconSizeUnit'] );
|
||||
$tablet_css->add_property( 'height', $settings['iconSizeTablet'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
$tablet_css->add_property( 'width', generateblocks_get_array_attribute_value( 'widthTablet', $settings['iconStyles'] ) );
|
||||
$tablet_css->add_property( 'height', generateblocks_get_array_attribute_value( 'heightTablet', $settings['iconStyles'] ) );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( $selector );
|
||||
generateblocks_add_layout_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_sizing_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_flex_child_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_typography_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_spacing_css( $mobile_css, $settings, 'Mobile' );
|
||||
generateblocks_add_border_css( $mobile_css, $settings, 'Mobile' );
|
||||
|
||||
if ( $blockVersion < 2 && $settings['inlineWidthMobile'] ) {
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$mobile_css->add_property( 'display', 'inline-flex' );
|
||||
} else {
|
||||
$mobile_css->add_property( 'display', 'inline-block' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
if ( $blockVersion < 2 ) {
|
||||
$mobile_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
|
||||
if ( 'inline' === $settings['iconLocationMobile'] ) {
|
||||
$mobile_css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignmentMobile'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationMobile'] ) {
|
||||
$mobile_css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( $selector . ' .gb-icon' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
if ( $blockVersion < 3 ) {
|
||||
$mobile_css->add_property( 'padding', array( $settings['iconPaddingTopMobile'], $settings['iconPaddingRightMobile'], $settings['iconPaddingBottomMobile'], $settings['iconPaddingLeftMobile'] ), $settings['iconPaddingUnit'] );
|
||||
} else {
|
||||
$mobile_css->add_property(
|
||||
'padding',
|
||||
array(
|
||||
generateblocks_get_array_attribute_value( 'paddingTopMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingRightMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingBottomMobile', $settings['iconStyles'] ),
|
||||
generateblocks_get_array_attribute_value( 'paddingLeftMobile', $settings['iconStyles'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $blockVersion < 2 ) {
|
||||
if ( 'above' === $settings['iconLocationMobile'] || ( 'above' === $settings['iconLocation'] && '' == $settings['iconLocationMobile'] ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
$mobile_css->add_property( 'align-self', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationMobile'] ) {
|
||||
$mobile_css->add_property( 'display', 'inline' );
|
||||
}
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( $selector . ' .gb-icon svg' );
|
||||
|
||||
if ( $blockVersion < 3 ) {
|
||||
$mobile_css->add_property( 'width', $settings['iconSizeMobile'], $settings['iconSizeUnit'] );
|
||||
$mobile_css->add_property( 'height', $settings['iconSizeMobile'], $settings['iconSizeUnit'] );
|
||||
}
|
||||
|
||||
$mobile_css->add_property( 'width', generateblocks_get_array_attribute_value( 'widthMobile', $settings['iconStyles'] ) );
|
||||
$mobile_css->add_property( 'height', generateblocks_get_array_attribute_value( 'heightMobile', $settings['iconStyles'] ) );
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* All of the below CSS is when the Headline block had a surrounding `div` element.
|
||||
* This surroundng div was deprecated in 1.2.0.
|
||||
*
|
||||
* @TODO Move this CSS to separate function to clean up this function.
|
||||
*/
|
||||
$fontFamily = $settings['fontFamily'];
|
||||
|
||||
if ( $fontFamily && $settings['fontFamilyFallback'] ) {
|
||||
$fontFamily = $fontFamily . ', ' . $settings['fontFamilyFallback'];
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-headline-wrapper' );
|
||||
$css->add_property( 'display', 'flex' );
|
||||
|
||||
$css->set_selector( '.gb-headline-wrapper > .gb-headline' );
|
||||
$css->add_property( 'margin', '0' );
|
||||
$css->add_property( 'padding', '0' );
|
||||
|
||||
$css->set_selector( '.gb-headline-' . $id );
|
||||
$css->add_property( 'font-family', $fontFamily );
|
||||
$css->add_property( 'text-align', $settings['alignment'] );
|
||||
$css->add_property( 'color', $settings['textColor'] );
|
||||
|
||||
if ( ! $settings['hasIcon'] ) {
|
||||
$css->add_property( 'background-color', generateblocks_hex2rgba( $settings['backgroundColor'], $settings['backgroundColorOpacity'] ) );
|
||||
|
||||
if ( $settings['inlineWidth'] ) {
|
||||
$css->add_property( 'display', 'inline-block' );
|
||||
}
|
||||
|
||||
$css->add_property( 'border-width', array( $settings['borderSizeTop'], $settings['borderSizeRight'], $settings['borderSizeBottom'], $settings['borderSizeLeft'] ), 'px' );
|
||||
$css->add_property( 'border-color', generateblocks_hex2rgba( $settings['borderColor'], $settings['borderColorOpacity'] ) );
|
||||
}
|
||||
|
||||
$css->add_property( 'font-size', $settings['fontSize'], $settings['fontSizeUnit'] );
|
||||
$css->add_property( 'font-weight', $settings['fontWeight'] );
|
||||
$css->add_property( 'text-transform', $settings['textTransform'] );
|
||||
$css->add_property( 'line-height', $settings['lineHeight'], $settings['lineHeightUnit'] );
|
||||
$css->add_property( 'letter-spacing', $settings['letterSpacing'], 'em' );
|
||||
|
||||
if ( ! $settings['hasIcon'] ) {
|
||||
$css->add_property( 'padding', array( $settings['paddingTop'], $settings['paddingRight'], $settings['paddingBottom'], $settings['paddingLeft'] ), $settings['paddingUnit'] );
|
||||
$css->add_property( 'margin', array( $settings['marginTop'], $settings['marginRight'], $settings['marginBottom'], $settings['marginLeft'] ), $settings['marginUnit'] );
|
||||
|
||||
if ( function_exists( 'generate_get_default_fonts' ) && '' === $settings['marginBottom'] ) {
|
||||
$defaultBlockStyles = generateblocks_get_default_styles();
|
||||
|
||||
if ( isset( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottom'] ) ) {
|
||||
$css->add_property( 'margin-bottom', $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottom'], $defaultBlockStyles['headline'][ $settings['element'] ]['marginUnit'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-headline-' . $id . ' a, .gb-headline-' . $id . ' a:visited' );
|
||||
$css->add_property( 'color', $settings['linkColor'] );
|
||||
|
||||
$css->set_selector( '.gb-headline-' . $id . ' a:hover' );
|
||||
$css->add_property( 'color', $settings['linkColorHover'] );
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon' );
|
||||
$css->add_property( 'line-height', '0' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
$css->add_property( 'padding', array( $settings['iconPaddingTop'], $settings['iconPaddingRight'], $settings['iconPaddingBottom'], $settings['iconPaddingLeft'] ), $settings['iconPaddingUnit'] );
|
||||
}
|
||||
|
||||
$css->add_property( 'color', generateblocks_hex2rgba( $settings['iconColor'], $settings['iconColorOpacity'] ) );
|
||||
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'display', 'inline' );
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon svg' );
|
||||
$css->add_property( 'width', $settings['iconSize'], $settings['iconSizeUnit'] );
|
||||
$css->add_property( 'height', $settings['iconSize'], $settings['iconSizeUnit'] );
|
||||
$css->add_property( 'fill', 'currentColor' );
|
||||
|
||||
$css->set_selector( '.gb-headline-wrapper-' . $id );
|
||||
$css->add_property( 'padding', array( $settings['paddingTop'], $settings['paddingRight'], $settings['paddingBottom'], $settings['paddingLeft'] ), $settings['paddingUnit'] );
|
||||
$css->add_property( 'margin', array( $settings['marginTop'], $settings['marginRight'], $settings['marginBottom'], $settings['marginLeft'] ), $settings['marginUnit'] );
|
||||
|
||||
$defaultBlockStyles = generateblocks_get_default_styles();
|
||||
|
||||
if ( '' === $settings['marginBottom'] && ! $settings['removeText'] && isset( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottom'] ) && is_numeric( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottom'] ) ) {
|
||||
$css->add_property( 'margin-bottom', $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottom'], $defaultBlockStyles['headline'][ $settings['element'] ]['marginUnit'] );
|
||||
}
|
||||
|
||||
if ( '' === $settings['fontSize'] && ! $settings['removeText'] && isset( $defaultBlockStyles['headline'][ $settings['element'] ]['fontSize'] ) ) {
|
||||
$css->add_property( 'font-size', $defaultBlockStyles['headline'][ $settings['element'] ]['fontSize'], $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeUnit'] );
|
||||
} else {
|
||||
$css->add_property( 'font-size', $settings['fontSize'], $settings['fontSizeUnit'] );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'text-align', $settings['alignment'] );
|
||||
} else {
|
||||
$css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignment'] ) );
|
||||
}
|
||||
|
||||
if ( $settings['inlineWidth'] ) {
|
||||
$css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
|
||||
if ( 'inline' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignment'] ) );
|
||||
}
|
||||
|
||||
$css->add_property( 'background-color', generateblocks_hex2rgba( $settings['backgroundColor'], $settings['backgroundColorOpacity'] ) );
|
||||
$css->add_property( 'color', $settings['textColor'] );
|
||||
$css->add_property( 'border-width', array( $settings['borderSizeTop'], $settings['borderSizeRight'], $settings['borderSizeBottom'], $settings['borderSizeLeft'] ), 'px' );
|
||||
$css->add_property( 'border-color', generateblocks_hex2rgba( $settings['borderColor'], $settings['borderColorOpacity'] ) );
|
||||
|
||||
if ( 'above' === $settings['iconLocation'] ) {
|
||||
$css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['highlightTextColor'] ) {
|
||||
$css->set_selector( '.gb-headline-' . $id . ' .gb-highlight' );
|
||||
$css->add_property( 'color', $settings['highlightTextColor'] );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-headline-' . $id );
|
||||
$tablet_css->add_property( 'text-align', $settings['alignmentTablet'] );
|
||||
$tablet_css->add_property( 'font-size', $settings['fontSizeTablet'], $settings['fontSizeUnit'] );
|
||||
$tablet_css->add_property( 'line-height', $settings['lineHeightTablet'], $settings['lineHeightUnit'] );
|
||||
$tablet_css->add_property( 'letter-spacing', $settings['letterSpacingTablet'], 'em' );
|
||||
|
||||
if ( ! $settings['hasIcon'] ) {
|
||||
$tablet_css->add_property( 'margin', array( $settings['marginTopTablet'], $settings['marginRightTablet'], $settings['marginBottomTablet'], $settings['marginLeftTablet'] ), $settings['marginUnit'] );
|
||||
$tablet_css->add_property( 'padding', array( $settings['paddingTopTablet'], $settings['paddingRightTablet'], $settings['paddingBottomTablet'], $settings['paddingLeftTablet'] ), $settings['paddingUnit'] );
|
||||
$tablet_css->add_property( 'border-width', array( $settings['borderSizeTopTablet'], $settings['borderSizeRightTablet'], $settings['borderSizeBottomTablet'], $settings['borderSizeLeftTablet'] ), 'px' );
|
||||
|
||||
if ( $settings['inlineWidthTablet'] ) {
|
||||
$tablet_css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$tablet_css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
$tablet_css->add_property( 'padding', array( $settings['iconPaddingTopTablet'], $settings['iconPaddingRightTablet'], $settings['iconPaddingBottomTablet'], $settings['iconPaddingLeftTablet'] ), $settings['iconPaddingUnit'] );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationTablet'] || ( 'above' === $settings['iconLocation'] && '' == $settings['iconLocationTablet'] ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
$tablet_css->add_property( 'align-self', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon svg' );
|
||||
$tablet_css->add_property( 'width', $settings['iconSizeTablet'], $settings['iconSizeUnit'] );
|
||||
$tablet_css->add_property( 'height', $settings['iconSizeTablet'], $settings['iconSizeUnit'] );
|
||||
|
||||
$tablet_css->set_selector( '.gb-headline-wrapper-' . $id );
|
||||
$tablet_css->add_property( 'margin', array( $settings['marginTopTablet'], $settings['marginRightTablet'], $settings['marginBottomTablet'], $settings['marginLeftTablet'] ), $settings['marginUnit'] );
|
||||
$tablet_css->add_property( 'padding', array( $settings['paddingTopTablet'], $settings['paddingRightTablet'], $settings['paddingBottomTablet'], $settings['paddingLeftTablet'] ), $settings['paddingUnit'] );
|
||||
$tablet_css->add_property( 'border-width', array( $settings['borderSizeTopTablet'], $settings['borderSizeRightTablet'], $settings['borderSizeBottomTablet'], $settings['borderSizeLeftTablet'] ), 'px' );
|
||||
$tablet_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentTablet'] ) );
|
||||
|
||||
$defaultBlockStyles = generateblocks_get_default_styles();
|
||||
|
||||
if ( '' === $settings['marginBottomTablet'] && ! $settings['removeText'] && isset( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomTablet'] ) && is_numeric( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomTablet'] ) ) {
|
||||
$tablet_css->add_property( 'margin-bottom', $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomTablet'], $defaultBlockStyles['headline'][ $settings['element'] ]['marginUnit'] );
|
||||
}
|
||||
|
||||
if ( '' === $settings['fontSizeTablet'] && ! $settings['removeText'] && isset( $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeTablet'] ) ) {
|
||||
$tablet_css->add_property( 'font-size', $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeTablet'], $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeUnit'] );
|
||||
} else {
|
||||
$tablet_css->add_property( 'font-size', $settings['fontSizeTablet'], $settings['fontSizeUnit'] );
|
||||
}
|
||||
|
||||
if ( $settings['inlineWidthTablet'] ) {
|
||||
$tablet_css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
|
||||
if ( 'inline' === $settings['iconLocationTablet'] ) {
|
||||
$tablet_css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignmentTablet'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationTablet'] ) {
|
||||
$tablet_css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-headline-' . $id );
|
||||
$mobile_css->add_property( 'text-align', $settings['alignmentMobile'] );
|
||||
$mobile_css->add_property( 'font-size', $settings['fontSizeMobile'], $settings['fontSizeUnit'] );
|
||||
$mobile_css->add_property( 'line-height', $settings['lineHeightMobile'], $settings['lineHeightUnit'] );
|
||||
$mobile_css->add_property( 'letter-spacing', $settings['letterSpacingMobile'], 'em' );
|
||||
|
||||
if ( ! $settings['hasIcon'] ) {
|
||||
$mobile_css->add_property( 'margin', array( $settings['marginTopMobile'], $settings['marginRightMobile'], $settings['marginBottomMobile'], $settings['marginLeftMobile'] ), $settings['marginUnit'] );
|
||||
$mobile_css->add_property( 'padding', array( $settings['paddingTopMobile'], $settings['paddingRightMobile'], $settings['paddingBottomMobile'], $settings['paddingLeftMobile'] ), $settings['paddingUnit'] );
|
||||
$mobile_css->add_property( 'border-width', array( $settings['borderSizeTopMobile'], $settings['borderSizeRightMobile'], $settings['borderSizeBottomMobile'], $settings['borderSizeLeftMobile'] ), 'px' );
|
||||
|
||||
if ( $settings['inlineWidthMobile'] ) {
|
||||
$mobile_css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$mobile_css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon' );
|
||||
|
||||
if ( ! $settings['removeText'] ) {
|
||||
$mobile_css->add_property( 'padding', array( $settings['iconPaddingTopMobile'], $settings['iconPaddingRightMobile'], $settings['iconPaddingBottomMobile'], $settings['iconPaddingLeftMobile'] ), $settings['iconPaddingUnit'] );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationMobile'] || ( 'above' === $settings['iconLocation'] && '' == $settings['iconLocationMobile'] ) || ( 'above' === $settings['iconLocationTablet'] && '' == $settings['iconLocationMobile'] ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
$mobile_css->add_property( 'align-self', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-headline-wrapper-' . $id . ' .gb-icon svg' );
|
||||
$mobile_css->add_property( 'width', $settings['iconSizeMobile'], $settings['iconSizeUnit'] );
|
||||
$mobile_css->add_property( 'height', $settings['iconSizeMobile'], $settings['iconSizeUnit'] );
|
||||
|
||||
$mobile_css->set_selector( '.gb-headline-wrapper-' . $id );
|
||||
$mobile_css->add_property( 'margin', array( $settings['marginTopMobile'], $settings['marginRightMobile'], $settings['marginBottomMobile'], $settings['marginLeftMobile'] ), $settings['marginUnit'] );
|
||||
$mobile_css->add_property( 'padding', array( $settings['paddingTopMobile'], $settings['paddingRightMobile'], $settings['paddingBottomMobile'], $settings['paddingLeftMobile'] ), $settings['paddingUnit'] );
|
||||
$mobile_css->add_property( 'justify-content', generateblocks_get_flexbox_alignment( $settings['alignmentMobile'] ) );
|
||||
|
||||
$defaultBlockStyles = generateblocks_get_default_styles();
|
||||
|
||||
if ( '' === $settings['marginBottomMobile'] && ! $settings['removeText'] && isset( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomMobile'] ) && is_numeric( $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomMobile'] ) ) {
|
||||
$mobile_css->add_property( 'margin-bottom', $defaultBlockStyles['headline'][ $settings['element'] ]['marginBottomMobile'], $defaultBlockStyles['headline'][ $settings['element'] ]['marginUnit'] );
|
||||
}
|
||||
|
||||
if ( '' === $settings['fontSizeMobile'] && ! $settings['removeText'] && ! empty( $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeMobile'] ) ) {
|
||||
$mobile_css->add_property( 'font-size', $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeMobile'], $defaultBlockStyles['headline'][ $settings['element'] ]['fontSizeUnit'] );
|
||||
} else {
|
||||
$mobile_css->add_property( 'font-size', $settings['fontSizeMobile'], $settings['fontSizeUnit'] );
|
||||
}
|
||||
|
||||
if ( $settings['inlineWidthMobile'] ) {
|
||||
$mobile_css->add_property( 'display', 'inline-flex' );
|
||||
}
|
||||
|
||||
if ( 'inline' === $settings['iconLocationMobile'] ) {
|
||||
$mobile_css->add_property( 'align-items', generateblocks_get_flexbox_alignment( $settings['iconVerticalAlignmentMobile'] ) );
|
||||
}
|
||||
|
||||
if ( 'above' === $settings['iconLocationMobile'] ) {
|
||||
$mobile_css->add_property( 'flex-direction', 'column' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store this block ID in memory.
|
||||
self::store_block_id( $id );
|
||||
|
||||
/**
|
||||
* Do generateblocks_block_css_data hook
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $name The name of our block.
|
||||
* @param array $settings The settings for the current block.
|
||||
* @param object $css Our desktop/main CSS data.
|
||||
* @param object $desktop_css Our desktop only CSS data.
|
||||
* @param object $tablet_css Our tablet CSS data.
|
||||
* @param object $tablet_only_css Our tablet only CSS data.
|
||||
* @param object $mobile_css Our mobile CSS data.
|
||||
*/
|
||||
do_action(
|
||||
'generateblocks_block_css_data',
|
||||
'headline',
|
||||
$settings,
|
||||
$css,
|
||||
$desktop_css,
|
||||
$tablet_css,
|
||||
$tablet_only_css,
|
||||
$mobile_css
|
||||
);
|
||||
|
||||
return [
|
||||
'main' => $css->css_output(),
|
||||
'desktop' => $desktop_css->css_output(),
|
||||
'tablet' => $tablet_css->css_output(),
|
||||
'tablet_only' => $tablet_only_css->css_output(),
|
||||
'mobile' => $mobile_css->css_output(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
if ( strpos( trim( $content ), '<div class="gb-headline-wrapper' ) === 0 ) {
|
||||
$attributes['hasWrapper'] = true;
|
||||
}
|
||||
|
||||
if ( ! isset( $attributes['useDynamicData'] ) || ! $attributes['useDynamicData'] ) {
|
||||
// Add styles to this block if needed.
|
||||
$content = generateblocks_maybe_add_block_css(
|
||||
$content,
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Headline',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$allow_empty_content = false;
|
||||
|
||||
if ( empty( $attributes['dynamicContentType'] ) ) {
|
||||
$dynamic_content = GenerateBlocks_Dynamic_Content::get_static_content( $content );
|
||||
|
||||
if ( ! empty( $attributes['hasIcon'] ) && ! empty( $attributes['removeText'] ) ) {
|
||||
// Allow icon-only items to continue.
|
||||
$allow_empty_content = true;
|
||||
}
|
||||
} else {
|
||||
$dynamic_content = GenerateBlocks_Dynamic_Content::get_content( $attributes, $block );
|
||||
}
|
||||
|
||||
if ( ! $dynamic_content && '0' !== $dynamic_content && ! $allow_empty_content ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['headline']
|
||||
);
|
||||
|
||||
$classNames = array(
|
||||
'gb-headline',
|
||||
'gb-headline-' . $settings['uniqueId'],
|
||||
);
|
||||
|
||||
if ( ! empty( $settings['className'] ) ) {
|
||||
$classNames[] = $settings['className'];
|
||||
}
|
||||
|
||||
if ( empty( $settings['hasIcon'] ) ) {
|
||||
$classNames[] = 'gb-headline-text';
|
||||
}
|
||||
|
||||
$tagName = apply_filters(
|
||||
'generateblocks_dynamic_headline_tagname',
|
||||
$settings['element'],
|
||||
$attributes,
|
||||
$block
|
||||
);
|
||||
|
||||
$allowedTagNames = apply_filters(
|
||||
'generateblocks_dynamic_headline_allowed_tagnames',
|
||||
array(
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'div',
|
||||
'p',
|
||||
'figcaption',
|
||||
),
|
||||
$attributes,
|
||||
$block
|
||||
);
|
||||
|
||||
if ( ! in_array( $tagName, $allowedTagNames ) ) {
|
||||
$tagName = 'div';
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Headline',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<%1$s %2$s>',
|
||||
$tagName,
|
||||
generateblocks_attr(
|
||||
'dynamic-headline',
|
||||
array(
|
||||
'id' => isset( $settings['anchor'] ) ? $settings['anchor'] : null,
|
||||
'class' => implode( ' ', $classNames ),
|
||||
),
|
||||
$settings,
|
||||
$block
|
||||
)
|
||||
);
|
||||
|
||||
$icon_html = '';
|
||||
|
||||
// Extract our icon from the static HTML.
|
||||
if ( $settings['hasIcon'] ) {
|
||||
$icon_html = GenerateBlocks_Dynamic_Content::get_icon_html( $content );
|
||||
|
||||
if ( $icon_html ) {
|
||||
$output .= $icon_html;
|
||||
$output .= '<span class="gb-headline-text">';
|
||||
}
|
||||
}
|
||||
|
||||
$dynamic_link = GenerateBlocks_Dynamic_Content::get_dynamic_url( $attributes, $block );
|
||||
|
||||
if ( $dynamic_link ) {
|
||||
$dynamic_content = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( $dynamic_link ),
|
||||
$dynamic_content
|
||||
);
|
||||
}
|
||||
|
||||
$output .= $dynamic_content;
|
||||
|
||||
if ( $icon_html ) {
|
||||
$output .= '</span>';
|
||||
}
|
||||
|
||||
$output .= sprintf(
|
||||
'</%s>',
|
||||
$tagName
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Image block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Image related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Image {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
private static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Keep track of CSS we want to output once per block type.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $singular_css_added = false;
|
||||
|
||||
/**
|
||||
* Block defaults.
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [
|
||||
'mediaId' => '',
|
||||
'sizeSlug' => '',
|
||||
'width' => '',
|
||||
'widthTablet' => '',
|
||||
'widthMobile' => '',
|
||||
'height' => '',
|
||||
'heightTablet' => '',
|
||||
'heightMobile' => '',
|
||||
'borderColor' => '',
|
||||
'objectFit' => '',
|
||||
'objectFitTablet' => '',
|
||||
'objectFitMobile' => '',
|
||||
'align' => '',
|
||||
'alignment' => '',
|
||||
'alignmentTablet' => '',
|
||||
'alignmentMobile' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store our block ID in memory.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function store_block_id( $id ) {
|
||||
self::$block_ids[] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our block ID exists.
|
||||
*
|
||||
* @param string $id The block ID to store.
|
||||
*/
|
||||
public static function block_id_exists( $id ) {
|
||||
return in_array( $id, (array) self::$block_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile our CSS data based on our block attributes.
|
||||
*
|
||||
* @param array $attributes Our block attributes.
|
||||
*/
|
||||
public static function get_css_data( $attributes ) {
|
||||
$css = new GenerateBlocks_Dynamic_CSS();
|
||||
$desktop_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$tablet_only_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$mobile_css = new GenerateBlocks_Dynamic_CSS();
|
||||
$css_data = [];
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['image']
|
||||
);
|
||||
|
||||
$id = $attributes['uniqueId'];
|
||||
|
||||
// Only add this CSS once.
|
||||
if ( ! self::$singular_css_added ) {
|
||||
do_action(
|
||||
'generateblocks_block_one_time_css_data',
|
||||
'image',
|
||||
$settings,
|
||||
$css
|
||||
);
|
||||
|
||||
self::$singular_css_added = true;
|
||||
}
|
||||
|
||||
// Map deprecated settings.
|
||||
$settings = GenerateBlocks_Map_Deprecated_Attributes::map_attributes( $settings );
|
||||
|
||||
$css->set_selector( '.gb-block-image-' . $id );
|
||||
generateblocks_add_spacing_css( $css, $settings );
|
||||
|
||||
// Set a flag we'll update later if we disable floats.
|
||||
$disable_float = false;
|
||||
$has_desktop_float = 'floatLeft' === $settings['alignment'] || 'floatRight' === $settings['alignment'];
|
||||
$has_tablet_float = 'floatLeft' === $settings['alignmentTablet'] || 'floatRight' === $settings['alignmentTablet'];
|
||||
|
||||
if ( $has_desktop_float ) {
|
||||
$css->add_property( 'float', generateblocks_get_float_alignment( $settings['alignment'] ) );
|
||||
} else {
|
||||
$css->add_property( 'text-align', $settings['alignment'] );
|
||||
}
|
||||
|
||||
$css->set_selector( '.gb-image-' . $id );
|
||||
generateblocks_add_border_css( $css, $settings );
|
||||
$css->add_property( 'width', $settings['width'] );
|
||||
$css->add_property( 'height', $settings['height'] );
|
||||
$css->add_property( 'object-fit', $settings['objectFit'] );
|
||||
$css->add_property( 'vertical-align', 'middle' );
|
||||
|
||||
$tablet_css->set_selector( '.gb-block-image-' . $id );
|
||||
generateblocks_add_spacing_css( $tablet_css, $settings, 'Tablet' );
|
||||
|
||||
if ( $has_tablet_float ) {
|
||||
$tablet_css->add_property( 'float', generateblocks_get_float_alignment( $settings['alignmentTablet'] ) );
|
||||
} else {
|
||||
$tablet_css->add_property( 'text-align', $settings['alignmentTablet'] );
|
||||
|
||||
if ( $settings['alignmentTablet'] && $has_desktop_float ) {
|
||||
$tablet_css->add_property( 'float', 'none' );
|
||||
$disable_float = true;
|
||||
}
|
||||
}
|
||||
|
||||
$tablet_css->set_selector( '.gb-image-' . $id );
|
||||
generateblocks_add_border_css( $tablet_css, $settings, 'Tablet' );
|
||||
$tablet_css->add_property( 'width', $settings['widthTablet'] );
|
||||
$tablet_css->add_property( 'height', $settings['heightTablet'] );
|
||||
$tablet_css->add_property( 'object-fit', $settings['objectFitTablet'] );
|
||||
|
||||
$mobile_css->set_selector( '.gb-block-image-' . $id );
|
||||
generateblocks_add_spacing_css( $mobile_css, $settings, 'Mobile' );
|
||||
|
||||
if ( 'floatLeft' === $settings['alignmentMobile'] || 'floatRight' === $settings['alignmentMobile'] ) {
|
||||
$mobile_css->add_property( 'float', generateblocks_get_float_alignment( $settings['alignmentMobile'] ) );
|
||||
} else {
|
||||
$mobile_css->add_property( 'text-align', $settings['alignmentMobile'] );
|
||||
|
||||
if (
|
||||
$settings['alignmentMobile'] &&
|
||||
! $disable_float &&
|
||||
(
|
||||
$has_desktop_float ||
|
||||
$has_tablet_float
|
||||
)
|
||||
) {
|
||||
$mobile_css->add_property( 'float', 'none' );
|
||||
}
|
||||
}
|
||||
|
||||
$mobile_css->set_selector( '.gb-image-' . $id );
|
||||
generateblocks_add_border_css( $mobile_css, $settings, 'Mobile' );
|
||||
$mobile_css->add_property( 'width', $settings['widthMobile'] );
|
||||
$mobile_css->add_property( 'height', $settings['heightMobile'] );
|
||||
$mobile_css->add_property( 'object-fit', $settings['objectFitMobile'] );
|
||||
|
||||
// Store this block ID in memory.
|
||||
self::store_block_id( $id );
|
||||
|
||||
/**
|
||||
* Do generateblocks_block_css_data hook
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $name The name of our block.
|
||||
* @param array $settings The settings for the current block.
|
||||
* @param object $css Our desktop/main CSS data.
|
||||
* @param object $desktop_css Our desktop only CSS data.
|
||||
* @param object $tablet_css Our tablet CSS data.
|
||||
* @param object $tablet_only_css Our tablet only CSS data.
|
||||
* @param object $mobile_css Our mobile CSS data.
|
||||
*/
|
||||
do_action(
|
||||
'generateblocks_block_css_data',
|
||||
'image',
|
||||
$settings,
|
||||
$css,
|
||||
$desktop_css,
|
||||
$tablet_css,
|
||||
$tablet_only_css,
|
||||
$mobile_css
|
||||
);
|
||||
|
||||
return [
|
||||
'main' => $css->css_output(),
|
||||
'desktop' => $desktop_css->css_output(),
|
||||
'tablet' => $tablet_css->css_output(),
|
||||
'tablet_only' => $tablet_only_css->css_output(),
|
||||
'mobile' => $mobile_css->css_output(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
if ( empty( $attributes['useDynamicData'] ) ) {
|
||||
// Add styles to this block if needed.
|
||||
$content = generateblocks_maybe_add_block_css(
|
||||
$content,
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Image',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return generateblocks_filter_images( $content, $attributes );
|
||||
}
|
||||
|
||||
$image = empty( $attributes['dynamicContentType'] )
|
||||
? generateblocks_filter_images( GenerateBlocks_Dynamic_Content::get_static_content( $content ), $attributes )
|
||||
: GenerateBlocks_Dynamic_Content::get_dynamic_image( $attributes, $block );
|
||||
|
||||
if ( ! $image ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['image']
|
||||
);
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => 'GenerateBlocks_Block_Image',
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<figure %s>',
|
||||
generateblocks_attr(
|
||||
'image-figure',
|
||||
array(
|
||||
'class' => implode(
|
||||
' ',
|
||||
array(
|
||||
'gb-block-image',
|
||||
'gb-block-image-' . $settings['uniqueId'],
|
||||
)
|
||||
),
|
||||
),
|
||||
$settings,
|
||||
$block
|
||||
)
|
||||
);
|
||||
|
||||
$dynamic_link = GenerateBlocks_Dynamic_Content::get_dynamic_url( $attributes, $block );
|
||||
|
||||
if ( $dynamic_link ) {
|
||||
$relAttributes = array();
|
||||
|
||||
if ( ! empty( $settings['relNoFollow'] ) ) {
|
||||
$relAttributes[] = 'nofollow';
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['openInNewWindow'] ) ) {
|
||||
$relAttributes[] = 'noopener';
|
||||
$relAttributes[] = 'noreferrer';
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['relSponsored'] ) ) {
|
||||
$relAttributes[] = 'sponsored';
|
||||
}
|
||||
|
||||
$image = sprintf(
|
||||
'<a %s>%s</a>',
|
||||
generateblocks_attr(
|
||||
'image-link',
|
||||
array(
|
||||
'class' => '',
|
||||
'href' => $dynamic_link,
|
||||
'rel' => ! empty( $relAttributes ) ? implode( ' ', $relAttributes ) : null,
|
||||
'target' => ! empty( $settings['openInNewWindow'] ) ? '_blank' : null,
|
||||
),
|
||||
$settings,
|
||||
$block
|
||||
),
|
||||
$image
|
||||
);
|
||||
}
|
||||
|
||||
$output .= $image;
|
||||
|
||||
if ( isset( $block->parsed_block['innerBlocks'][0]['attrs'] ) ) {
|
||||
$image_id = GenerateBlocks_Dynamic_Content::get_dynamic_image_id( $attributes );
|
||||
$block->parsed_block['innerBlocks'][0]['attrs']['dynamicImage'] = $image_id;
|
||||
|
||||
$caption = (
|
||||
new WP_Block(
|
||||
$block->parsed_block['innerBlocks'][0]
|
||||
)
|
||||
)->render( array( 'dynamic' => true ) );
|
||||
|
||||
if ( $caption ) {
|
||||
$output .= $caption;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</figure>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Element block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Loop_Item extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/loop-item';
|
||||
|
||||
/**
|
||||
* Render the Element block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param object $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
$query_type = $block->context['generateblocks/queryType'] ?? null;
|
||||
|
||||
if ( GenerateBlocks_Block_Query::TYPE_WP_QUERY === $query_type ) {
|
||||
$post_classes = get_post_class();
|
||||
|
||||
if ( class_exists( 'WP_HTML_Tag_Processor' ) ) {
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
if ( $tags->next_tag( $attributes['tagName'] ) ) {
|
||||
$existing_classes = $tags->get_attribute( 'class' );
|
||||
$new_classes = $existing_classes . ' ' . implode( ' ', $post_classes );
|
||||
$tags->set_attribute( 'class', $new_classes );
|
||||
$block_content = $tags->get_updated_html();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$block_content = generateblocks_maybe_add_block_css(
|
||||
$block_content,
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Element block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Looper extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/looper';
|
||||
|
||||
/**
|
||||
* Sanitize a loop item's data for security.
|
||||
*
|
||||
* @param array|object $loop_item The loop item to sanitize.
|
||||
* @return array|object The sanitized loop item.
|
||||
*/
|
||||
public static function sanitize_loop_item( $loop_item ) {
|
||||
|
||||
$disallowed_keys = GenerateBlocks_Meta_Handler::DISALLOWED_KEYS ?? [];
|
||||
|
||||
foreach ( $disallowed_keys as $key ) {
|
||||
|
||||
if ( is_object( $loop_item ) ) {
|
||||
if ( isset( $loop_item->$key ) ) {
|
||||
unset( $loop_item->$key );
|
||||
}
|
||||
} elseif ( is_array( $loop_item ) ) {
|
||||
if ( isset( $loop_item[ $key ] ) ) {
|
||||
unset( $loop_item[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $loop_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the repeater items for the Looper block.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
* @param WP_Block $block The block instance.
|
||||
* @return string The rendered content.
|
||||
*/
|
||||
public static function render_loop_items( $attributes, $block ) {
|
||||
$query_data = $block->context['generateblocks/queryData']['data'] ?? null;
|
||||
$query_type = $block->context['generateblocks/queryData']['type'] ?? null;
|
||||
$output = '';
|
||||
|
||||
if ( GenerateBlocks_Block_Query::TYPE_WP_QUERY === $query_type ) {
|
||||
$output = self::render_wp_query( $query_data, $attributes, $block );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow users to filter the looper rendering of loop items.
|
||||
*
|
||||
* @param string $output The block output.
|
||||
* @param string $query_type The query type.
|
||||
* @param array|object $query_data The query data.
|
||||
* @param array $attributes Block attributes.
|
||||
* @param WP_Block $block The block instance.
|
||||
*/
|
||||
$output = apply_filters( 'generateblocks_looper_render_loop_items', $output, $query_type, $query_data, $block, $attributes );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the repeater items for the Looper block.
|
||||
*
|
||||
* @param WP_Query $query The WP_Query instance.
|
||||
* @param array $attributes Block attributes.
|
||||
* @param WP_Block $block The block instance.
|
||||
* @return string The rendered content.
|
||||
*/
|
||||
public static function render_wp_query( $query, $attributes, $block ) {
|
||||
$query_id = $block->context['generateblocks/queryData']['id'] ?? null;
|
||||
$page_key = $query_id ? 'query-' . $query_id . '-page' : 'query-page';
|
||||
$per_page = $query->query_vars['posts_per_page'] ?? get_option( 'posts_per_page', 10 );
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$page_index = $page - 1; // Zero based index for pages.
|
||||
$offset = $page_index * $per_page;
|
||||
$content = '';
|
||||
$inner_blocks = $block->parsed_block['innerBlocks'] ?? [];
|
||||
|
||||
if ( empty( $inner_blocks ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fallback to support preview in Elements.
|
||||
if ( ! $query ) {
|
||||
return (
|
||||
new WP_Block(
|
||||
$inner_blocks[0],
|
||||
array(
|
||||
'postType' => 'post',
|
||||
'postId' => 0,
|
||||
'generateblocks/queryType' => GenerateBlocks_Block_Query::TYPE_WP_QUERY,
|
||||
'generateblocks/loopIndex' => 1,
|
||||
'generateblocks/loopItem' => [ 'ID' => 0 ],
|
||||
)
|
||||
)
|
||||
)->render( array( 'dynamic' => false ) );
|
||||
}
|
||||
|
||||
if ( $query->have_posts() ) {
|
||||
while ( $query->have_posts() ) {
|
||||
$query->the_post();
|
||||
global $post;
|
||||
// Get the current index of the Loop.
|
||||
foreach ( $inner_blocks as $inner_block ) {
|
||||
$content .= (
|
||||
new WP_Block(
|
||||
$inner_block,
|
||||
array(
|
||||
'postType' => get_post_type(),
|
||||
'postId' => get_the_ID(),
|
||||
'generateblocks/queryType' => GenerateBlocks_Block_Query::TYPE_WP_QUERY,
|
||||
'generateblocks/loopIndex' => $offset + $query->current_post + 1,
|
||||
'generateblocks/loopItem' => self::sanitize_loop_item( $post ),
|
||||
)
|
||||
)
|
||||
)->render();
|
||||
}
|
||||
}
|
||||
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Query block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
$loop_items = self::render_loop_items( $attributes, $block );
|
||||
|
||||
if ( ! $loop_items ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html_attributes = generateblocks_get_processed_html_attributes( $block_content );
|
||||
|
||||
// If our processing returned nothing, let's try to build our attributes from the block attributes.
|
||||
if ( empty( $html_attributes ) ) {
|
||||
$html_attributes = generateblocks_get_backup_html_attributes( 'gb-looper', $attributes );
|
||||
}
|
||||
|
||||
$tag_name = $attributes['tagName'] ?? 'div';
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<%1$s %2$s>%3$s</%1$s>',
|
||||
$tag_name,
|
||||
generateblocks_attr(
|
||||
'looper',
|
||||
$html_attributes,
|
||||
$attributes,
|
||||
$block
|
||||
),
|
||||
$loop_items
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Element block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Media extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/media';
|
||||
|
||||
/**
|
||||
* Render the Void Element block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
// Add styles to this block if needed.
|
||||
$block_content = generateblocks_maybe_add_block_css(
|
||||
$block_content,
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Query Loop block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Query Loop related functions.
|
||||
*/
|
||||
class GenerateBlocks_Block_Query_Loop {
|
||||
/**
|
||||
* Wrapper function for our dynamic buttons.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $content The dynamic text to display.
|
||||
* @param WP_Block $block Block instance.
|
||||
*/
|
||||
public static function render_block( $attributes, $content, $block ) {
|
||||
$page_key = isset( $block->context['generateblocks/queryId'] ) ? 'query-' . $block->context['generateblocks/queryId'] . '-page' : 'query-page';
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$query_args = GenerateBlocks_Query_Loop::get_query_args( $block, $page );
|
||||
|
||||
// Override the custom query with the global query if needed.
|
||||
$use_global_query = ( isset( $block->context['generateblocks/inheritQuery'] ) && $block->context['generateblocks/inheritQuery'] );
|
||||
|
||||
if ( $use_global_query ) {
|
||||
global $wp_query;
|
||||
|
||||
if ( $wp_query && isset( $wp_query->query_vars ) && is_array( $wp_query->query_vars ) ) {
|
||||
// Unset `offset` because if is set, $wp_query overrides/ignores the paged parameter and breaks pagination.
|
||||
unset( $query_args['offset'] );
|
||||
$query_args = wp_parse_args( $wp_query->query_vars, $query_args );
|
||||
|
||||
if ( empty( $query_args['post_type'] ) && is_singular() ) {
|
||||
$query_args['post_type'] = get_post_type( get_the_ID() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query_args = apply_filters(
|
||||
'generateblocks_query_loop_args',
|
||||
$query_args,
|
||||
$attributes,
|
||||
$block
|
||||
);
|
||||
|
||||
$the_query = new WP_Query( $query_args );
|
||||
|
||||
$content = '';
|
||||
if ( $the_query->have_posts() ) {
|
||||
while ( $the_query->have_posts() ) {
|
||||
$the_query->the_post();
|
||||
|
||||
$block_content = (
|
||||
new WP_Block(
|
||||
$block->parsed_block,
|
||||
array(
|
||||
'postType' => get_post_type(),
|
||||
'postId' => get_the_ID(),
|
||||
)
|
||||
)
|
||||
)->render( array( 'dynamic' => false ) );
|
||||
|
||||
$content .= $block_content;
|
||||
}
|
||||
}
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the No Results block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The No Results block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Query_No_Results extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/query-no-results';
|
||||
|
||||
/**
|
||||
* Render the Shape block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
$no_results = $block->context['generateblocks/queryData']['noResults'] ?? false;
|
||||
|
||||
if ( ! $no_results ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the No Results block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The No Results block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Query_Page_Numbers extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/query-page-numbers';
|
||||
|
||||
/**
|
||||
* Render the Shape block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param object $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
$query_id = $block->context['generateblocks/queryData']['id'] ?? null;
|
||||
$page_key = $query_id ? 'query-' . $query_id . '-page' : 'query-page';
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$args = $block->context['generateblocks/queryData']['args'] ?? [];
|
||||
$per_page = $args['posts_per_page'] ?? apply_filters( 'generateblocks_query_per_page_default', 10, $args );
|
||||
$content = '';
|
||||
$mid_size = isset( $block->attributes['midSize'] ) ? (int) $block->attributes['midSize'] : null;
|
||||
$inherit_query = $block->context['generateblocks/queryData']['inherit'] ?? false;
|
||||
|
||||
if ( $inherit_query ) {
|
||||
$paginate_args = [ 'prev_next' => false ];
|
||||
|
||||
if ( null !== $mid_size ) {
|
||||
$paginate_args['mid_size'] = $mid_size;
|
||||
}
|
||||
|
||||
$content = paginate_links( $paginate_args );
|
||||
} else {
|
||||
$query_data = $block->context['generateblocks/queryData']['data'] ?? null;
|
||||
$max_pages = $block->context['generateblocks/queryData']['maxPages'] ?? 0;
|
||||
|
||||
if ( ! $query_data ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$paginate_args = array(
|
||||
'base' => '%_%',
|
||||
'format' => "?$page_key=%#%",
|
||||
'current' => max( 1, $page ),
|
||||
'total' => $max_pages,
|
||||
'prev_next' => false,
|
||||
);
|
||||
|
||||
if ( null !== $mid_size ) {
|
||||
$paginate_args['mid_size'] = $mid_size;
|
||||
}
|
||||
|
||||
if ( 1 !== $page ) {
|
||||
/**
|
||||
* `paginate_links` doesn't use the provided `format` when the page is `1`.
|
||||
* This is great for the main query as it removes the extra query params
|
||||
* making the URL shorter, but in the case of multiple custom queries is
|
||||
* problematic. It results in returning an empty link which ends up with
|
||||
* a link to the current page.
|
||||
*
|
||||
* A way to address this is to add a `fake` query arg with no value that
|
||||
* is the same for all custom queries. This way the link is not empty and
|
||||
* preserves all the other existent query args.
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/functions/paginate_links/
|
||||
*
|
||||
* The proper fix of this should be in core. Track Ticket:
|
||||
* @see https://core.trac.wordpress.org/ticket/53868
|
||||
*
|
||||
* TODO: After two WP versions (starting from the WP version the core patch landed),
|
||||
* we should remove this and call `paginate_links` with the proper new arg.
|
||||
*/
|
||||
$paginate_args['add_args'] = array( 'cst' => '' );
|
||||
}
|
||||
|
||||
// We still need to preserve `paged` query param if exists, as is used
|
||||
// for Queries that inherit from global context.
|
||||
$paged = empty( $_GET['paged'] ) ? null : (int) $_GET['paged']; // phpcs:ignore -- No data processing happening.
|
||||
|
||||
if ( $paged ) {
|
||||
$paginate_args['add_args'] = array( 'paged' => $paged );
|
||||
}
|
||||
|
||||
$content = paginate_links( $paginate_args );
|
||||
}
|
||||
|
||||
if ( empty( $content ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$pagination_type = $block->context['generateblocks/queryData']['paginationType'] ?? '';
|
||||
$instant_pagination = GenerateBlocks_Block_Query::TYPE_INSTANT_PAGINATION === $pagination_type;
|
||||
|
||||
if ( $instant_pagination && class_exists( 'WP_HTML_Tag_Processor' ) ) {
|
||||
$p = new WP_HTML_Tag_Processor( $content );
|
||||
|
||||
while ( $p->next_tag(
|
||||
array( 'class_name' => 'page-numbers' )
|
||||
) ) {
|
||||
if ( 'A' === $p->get_tag() ) {
|
||||
$p->set_attribute( 'data-gb-router-target', 'query-' . $query_id );
|
||||
$p->set_attribute( 'data-gb-prefetch', true );
|
||||
}
|
||||
}
|
||||
$content = $p->get_updated_html();
|
||||
}
|
||||
|
||||
$html_attributes = generateblocks_get_processed_html_attributes( $block_content );
|
||||
|
||||
// If our processing returned nothing, let's try to build our attributes from the block attributes.
|
||||
if ( empty( $html_attributes ) ) {
|
||||
$html_attributes = generateblocks_get_backup_html_attributes( 'gb-query-page-numbers', $attributes );
|
||||
}
|
||||
|
||||
$tag_name = $attributes['tagName'] ?? 'div';
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<%1$s %2$s>%3$s</%1$s>',
|
||||
$tag_name,
|
||||
generateblocks_attr(
|
||||
'query-page-numbers',
|
||||
$html_attributes,
|
||||
$attributes,
|
||||
$block
|
||||
),
|
||||
$content
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Query block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Query extends GenerateBlocks_Block {
|
||||
|
||||
const TYPE_WP_QUERY = 'WP_Query';
|
||||
const TYPE_INSTANT_PAGINATION = 'instant';
|
||||
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/query';
|
||||
|
||||
/**
|
||||
* Get the query data based on the type.
|
||||
*
|
||||
* @param string $query_type The type of query (WP_Query, post_meta, etc).
|
||||
* @param array $attributes Block attributes.
|
||||
* @param object $block The block instance.
|
||||
* @param int $page The current query page.
|
||||
* @return array Array of query data including the data for looping and no_results.
|
||||
*/
|
||||
public static function get_query_data( $query_type, $attributes, $block, $page ) {
|
||||
$original_args = $attributes['query'] ?? [];
|
||||
$query_data = [
|
||||
'data' => [],
|
||||
'no_results' => true,
|
||||
'args' => $original_args,
|
||||
];
|
||||
|
||||
if ( self::TYPE_WP_QUERY === $query_type ) {
|
||||
// Override the custom query with the global query if needed.
|
||||
$use_global_query = ( isset( $attributes['inheritQuery'] ) && $attributes['inheritQuery'] );
|
||||
|
||||
if ( $use_global_query ) {
|
||||
global $wp_query;
|
||||
|
||||
/*
|
||||
* If already in the main query loop, duplicate the query instance to not tamper with the main instance.
|
||||
* Since this is a nested query, it should start at the beginning, therefore rewind posts.
|
||||
* Otherwise, the main query loop has not started yet and this block is responsible for doing so.
|
||||
*/
|
||||
if ( in_the_loop() ) {
|
||||
$data = clone $wp_query;
|
||||
$data->rewind_posts();
|
||||
} else {
|
||||
$data = $wp_query;
|
||||
}
|
||||
|
||||
$query_args = $data->query_vars;
|
||||
} else {
|
||||
$query_args = GenerateBlocks_Query_Utils::get_wp_query_args(
|
||||
$query_data['args'],
|
||||
$page,
|
||||
$attributes,
|
||||
$block
|
||||
);
|
||||
|
||||
// Make the new WP_Query with filtered args.
|
||||
$data = new WP_Query( $query_args );
|
||||
}
|
||||
|
||||
$query_data = [
|
||||
'data' => $data,
|
||||
'no_results' => 0 === $data->found_posts,
|
||||
'args' => $query_args,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the Query block's query data.
|
||||
*
|
||||
* @param array $query_data The current query data.
|
||||
* @param string $query_type The type of query.
|
||||
* @param array $attributes An array of block attributes.
|
||||
* @param object $object The block instance.
|
||||
* @param int $page The current page number.
|
||||
*
|
||||
* @return array An array of query data.
|
||||
*/
|
||||
return apply_filters( 'generateblocks_query_data', $query_data, $query_type, $attributes, $block, $page );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Query block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param object $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
$query_id = isset( $attributes['uniqueId'] ) ? 'query-' . $attributes['uniqueId'] : 'query';
|
||||
$page_key = $query_id . '-page';
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$pagination_type = $attributes['paginationType'] ?? '';
|
||||
$instant_pagination = self::TYPE_INSTANT_PAGINATION === $pagination_type;
|
||||
$query_type = $attributes['queryType'] ?? self::TYPE_WP_QUERY;
|
||||
$query_data = self::get_query_data(
|
||||
$query_type,
|
||||
$attributes,
|
||||
$block,
|
||||
$page
|
||||
);
|
||||
|
||||
if ( $instant_pagination ) {
|
||||
if ( ! wp_script_is( 'generateblocks-looper', 'enqueued' ) ) {
|
||||
$asset_info = generateblocks_get_enqueue_assets( 'generateblocks-looper' );
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-looper',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/looper.js',
|
||||
$asset_info['dependencies'],
|
||||
$asset_info['version'],
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$max_pages = $query_data['max_num_pages'] ?? $query_data['data']->max_num_pages ?? 0;
|
||||
|
||||
$parsed_content = (
|
||||
new WP_Block(
|
||||
$block->parsed_block,
|
||||
array(
|
||||
'generateblocks/queryData' => [
|
||||
'id' => $attributes['uniqueId'] ?? '',
|
||||
'noResults' => $query_data['no_results'],
|
||||
'data' => $query_data['data'],
|
||||
'args' => $query_data['args'],
|
||||
'type' => $query_type,
|
||||
'maxPages' => $max_pages,
|
||||
'inherit' => $attributes['inheritQuery'] ?? false,
|
||||
'paginationType' => $pagination_type,
|
||||
],
|
||||
)
|
||||
)
|
||||
)->render( array( 'dynamic' => false ) );
|
||||
|
||||
if ( $instant_pagination && class_exists( 'WP_HTML_Tag_Processor' ) ) {
|
||||
$processor = new WP_HTML_Tag_Processor( $parsed_content );
|
||||
|
||||
if ( $processor->next_tag( $attributes['tagName'] ) ) {
|
||||
$processor->set_attribute( 'data-gb-router-region', $query_id );
|
||||
$parsed_content = $processor->get_updated_html();
|
||||
}
|
||||
}
|
||||
|
||||
// Add styles to this block if needed.
|
||||
$output = generateblocks_maybe_add_block_css(
|
||||
'',
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
$output .= $parsed_content;
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Element block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Element block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Shape extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/shape';
|
||||
|
||||
/**
|
||||
* Render the Shape block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
// Add styles to this block if needed.
|
||||
$block_content = generateblocks_maybe_add_block_css(
|
||||
$block_content,
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the Text block.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Text block.
|
||||
*/
|
||||
class GenerateBlocks_Block_Text extends GenerateBlocks_Block {
|
||||
/**
|
||||
* Keep track of all blocks of this type on the page.
|
||||
*
|
||||
* @var array $block_ids The current block id.
|
||||
*/
|
||||
protected static $block_ids = [];
|
||||
|
||||
/**
|
||||
* Store our block name.
|
||||
*
|
||||
* @var string $block_name The block name.
|
||||
*/
|
||||
public static $block_name = 'generateblocks/text';
|
||||
|
||||
/**
|
||||
* Render the Text block.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block.
|
||||
*/
|
||||
public static function render_block( $attributes, $block_content, $block ) {
|
||||
// Add styles to this block if needed.
|
||||
$block_content = generateblocks_maybe_add_block_css(
|
||||
$block_content,
|
||||
[
|
||||
'class_name' => __CLASS__,
|
||||
'attributes' => $attributes,
|
||||
'block_ids' => self::$block_ids,
|
||||
]
|
||||
);
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
/**
|
||||
* Builds our dynamic CSS.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates minified css via PHP.
|
||||
*/
|
||||
class GenerateBlocks_Dynamic_CSS {
|
||||
|
||||
/**
|
||||
* The css selector that you're currently adding rules to
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_selector = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* Stores the final css output with all of its rules for the current selector.
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_selector_output = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* Stores all of the rules that will be added to the selector
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_css = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* The string that holds all of the css to output
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_output = array(); // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
|
||||
|
||||
/**
|
||||
* Sets a selector to the object and changes the current selector to a new one
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $selector - the css identifier of the html that you wish to target.
|
||||
* @return $this
|
||||
*/
|
||||
public function set_selector( $selector = '' ) {
|
||||
// Render the css in the output string everytime the selector changes.
|
||||
if ( '' !== $this->_selector ) {
|
||||
$this->add_selector_rules_to_output();
|
||||
}
|
||||
|
||||
$this->_selector = $selector;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a css property with value to the css output
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $property - the css property.
|
||||
* @param string $value - the value to be placed with the property.
|
||||
* @param string $unit - the unit for the value (px).
|
||||
* @return $this
|
||||
*/
|
||||
public function add_property( $property, $value, $unit = false ) {
|
||||
if ( empty( $value ) && ! is_numeric( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
is_array( $value ) &&
|
||||
! array_filter(
|
||||
$value,
|
||||
function( $v ) {
|
||||
return is_numeric( $v ) || $v;
|
||||
}
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
$valueTop = generateblocks_has_number_value( $value[0] );
|
||||
$valueRight = generateblocks_has_number_value( $value[1] );
|
||||
$valueBottom = generateblocks_has_number_value( $value[2] );
|
||||
$valueLeft = generateblocks_has_number_value( $value[3] );
|
||||
|
||||
if ( $valueTop && $valueRight && $valueBottom && $valueLeft ) {
|
||||
$value = generateblocks_get_shorthand_css( $value[0], $value[1], $value[2], $value[3], $unit );
|
||||
|
||||
if ( 'border-width' === $property ) {
|
||||
$this->_css .= 'border-style: solid;';
|
||||
}
|
||||
|
||||
$this->_css .= $property . ':' . $value . ';';
|
||||
return $this;
|
||||
} else {
|
||||
if ( $valueTop ) {
|
||||
$property_top = $property . '-top';
|
||||
$unit_top = $unit;
|
||||
|
||||
if ( 'border-radius' === $property ) {
|
||||
$property_top = 'border-top-left-radius';
|
||||
} elseif ( 'border-width' === $property ) {
|
||||
$property_top = 'border-top-width';
|
||||
$this->_css .= 'border-top-style: solid;';
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $value[0] ) || 0 === $value[0] || '0' === $value[0] ) {
|
||||
$unit_top = '';
|
||||
}
|
||||
|
||||
$this->_css .= $property_top . ':' . $value[0] . $unit_top . ';';
|
||||
}
|
||||
|
||||
if ( $valueRight ) {
|
||||
$property_right = $property . '-right';
|
||||
$unit_right = $unit;
|
||||
|
||||
if ( 'border-radius' === $property ) {
|
||||
$property_right = 'border-top-right-radius';
|
||||
} elseif ( 'border-width' === $property ) {
|
||||
$property_right = 'border-right-width';
|
||||
$this->_css .= 'border-right-style: solid;';
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $value[1] ) || 0 === $value[1] || '0' === $value[1] ) {
|
||||
$unit_right = '';
|
||||
}
|
||||
|
||||
$this->_css .= $property_right . ':' . $value[1] . $unit_right . ';';
|
||||
}
|
||||
|
||||
if ( $valueBottom ) {
|
||||
$property_bottom = $property . '-bottom';
|
||||
$unit_bottom = $unit;
|
||||
|
||||
if ( 'border-radius' === $property ) {
|
||||
$property_bottom = 'border-bottom-right-radius';
|
||||
} elseif ( 'border-width' === $property ) {
|
||||
$property_bottom = 'border-bottom-width';
|
||||
$this->_css .= 'border-bottom-style: solid;';
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $value[2] ) || 0 === $value[2] || '0' === $value[2] ) {
|
||||
$unit_bottom = '';
|
||||
}
|
||||
|
||||
$this->_css .= $property_bottom . ':' . $value[2] . $unit_bottom . ';';
|
||||
}
|
||||
|
||||
if ( $valueLeft ) {
|
||||
$property_left = $property . '-left';
|
||||
$unit_left = $unit;
|
||||
|
||||
if ( 'border-radius' === $property ) {
|
||||
$property_left = 'border-bottom-left-radius';
|
||||
} elseif ( 'border-width' === $property ) {
|
||||
$property_left = 'border-left-width';
|
||||
$this->_css .= 'border-left-style: solid;';
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $value[3] ) || 0 === $value[3] || '0' === $value[3] ) {
|
||||
$unit_left = '';
|
||||
}
|
||||
|
||||
$this->_css .= $property_left . ':' . $value[3] . $unit_left . ';';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
// Add our unit to our value if it exists.
|
||||
if ( $unit && is_numeric( $value ) ) {
|
||||
$value = $value . $unit;
|
||||
}
|
||||
|
||||
$this->_css .= $property . ':' . $value . ';';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current selector rules to the output variable
|
||||
*
|
||||
* @access private
|
||||
* @since 1.0
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function add_selector_rules_to_output() {
|
||||
if ( ! empty( $this->_css ) ) {
|
||||
$this->_selector_output = $this->_selector;
|
||||
$this->_output[ $this->_selector_output ][] = $this->_css;
|
||||
$this->_output[ $this->_selector_output ] = array_unique( $this->_output[ $this->_selector_output ] );
|
||||
|
||||
// Reset the css.
|
||||
$this->_css = '';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minified css in the $_output variable
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function css_output() {
|
||||
// Add current selector's rules to output.
|
||||
$this->add_selector_rules_to_output();
|
||||
|
||||
return $this->_output;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,875 @@
|
||||
<?php
|
||||
/**
|
||||
* Dynamic tag security utilities.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Dynamic_Tag_Security
|
||||
*/
|
||||
class GenerateBlocks_Dynamic_Tag_Security extends GenerateBlocks_Singleton {
|
||||
const DISALLOWED_KEYS = [
|
||||
'post_password',
|
||||
'password',
|
||||
'user_pass',
|
||||
'user_activation_key',
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialize all hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'wp_insert_post_data', [ $this, 'validate_content_before_save' ], 10, 2 );
|
||||
add_action( 'init', [ $this, 'register_rest_validation_for_post_types' ], 999 );
|
||||
add_filter( 'rest_pre_dispatch', [ $this, 'validate_autosave_rest_request' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate dynamic tag usage in post content at save time.
|
||||
*
|
||||
* Prevents restricted meta keys from being saved inside GenerateBlocks dynamic tags.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content The post content to validate.
|
||||
* @return true|WP_Error True if valid, WP_Error if validation fails.
|
||||
*/
|
||||
public static function validate_content( $content ) {
|
||||
return self::validate_content_with_existing_signatures( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate content while allowing previously existing violations.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content The post content to validate.
|
||||
* @param array $existing_signatures List of violation signatures already stored for this post.
|
||||
* @return true|WP_Error
|
||||
*/
|
||||
public static function validate_content_with_existing_signatures( $content, $existing_signatures = [] ) {
|
||||
return self::validate_content_internal( $content, $existing_signatures );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve restricted reference signatures present in content.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content Serialized post content.
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function get_restricted_reference_signatures( $content ) {
|
||||
$violations = self::get_violation_errors( $content );
|
||||
$signatures = [];
|
||||
|
||||
foreach ( $violations as $signature => $details ) {
|
||||
$signatures[ $signature ] = (int) ( $details['count'] ?? 0 );
|
||||
}
|
||||
|
||||
return $signatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given piece of content should be scanned for dynamic tag usage.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content The post content to evaluate.
|
||||
* @return bool True when validation should proceed.
|
||||
*/
|
||||
public static function should_validate_content( $content ) {
|
||||
$content = self::normalize_serialized_content( $content );
|
||||
|
||||
if ( '' === $content ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$has_dynamic_tag_syntax = false !== strpos( $content, '{{' );
|
||||
$has_dynamic_attributes = false;
|
||||
|
||||
if ( class_exists( 'GenerateBlocks_Dynamic_Content' ) && method_exists( 'GenerateBlocks_Dynamic_Content', 'content_has_dynamic_attribute_markers' ) ) {
|
||||
$has_dynamic_attributes = (bool) GenerateBlocks_Dynamic_Content::content_has_dynamic_attribute_markers( $content );
|
||||
}
|
||||
|
||||
if ( ! $has_dynamic_tag_syntax ) {
|
||||
if ( $has_dynamic_attributes ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) apply_filters( 'generateblocks_force_validate_content', false, $content );
|
||||
}
|
||||
|
||||
$block_names = self::get_dynamic_tag_enabled_blocks();
|
||||
|
||||
if ( empty( $block_names ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $block_names as $block_name ) {
|
||||
if ( function_exists( 'has_block' ) && has_block( $block_name, $content ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return (bool) apply_filters( 'generateblocks_force_validate_content', false, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract violation errors keyed by signature.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content Serialized post content.
|
||||
* @return array<string, array{error:WP_Error,count:int}>
|
||||
*/
|
||||
protected static function get_violation_errors( $content ) {
|
||||
$content = self::normalize_serialized_content( $content );
|
||||
|
||||
if ( '' === $content ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return self::scan_content_for_violations( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate content while considering existing violations.
|
||||
*
|
||||
* @param string $content Content to validate.
|
||||
* @param array $existing_signatures Previously stored violation signatures.
|
||||
* @return true|WP_Error
|
||||
*/
|
||||
protected static function validate_content_internal( $content, $existing_signatures = [] ) {
|
||||
$content = self::normalize_serialized_content( $content );
|
||||
$violations = self::get_violation_errors( $content );
|
||||
|
||||
if ( empty( $violations ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! empty( $existing_signatures ) ) {
|
||||
$existing_counts = self::normalize_signature_counts( $existing_signatures );
|
||||
|
||||
foreach ( $violations as $signature => $details ) {
|
||||
$count = (int) ( $details['count'] ?? 0 );
|
||||
$allowance = isset( $existing_counts[ $signature ] ) ? (int) $existing_counts[ $signature ] : 0;
|
||||
|
||||
if ( $count > $allowance ) {
|
||||
return $details['error'];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$first_violation = reset( $violations );
|
||||
|
||||
return $first_violation['error'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a violation signature for comparison.
|
||||
*
|
||||
* @param string $type Type of violation (user_meta/post_meta/term_meta).
|
||||
* @param string $field_name Field name involved.
|
||||
* @return string
|
||||
*/
|
||||
protected static function format_violation_signature( $type, $field_name ) {
|
||||
$type = is_string( $type ) ? strtolower( trim( $type ) ) : '';
|
||||
$field_name = is_string( $field_name ) ? strtolower( trim( $field_name ) ) : '';
|
||||
|
||||
if ( '' === $type || '' === $field_name ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return "{$type}:{$field_name}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize stored signature data into signature => count pairs.
|
||||
*
|
||||
* @param array $signatures Raw signature data.
|
||||
* @return array<string,int>
|
||||
*/
|
||||
protected static function normalize_signature_counts( $signatures ) {
|
||||
if ( empty( $signatures ) || ! is_array( $signatures ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$normalized = [];
|
||||
|
||||
foreach ( $signatures as $key => $value ) {
|
||||
if ( is_string( $key ) && is_numeric( $value ) ) {
|
||||
$normalized[ $key ] = max( 0, (int) $value );
|
||||
} elseif ( is_string( $value ) ) {
|
||||
$normalized[ $value ] = isset( $normalized[ $value ] ) ? $normalized[ $value ] + 1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store or increment a violation entry.
|
||||
*
|
||||
* @param array $violations Reference to violations array.
|
||||
* @param string $signature Signature key.
|
||||
* @param WP_Error $error Violation error.
|
||||
* @param int $increment Optional increment amount.
|
||||
* @return void
|
||||
*/
|
||||
protected static function add_violation_entry( array &$violations, $signature, $error, $increment = 1 ) {
|
||||
if ( ! isset( $violations[ $signature ] ) ) {
|
||||
$violations[ $signature ] = [
|
||||
'error' => $error,
|
||||
'count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$violations[ $signature ]['count'] += max( 1, (int) $increment );
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan content and collect violation errors.
|
||||
*
|
||||
* @param string $content Serialized block content.
|
||||
* @return array<string, array{error:WP_Error,count:int}>
|
||||
*/
|
||||
protected static function scan_content_for_violations( $content ) {
|
||||
$content = self::normalize_serialized_content( $content );
|
||||
$violations = [];
|
||||
$rules = self::get_dynamic_tag_validation_rules();
|
||||
|
||||
foreach ( $rules as $rule_key => $rule ) {
|
||||
if ( ! self::should_enforce_rule( $rule ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $rule['pattern'] ) && preg_match_all( $rule['pattern'], $content, $matches ) ) {
|
||||
$field_matches = ! empty( $matches[1] ) ? $matches[1] : ( $matches[0] ?? [] );
|
||||
|
||||
foreach ( $field_matches as $field_name ) {
|
||||
if ( isset( $rule['fixed_field'] ) ) {
|
||||
$field_name = $rule['fixed_field'];
|
||||
}
|
||||
|
||||
$result = call_user_func( $rule['validator'], $field_name );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$signature = self::format_violation_signature( $rule_key, $field_name );
|
||||
|
||||
if ( $signature ) {
|
||||
self::add_violation_entry( $violations, $signature, $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $rule['link_tokens'] ) ) {
|
||||
$link_violations = self::collect_meta_link_target_violations(
|
||||
$content,
|
||||
$rule_key,
|
||||
array_fill_keys( $rule['link_tokens'], $rule['validator'] )
|
||||
);
|
||||
|
||||
foreach ( $link_violations as $signature => $details ) {
|
||||
self::add_violation_entry(
|
||||
$violations,
|
||||
$signature,
|
||||
$details['error'],
|
||||
$details['count'] ?? 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
class_exists( 'GenerateBlocks_Dynamic_Content' ) &&
|
||||
method_exists( 'GenerateBlocks_Dynamic_Content', 'get_dynamic_attribute_violation_items' )
|
||||
) {
|
||||
$attribute_violations = GenerateBlocks_Dynamic_Content::get_dynamic_attribute_violation_items( $content );
|
||||
|
||||
foreach ( $attribute_violations as $violation ) {
|
||||
$type = $violation['type'] ?? '';
|
||||
$field_name = $violation['field'] ?? '';
|
||||
$error = $violation['error'] ?? null;
|
||||
|
||||
if ( ! $error instanceof WP_Error ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$signature = self::format_violation_signature( $type, $field_name );
|
||||
|
||||
if ( $signature ) {
|
||||
self::add_violation_entry( $violations, $signature, $error );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $violations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve dynamic tag validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_dynamic_tag_validation_rules() {
|
||||
$rules = [
|
||||
'user_meta' => [
|
||||
'pattern' => '/\{\{(?:user_meta|author_meta)(?:\s+|\|)+[^}]*key:([^|}]+)[^}]*}}/i',
|
||||
'validator' => [ self::class, 'validate_user_meta_field_name' ],
|
||||
'link_tokens' => [ 'author_meta', 'author_email' ],
|
||||
'bypass_cap' => 'list_users',
|
||||
],
|
||||
'post_meta' => [
|
||||
'pattern' => '/\{\{post_meta(?:\s+|\|)+[^}]*key:([^|}]+)[^}]*}}/i',
|
||||
'validator' => [ self::class, 'validate_post_meta_field_name' ],
|
||||
'link_tokens' => [ 'post_meta' ],
|
||||
],
|
||||
'term_meta' => [
|
||||
'pattern' => '/\{\{term_meta(?:\s+|\|)+[^}]*key:([^|}]+)[^}]*}}/i',
|
||||
'validator' => [ self::class, 'validate_term_meta_field_name' ],
|
||||
'link_tokens' => [ 'term_meta' ],
|
||||
],
|
||||
];
|
||||
|
||||
return apply_filters( 'generateblocks_dynamic_tag_validation_rules', $rules );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if current user should validate user meta fields.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function should_validate_user_meta_fields() {
|
||||
$rules = self::get_dynamic_tag_validation_rules();
|
||||
$rule = $rules['user_meta'] ?? null;
|
||||
|
||||
if ( ! $rule ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return self::should_enforce_rule( $rule );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current user should be subject to a validation rule.
|
||||
*
|
||||
* @param array $rule Rule definition.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function should_enforce_rule( $rule ) {
|
||||
if ( empty( $rule['bypass_cap'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! current_user_can( $rule['bypass_cap'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single user meta field name against safe/disallowed lists.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $field_name The meta key to validate.
|
||||
* @return true|WP_Error True if valid, WP_Error on violation.
|
||||
*/
|
||||
public static function validate_user_meta_field_name( $field_name ) {
|
||||
$field_name = is_string( $field_name ) ? trim( $field_name ) : '';
|
||||
|
||||
if ( '' === $field_name ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( is_protected_meta( $field_name, 'user' ) ) {
|
||||
return new WP_Error(
|
||||
'restricted_user_meta',
|
||||
sprintf(
|
||||
/* translators: %s: The restricted field name */
|
||||
__( 'You do not have permission to use the field "%s" in dynamic tags. Fields starting with underscore are protected.', 'generateblocks' ),
|
||||
esc_html( $field_name )
|
||||
),
|
||||
[ 'status' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( $field_name, self::DISALLOWED_KEYS, true ) ) {
|
||||
return new WP_Error(
|
||||
'restricted_user_meta',
|
||||
sprintf(
|
||||
/* translators: %s: The restricted field name */
|
||||
__( 'You do not have permission to use the field "%s" in dynamic tags. This field is restricted for security reasons.', 'generateblocks' ),
|
||||
esc_html( $field_name )
|
||||
),
|
||||
[ 'status' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! in_array( $field_name, self::get_safe_user_meta_keys(), true ) ) {
|
||||
return new WP_Error(
|
||||
'restricted_user_meta',
|
||||
sprintf(
|
||||
/* translators: %s: The restricted field name */
|
||||
__( 'You do not have permission to use the field "%s" in dynamic tags. Only administrators can use custom user fields.', 'generateblocks' ),
|
||||
esc_html( $field_name )
|
||||
),
|
||||
[ 'status' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a post meta key (blocks underscore-prefixed fields).
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $field_name Meta key to validate.
|
||||
* @return true|WP_Error True if valid key, WP_Error if restricted.
|
||||
*/
|
||||
public static function validate_post_meta_field_name( $field_name ) {
|
||||
$field_name = is_string( $field_name ) ? trim( $field_name ) : '';
|
||||
|
||||
if ( '' === $field_name ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( is_protected_meta( $field_name, 'post' ) ) {
|
||||
return new WP_Error(
|
||||
'restricted_post_meta',
|
||||
sprintf(
|
||||
/* translators: %s: The restricted field name */
|
||||
__( 'You do not have permission to use the post meta field "%s" in dynamic tags. Fields starting with underscore are protected.', 'generateblocks' ),
|
||||
esc_html( $field_name )
|
||||
),
|
||||
[ 'status' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a term meta key (blocks underscore-prefixed fields).
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $field_name Meta key to validate.
|
||||
* @return true|WP_Error True if valid key, WP_Error if restricted.
|
||||
*/
|
||||
public static function validate_term_meta_field_name( $field_name ) {
|
||||
$field_name = is_string( $field_name ) ? trim( $field_name ) : '';
|
||||
|
||||
if ( '' === $field_name ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( is_protected_meta( $field_name, 'term' ) ) {
|
||||
return new WP_Error(
|
||||
'restricted_term_meta',
|
||||
sprintf(
|
||||
/* translators: %s: The restricted field name */
|
||||
__( 'You do not have permission to use the term meta field "%s" in dynamic tags. Fields starting with underscore are protected.', 'generateblocks' ),
|
||||
esc_html( $field_name )
|
||||
),
|
||||
[ 'status' => 403 ]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect link target violations for dynamic tags.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string $content Serialized post content.
|
||||
* @param string $rule_key The base rule key (user_meta/post_meta/term_meta).
|
||||
* @param callable[] $validators Validators keyed by link token.
|
||||
* @return array<string, array{error:WP_Error,count:int}>
|
||||
*/
|
||||
protected static function collect_meta_link_target_violations( $content, $rule_key, $validators ) {
|
||||
$content = self::normalize_serialized_content( $content );
|
||||
$violations = [];
|
||||
|
||||
if ( '' === $content ) {
|
||||
return $violations;
|
||||
}
|
||||
|
||||
if ( empty( $validators ) || ! is_array( $validators ) ) {
|
||||
return $violations;
|
||||
}
|
||||
|
||||
if ( ! preg_match_all( '/\{\{([^}]+)\}\}/', $content, $tag_matches ) ) {
|
||||
return $violations;
|
||||
}
|
||||
|
||||
foreach ( $tag_matches[1] as $tag_body ) {
|
||||
$parts = preg_split( '/(?:\s+|\|)+/', $tag_body, 2 );
|
||||
|
||||
if ( count( $parts ) < 2 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options_string = $parts[1];
|
||||
|
||||
if ( false === stripos( $options_string, 'link:' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( explode( '|', $options_string ) as $option ) {
|
||||
$option = trim( $option );
|
||||
|
||||
if ( '' === $option || stripos( $option, 'link:' ) !== 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$link_value = trim( substr( $option, 5 ) );
|
||||
|
||||
if ( '' === $link_value ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$link_parts = array_map( 'trim', explode( ',', $link_value ) );
|
||||
$target = strtolower( $link_parts[0] ?? '' );
|
||||
$field_name = $link_parts[1] ?? '';
|
||||
|
||||
if ( 'author_email' === $target ) {
|
||||
$field_name = 'user_email';
|
||||
}
|
||||
|
||||
if ( ! isset( $validators[ $target ] ) || ! is_callable( $validators[ $target ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = call_user_func( $validators[ $target ], $field_name );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$signature = self::format_violation_signature( $rule_key, $field_name );
|
||||
|
||||
if ( $signature ) {
|
||||
self::add_violation_entry( $violations, $signature, $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $violations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get safe user meta keys accessible without list_users capability.
|
||||
*
|
||||
* @since 2.1.4
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function get_safe_user_meta_keys() {
|
||||
$safe_keys = [
|
||||
'description',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'nickname',
|
||||
'display_name',
|
||||
'user_nicename',
|
||||
'user_url',
|
||||
'locale',
|
||||
'show_admin_bar_front',
|
||||
];
|
||||
|
||||
$safe_keys = apply_filters( 'generateblocks_safe_user_meta_keys', $safe_keys );
|
||||
|
||||
return array_values( array_unique( array_filter( $safe_keys, 'is_string' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of blocks that support GenerateBlocks dynamic tags.
|
||||
*
|
||||
* Mirrors the filter used when replacing tags so integrations stay in sync.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected static function get_dynamic_tag_enabled_blocks() {
|
||||
$blocks = [];
|
||||
|
||||
if ( class_exists( 'GenerateBlocks_Dynamic_Tags' ) ) {
|
||||
$dynamic_tags = GenerateBlocks_Dynamic_Tags::get_instance();
|
||||
|
||||
if ( method_exists( $dynamic_tags, 'get_allowed_blocks' ) ) {
|
||||
$blocks = $dynamic_tags->get_allowed_blocks();
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_array( $blocks ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(
|
||||
array_filter(
|
||||
$blocks,
|
||||
function( $block_name ) {
|
||||
return is_string( $block_name ) && '' !== $block_name;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch violation signatures from an existing post.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_existing_content_signatures( $post_id ) {
|
||||
if ( ! $post_id ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post || is_wp_error( $post ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return self::get_restricted_reference_signatures( $post->post_content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate post content before database insert.
|
||||
*
|
||||
* This filter runs BEFORE the post is saved to the database, allowing us to
|
||||
* prevent the save by triggering an error. Handles classic editor, quick edit,
|
||||
* and other non-REST saves.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param array $data An array of slashed, sanitized, and processed post data.
|
||||
* @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data.
|
||||
* @return array The post data array (unmodified if validation passes).
|
||||
*/
|
||||
public function validate_content_before_save( $data, $postarr ) {
|
||||
// Admins already have unfiltered_html, so skip enforcement.
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// REST requests are validated via rest_pre_insert hooks.
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Skip if no content.
|
||||
if ( empty( $data['post_content'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Skip classic revisions but continue validating autosaves.
|
||||
if ( ! empty( $postarr['ID'] ) ) {
|
||||
$is_revision = wp_is_post_revision( $postarr['ID'] );
|
||||
$is_autosave = wp_is_post_autosave( $postarr['ID'] );
|
||||
|
||||
if ( $is_revision && ! $is_autosave ) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! self::should_validate_content( $data['post_content'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$existing_signatures = [];
|
||||
|
||||
if ( ! empty( $postarr['ID'] ) ) {
|
||||
$existing_signatures = self::get_existing_content_signatures( (int) $postarr['ID'] );
|
||||
}
|
||||
|
||||
// Validate the content.
|
||||
$result = self::validate_content_with_existing_signatures( $data['post_content'], $existing_signatures );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
// Block the save with a clear error message.
|
||||
wp_die(
|
||||
esc_html( $result->get_error_message() ),
|
||||
esc_html( __( 'Content Validation Failed', 'generateblocks' ) ),
|
||||
array(
|
||||
'response' => 403,
|
||||
'back_link' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate post content before REST API insert/update.
|
||||
*
|
||||
* This handles Gutenberg saves and autosaves via REST API.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param stdClass $prepared_post An object representing a single post prepared for inserting or updating the database.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return stdClass|WP_Error The prepared post object or WP_Error on validation failure.
|
||||
*/
|
||||
public function validate_content_rest( $prepared_post, $request ) {
|
||||
// Admins already have unfiltered_html, so skip enforcement.
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return $prepared_post;
|
||||
}
|
||||
|
||||
// Skip if no content.
|
||||
$content = $prepared_post->post_content ?? '';
|
||||
if ( empty( $content ) ) {
|
||||
return $prepared_post;
|
||||
}
|
||||
|
||||
if ( ! self::should_validate_content( $content ) ) {
|
||||
return $prepared_post;
|
||||
}
|
||||
|
||||
$post_id = 0;
|
||||
|
||||
if ( ! empty( $prepared_post->ID ) ) {
|
||||
$post_id = (int) $prepared_post->ID;
|
||||
} elseif ( $request instanceof WP_REST_Request && $request->get_param( 'id' ) ) {
|
||||
$post_id = (int) $request->get_param( 'id' );
|
||||
}
|
||||
|
||||
$existing_signatures = self::get_existing_content_signatures( $post_id );
|
||||
|
||||
// Validate the content.
|
||||
$result = self::validate_content_with_existing_signatures( $content, $existing_signatures );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
// Return the error - Gutenberg will display it in the editor.
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $prepared_post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API validation hooks for all public post types.
|
||||
*
|
||||
* This ensures validation runs for posts, pages, and custom post types that support the block editor.
|
||||
*
|
||||
* @since 2.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function register_rest_validation_for_post_types() {
|
||||
$post_types = get_post_types(
|
||||
array(
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'names'
|
||||
);
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
add_filter( "rest_pre_insert_{$post_type}", [ $this, 'validate_content_rest' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept Gutenberg autosave REST requests and validate their content.
|
||||
*
|
||||
* Autosaves use the /wp/v2/{post-type}/{id}/autosaves endpoint, which bypasses
|
||||
* the regular rest_pre_insert_{post_type} filters. Hooking rest_pre_dispatch allows
|
||||
* us to run the same validation logic and return a proper WP_Error response so
|
||||
* the editor surfaces the issue immediately.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param mixed $response Response to replace the requested version with. Default false.
|
||||
* @param WP_REST_Server $server Server instance.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
* @return mixed Either the original response or a WP_Error to halt dispatch.
|
||||
*/
|
||||
public function validate_autosave_rest_request( $response, $server, $request ) {
|
||||
if ( $response ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( 'POST' !== $request->get_method() ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$route = $request->get_route();
|
||||
|
||||
// Only run on wp/v2 autosave endpoints.
|
||||
if ( ! is_string( $route ) || ! preg_match( '#^/wp/v2/([a-z0-9_-]+)/(\d+)/autosaves/?$#', $route, $route_matches ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$content_param = $request->get_param( 'content' );
|
||||
$content = '';
|
||||
|
||||
if ( is_array( $content_param ) ) {
|
||||
$content = isset( $content_param['raw'] ) ? (string) $content_param['raw'] : '';
|
||||
} elseif ( is_string( $content_param ) ) {
|
||||
$content = $content_param;
|
||||
}
|
||||
|
||||
if ( '' === $content ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( ! self::should_validate_content( $content ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$post_id = (int) ( $route_matches[2] ?? 0 );
|
||||
$existing_signatures = self::get_existing_content_signatures( $post_id );
|
||||
|
||||
$result = self::validate_content_with_existing_signatures( $content, $existing_signatures );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
if ( ! $result->get_error_data() ) {
|
||||
$result->add_data( array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize serialized block content so validation sees unslashed values.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param mixed $content Possibly slashed content string.
|
||||
* @return string Normalized content string.
|
||||
*/
|
||||
public static function normalize_serialized_content( $content ) {
|
||||
if ( ! is_string( $content ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$normalized = function_exists( 'wp_unslash' ) ? wp_unslash( $content ) : stripslashes( $content );
|
||||
|
||||
return trim( (string) $normalized );
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Dynamic_Tag_Security::get_instance()->init();
|
||||
@@ -0,0 +1,526 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles the CSS Output.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our block CSS to the current page.
|
||||
*/
|
||||
class GenerateBlocks_Enqueue_CSS {
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* @access private
|
||||
* @var object Instance
|
||||
* @since 0.1
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Check to see if we've made this CSS in this instance.
|
||||
* This should only run on first load if needed.
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private static $has_made_css = false;
|
||||
|
||||
/**
|
||||
* Check to see if we've enqueued our CSS.
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private static $has_enqueued_css = false;
|
||||
|
||||
/**
|
||||
* Initiator.
|
||||
*
|
||||
* @since 0.1
|
||||
* @return object initialized object of class.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->add_options();
|
||||
|
||||
add_action( 'save_post', array( $this, 'post_update_option' ), 10, 2 );
|
||||
add_action( 'save_post_wp_block', array( $this, 'wp_block_update' ), 10, 2 );
|
||||
add_action( 'init', array( $this, 'enqueue_assets' ) );
|
||||
add_filter( 'widget_update_callback', array( $this, 'force_file_regen_on_widget_save' ) );
|
||||
add_action( 'customize_save_after', array( $this, 'force_file_regen_on_customizer_save' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell our system it's ok to generate CSS.
|
||||
*/
|
||||
private function enable_enqueue() {
|
||||
self::$has_enqueued_css = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we can generate CSS.
|
||||
*/
|
||||
public static function can_enqueue() {
|
||||
return self::$has_enqueued_css || ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our front-end assets.
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$dynamic_css_priority = apply_filters( 'generateblocks_dynamic_css_priority', 25 );
|
||||
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dynamic_css' ), $dynamic_css_priority );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'print_inline_css' ), $dynamic_css_priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page ID.
|
||||
*/
|
||||
public function page_id() {
|
||||
global $post;
|
||||
|
||||
$id = isset( $post ) ? $post->ID : false;
|
||||
$id = ( ! is_singular() ) ? false : $id;
|
||||
$id = ( function_exists( 'is_shop' ) && is_shop() ) ? get_option( 'woocommerce_shop_page_id' ) : $id;
|
||||
$id = ( is_home() ) ? get_option( 'page_for_posts' ) : $id;
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we're using file mode or inline mode.
|
||||
*/
|
||||
public function mode() {
|
||||
// Check if we're using file mode or inline mode.
|
||||
// Default to file mode and fallback to inline if file mode is not possible.
|
||||
$mode = apply_filters( 'generateblocks_css_print_method', 'file' );
|
||||
|
||||
if (
|
||||
( function_exists( 'is_customize_preview' ) && is_customize_preview() )
|
||||
||
|
||||
is_preview()
|
||||
||
|
||||
// AMP inlines all CSS, so inlining from the start improves CSS processing performance.
|
||||
( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() )
|
||||
) {
|
||||
return 'inline';
|
||||
}
|
||||
|
||||
// Additional checks for file mode.
|
||||
if ( 'file' === $mode && $this->needs_update() ) {
|
||||
// Only allow processing 1 file every 5 seconds.
|
||||
$current_time = (int) time();
|
||||
$last_time = (int) get_option( 'generateblocks_dynamic_css_time' );
|
||||
|
||||
if ( 5 <= ( $current_time - $last_time ) ) {
|
||||
// Attempt to write to the file.
|
||||
$mode = ( $this->can_write() && $this->make_css() ) ? 'file' : 'inline';
|
||||
|
||||
// Does again if the file exists.
|
||||
if ( 'file' === $mode ) {
|
||||
$mode = ( file_exists( $this->file( 'path' ) ) ) ? 'file' : 'inline';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the dynamic CSS.
|
||||
*/
|
||||
public function enqueue_dynamic_css() {
|
||||
$this->enable_enqueue();
|
||||
$page_id = $this->page_id();
|
||||
|
||||
if ( ! $page_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_generateblocks = get_post_meta( $page_id, '_generateblocks_dynamic_css_version', true );
|
||||
|
||||
if ( empty( $has_generateblocks ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'file' === $this->mode() ) {
|
||||
if ( ! self::$has_made_css ) {
|
||||
// Store our block IDs based on the content we find.
|
||||
generateblocks_get_dynamic_css( '', true );
|
||||
}
|
||||
|
||||
wp_enqueue_style( 'generateblocks', esc_url( $this->file( 'uri' ) ), array(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print our inline CSS.
|
||||
*/
|
||||
public function print_inline_css() {
|
||||
$this->enable_enqueue();
|
||||
|
||||
if ( 'inline' === $this->mode() || ! wp_style_is( 'generateblocks', 'enqueued' ) ) {
|
||||
// Build our CSS based on the content we find.
|
||||
generateblocks_get_dynamic_css();
|
||||
|
||||
$css = generateblocks_get_frontend_block_css();
|
||||
|
||||
if ( empty( $css ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a "dummy" handle we can add inline styles to.
|
||||
wp_register_style( 'generateblocks', false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_enqueue_style( 'generateblocks' );
|
||||
|
||||
$css = html_entity_decode( $css, ENT_QUOTES, 'UTF-8' );
|
||||
|
||||
wp_add_inline_style(
|
||||
'generateblocks',
|
||||
wp_strip_all_tags( $css ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make our CSS.
|
||||
*/
|
||||
public function make_css() {
|
||||
$page_id = $this->page_id();
|
||||
|
||||
if ( ! $page_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$has_generateblocks = get_post_meta( $page_id, '_generateblocks_dynamic_css_version', true );
|
||||
|
||||
if ( empty( $has_generateblocks ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build our CSS based on the content we find.
|
||||
generateblocks_get_dynamic_css();
|
||||
|
||||
$content = generateblocks_get_frontend_block_css();
|
||||
|
||||
if ( ! $content ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we only have a little CSS, we should inline it.
|
||||
$css_size = strlen( $content );
|
||||
|
||||
if ( $css_size < (int) apply_filters( 'generateblocks_css_inline_length', 500 ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filesystem = generateblocks_get_wp_filesystem();
|
||||
|
||||
if ( ! $filesystem ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take care of domain mapping.
|
||||
if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
|
||||
if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) {
|
||||
$mapped_domain = domain_mapping_siteurl( false );
|
||||
$original_domain = get_original_url( 'siteurl' );
|
||||
|
||||
$content = str_replace( $original_domain, $mapped_domain, $content );
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_writable( $this->file( 'path' ) ) || ( ! file_exists( $this->file( 'path' ) ) && is_writable( dirname( $this->file( 'path' ) ) ) ) ) {
|
||||
$chmod_file = 0644;
|
||||
|
||||
if ( defined( 'FS_CHMOD_FILE' ) ) {
|
||||
$chmod_file = FS_CHMOD_FILE;
|
||||
}
|
||||
|
||||
$content = html_entity_decode( $content, ENT_QUOTES, 'UTF-8' );
|
||||
|
||||
if ( ! $filesystem->put_contents( $this->file( 'path' ), wp_strip_all_tags( $content ), $chmod_file ) ) {
|
||||
|
||||
// Fail!
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
$option = get_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
$option[ $page_id ] = true;
|
||||
update_option( 'generateblocks_dynamic_css_posts', $option );
|
||||
|
||||
// Update the 'generateblocks_dynamic_css_time' option.
|
||||
$this->update_saved_time();
|
||||
|
||||
// Set flag.
|
||||
self::$has_made_css = true;
|
||||
|
||||
// Success!
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the CSS file is writable.
|
||||
*/
|
||||
public function can_write() {
|
||||
global $blog_id;
|
||||
|
||||
// Get the upload directory for this site.
|
||||
$upload_dir = wp_get_upload_dir();
|
||||
|
||||
// If this is a multisite installation, append the blogid to the filename.
|
||||
$css_blog_id = ( is_multisite() && $blog_id > 1 ) ? '_blog-' . $blog_id : null;
|
||||
$page_id = $this->page_id();
|
||||
|
||||
if ( ! $page_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_name = '/style' . $css_blog_id . '-' . $page_id . '.css';
|
||||
$folder_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'generateblocks';
|
||||
|
||||
// Does the folder exist?
|
||||
if ( file_exists( $folder_path ) ) {
|
||||
// Folder exists, but is the folder writable?
|
||||
if ( ! is_writable( $folder_path ) ) {
|
||||
// Folder is not writable.
|
||||
// Does the file exist?
|
||||
if ( ! file_exists( $folder_path . $file_name ) ) {
|
||||
// File does not exist, therefore it can't be created
|
||||
// since the parent folder is not writable.
|
||||
return false;
|
||||
} else {
|
||||
// File exists, but is it writable?
|
||||
if ( ! is_writable( $folder_path . $file_name ) ) {
|
||||
// Nope, it's not writable.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The folder is writable.
|
||||
// Does the file exist?
|
||||
if ( file_exists( $folder_path . $file_name ) ) {
|
||||
// File exists.
|
||||
// Is it writable?
|
||||
if ( ! is_writable( $folder_path . $file_name ) ) {
|
||||
// Nope, it's not writable.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Can we create the folder?
|
||||
// returns true if yes and false if not.
|
||||
return wp_mkdir_p( $folder_path );
|
||||
}
|
||||
|
||||
// all is well!
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the css path or url to the stylesheet
|
||||
*
|
||||
* @param string $target path/url.
|
||||
*/
|
||||
public function file( $target = 'path' ) {
|
||||
global $blog_id;
|
||||
|
||||
// Get the upload directory for this site.
|
||||
$upload_dir = wp_get_upload_dir();
|
||||
|
||||
// If this is a multisite installation, append the blogid to the filename.
|
||||
$css_blog_id = ( is_multisite() && $blog_id > 1 ) ? '_blog-' . $blog_id : null;
|
||||
$page_id = $this->page_id();
|
||||
|
||||
$file_name = 'style' . $css_blog_id . '-' . $page_id . '.css';
|
||||
$folder_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'generateblocks';
|
||||
|
||||
// The complete path to the file.
|
||||
$file_path = $folder_path . DIRECTORY_SEPARATOR . $file_name;
|
||||
|
||||
// Get the URL directory of the stylesheet.
|
||||
$css_uri_folder = $upload_dir['baseurl'];
|
||||
|
||||
$css_uri = trailingslashit( $css_uri_folder ) . 'generateblocks/' . $file_name;
|
||||
|
||||
// Take care of domain mapping.
|
||||
if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
|
||||
if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) {
|
||||
$mapped_domain = domain_mapping_siteurl( false );
|
||||
$original_domain = get_original_url( 'siteurl' );
|
||||
$css_uri = str_replace( $original_domain, $mapped_domain, $css_uri );
|
||||
}
|
||||
}
|
||||
|
||||
$css_uri = set_url_scheme( $css_uri );
|
||||
|
||||
if ( 'path' === $target ) {
|
||||
return $file_path;
|
||||
} elseif ( 'url' === $target || 'uri' === $target ) {
|
||||
$timestamp = ( file_exists( $file_path ) ) ? '?ver=' . filemtime( $file_path ) : '';
|
||||
return $css_uri . $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create settings.
|
||||
*/
|
||||
public function add_options() {
|
||||
/**
|
||||
* The 'generateblocks_dynamic_css_posts' option will hold an array of posts that have had their css generated.
|
||||
* We can use that to keep track of which pages need their CSS to be recreated and which don't.
|
||||
*/
|
||||
add_option( 'generateblocks_dynamic_css_posts', array(), '', 'yes' );
|
||||
|
||||
/**
|
||||
* The 'generateblocks_dynamic_css_time' option holds the time the file writer was last used.
|
||||
*/
|
||||
add_option( 'generateblocks_dynamic_css_time', time(), '', 'yes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the generateblocks_dynamic_css_posts option when a post is saved.
|
||||
*
|
||||
* @param int $post_id The current post ID.
|
||||
* @param object $post The current post.
|
||||
*/
|
||||
public function post_update_option( $post_id, $post ) {
|
||||
$is_autosave = wp_is_post_autosave( $post_id );
|
||||
$is_revision = wp_is_post_revision( $post_id );
|
||||
|
||||
if ( $is_autosave || $is_revision || ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
if ( isset( $post->post_content ) ) {
|
||||
if ( strpos( $post->post_content, 'wp:generateblocks' ) !== false ) {
|
||||
update_post_meta( $post_id, '_generateblocks_dynamic_css_version', sanitize_text_field( GENERATEBLOCKS_VERSION ) );
|
||||
} else {
|
||||
delete_post_meta( $post_id, '_generateblocks_dynamic_css_version' );
|
||||
}
|
||||
|
||||
// Store any re-usable block IDs on the page. We need this to regenerate CSS files later if the re-usable block is changed.
|
||||
$reusable_blocks = preg_match_all( '/wp:block {"ref":([^}]*)}/', $post->post_content, $matches );
|
||||
$stored_reusable_blocks = array();
|
||||
|
||||
foreach ( $matches[1] as $match ) {
|
||||
$stored_reusable_blocks[] = $match;
|
||||
}
|
||||
|
||||
if ( ! empty( $stored_reusable_blocks ) ) {
|
||||
$stored_reusable_blocks = array_map( 'intval', $stored_reusable_blocks );
|
||||
update_post_meta( $post_id, '_generateblocks_reusable_blocks', $stored_reusable_blocks );
|
||||
} else {
|
||||
delete_post_meta( $post_id, '_generateblocks_reusable_blocks' );
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new CSS file for this post on next load.
|
||||
$option = get_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
unset( $option[ $post_id ] );
|
||||
|
||||
update_option( 'generateblocks_dynamic_css_posts', $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Force regeneration of CSS files attached to pages with this re-usable block.
|
||||
*
|
||||
* @param int $post_id The current post ID.
|
||||
* @param object $post The current post.
|
||||
*/
|
||||
public function wp_block_update( $post_id, $post ) {
|
||||
$is_autosave = wp_is_post_autosave( $post_id );
|
||||
$is_revision = wp_is_post_revision( $post_id );
|
||||
|
||||
if ( $is_autosave || $is_revision || ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
if ( isset( $post->post_content ) ) {
|
||||
if ( strpos( $post->post_content, 'wp:generateblocks' ) !== false ) {
|
||||
global $wpdb;
|
||||
|
||||
$option = get_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
|
||||
$posts = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_generateblocks_reusable_blocks'" );
|
||||
|
||||
foreach ( (array) $posts as $id ) {
|
||||
unset( $option[ $id ] );
|
||||
}
|
||||
|
||||
update_option( 'generateblocks_dynamic_css_posts', $option );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do we need to update the CSS file?
|
||||
*/
|
||||
public function needs_update() {
|
||||
$option = get_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
$page_id = $this->page_id();
|
||||
|
||||
// If the CSS file does not exist then we definitely need to regenerate the CSS.
|
||||
if ( ! file_exists( $this->file( 'path' ) ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ( ! isset( $option[ $page_id ] ) || ! $option[ $page_id ] ) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the 'generateblocks_dynamic_css_time' option.
|
||||
*/
|
||||
public function update_saved_time() {
|
||||
update_option( 'generateblocks_dynamic_css_time', time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Force CSS files to regenerate after a widget has been saved.
|
||||
*
|
||||
* @param array $instance The current widget instance's settings.
|
||||
*/
|
||||
public function force_file_regen_on_widget_save( $instance ) {
|
||||
if ( function_exists( 'wp_use_widgets_block_editor' ) && wp_use_widgets_block_editor() ) {
|
||||
update_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force CSS files to regenerate after the Customizer has been saved.
|
||||
* This is necessary because force_file_regen_on_widget_save() doesn't fire in the Customizer for some reason.
|
||||
*
|
||||
* @todo Make this only happen when the widgets have been changed.
|
||||
*/
|
||||
public function force_file_regen_on_customizer_save() {
|
||||
if ( function_exists( 'wp_use_widgets_block_editor' ) && wp_use_widgets_block_editor() ) {
|
||||
update_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Enqueue_CSS::get_instance();
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles legacy attributes that have changed.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a method to define old attributes and serve old settings based on them.
|
||||
*/
|
||||
class GenerateBlocks_Legacy_Attributes {
|
||||
/**
|
||||
* Get our old defaults that have changed.
|
||||
*
|
||||
* @param string $version The version to get defaults from.
|
||||
*/
|
||||
public static function get_defaults( $version ) {
|
||||
if ( '1.4.0' === $version ) {
|
||||
return apply_filters(
|
||||
'generateblocks_defaults',
|
||||
array(
|
||||
'gridContainer' => array(
|
||||
'horizontalGap' => 30,
|
||||
),
|
||||
'container' => array(
|
||||
'paddingTop' => '40',
|
||||
'paddingRight' => '40',
|
||||
'paddingBottom' => '40',
|
||||
'paddingLeft' => '40',
|
||||
'width' => 50,
|
||||
'widthMobile' => 100,
|
||||
'gradientDirection' => 90,
|
||||
'gradientColorOne' => '#ffffff',
|
||||
'gradientColorOneOpacity' => 0.1,
|
||||
'gradientColorTwo' => '#000000',
|
||||
'gradientColorTwoOpacity' => 0.3,
|
||||
),
|
||||
'button' => array(
|
||||
'gradientDirection' => 90,
|
||||
'gradientColorOne' => '#ffffff',
|
||||
'gradientColorOneOpacity' => 0.1,
|
||||
'gradientColorTwo' => '#000000',
|
||||
'gradientColorTwoOpacity' => 0.3,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update our settings based on old defaults.
|
||||
*
|
||||
* @param string $version The version to target.
|
||||
* @param string $block The name of the block we're targeting.
|
||||
* @param array $settings The current settings.
|
||||
* @param array $atts The block attributes.
|
||||
*/
|
||||
public static function get_settings( $version, $block, $settings, $atts ) {
|
||||
$legacy_defaults = self::get_defaults( $version );
|
||||
|
||||
if ( empty( $legacy_defaults ) ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
if ( '1.4.0' === $version ) {
|
||||
if ( 'grid' === $block ) {
|
||||
$legacy_settings = wp_parse_args(
|
||||
$atts,
|
||||
$legacy_defaults['gridContainer']
|
||||
);
|
||||
|
||||
$settings['horizontalGap'] = $legacy_settings['horizontalGap'];
|
||||
}
|
||||
|
||||
if ( 'container' === $block ) {
|
||||
$legacy_settings = wp_parse_args(
|
||||
$atts,
|
||||
$legacy_defaults['container']
|
||||
);
|
||||
|
||||
$settings['paddingTop'] = $legacy_settings['paddingTop'];
|
||||
$settings['paddingRight'] = $legacy_settings['paddingRight'];
|
||||
$settings['paddingBottom'] = $legacy_settings['paddingBottom'];
|
||||
$settings['paddingLeft'] = $legacy_settings['paddingLeft'];
|
||||
$settings['width'] = $legacy_settings['width'];
|
||||
$settings['widthMobile'] = $legacy_settings['widthMobile'];
|
||||
|
||||
$settings['gradientDirection'] = $legacy_settings['gradientDirection'];
|
||||
$settings['gradientColorOne'] = $legacy_settings['gradientColorOne'];
|
||||
$settings['gradientColorOneOpacity'] = $legacy_settings['gradientColorOneOpacity'];
|
||||
$settings['gradientColorTwo'] = $legacy_settings['gradientColorTwo'];
|
||||
$settings['gradientColorTwoOpacity'] = $legacy_settings['gradientColorTwoOpacity'];
|
||||
}
|
||||
|
||||
if ( 'button' === $block ) {
|
||||
$button_legacy_settings = wp_parse_args(
|
||||
$atts,
|
||||
$legacy_defaults['button']
|
||||
);
|
||||
|
||||
$settings['gradientColorOne'] = $button_legacy_settings['gradientColorOne'];
|
||||
$settings['gradientColorOneOpacity'] = $button_legacy_settings['gradientColorOneOpacity'];
|
||||
$settings['gradientColorTwo'] = $button_legacy_settings['gradientColorTwo'];
|
||||
$settings['gradientColorTwoOpacity'] = $button_legacy_settings['gradientColorTwoOpacity'];
|
||||
$settings['gradientDirection'] = $button_legacy_settings['gradientDirection'];
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
* Maps our old attributes to their new attribute names.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Map our deprecated attributes.
|
||||
*/
|
||||
class GenerateBlocks_Map_Deprecated_Attributes {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @access private
|
||||
* @var $instance Class instance.
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Our devices.
|
||||
*
|
||||
* @access private
|
||||
* @var $devices List of devices.
|
||||
*/
|
||||
private static $devices = [ '', 'Tablet', 'Mobile' ];
|
||||
|
||||
/**
|
||||
* Initiator
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of our mapped attributes.
|
||||
*
|
||||
* @param array $settings Existing settings.
|
||||
* @return array Mapped settings.
|
||||
*/
|
||||
public static function map_attributes( $settings ) {
|
||||
$settings = self::map_spacing( $settings );
|
||||
$settings = self::map_borders( $settings );
|
||||
$settings = self::map_typography( $settings );
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map our old spacing attributes.
|
||||
*
|
||||
* @param array $settings Existing settings.
|
||||
* @return array Mapped spacing settings.
|
||||
*/
|
||||
public static function map_spacing( $settings ) {
|
||||
if ( ! empty( $settings['spacing'] ) ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$padding_attributes = [
|
||||
'paddingTop',
|
||||
'paddingRight',
|
||||
'paddingBottom',
|
||||
'paddingLeft',
|
||||
];
|
||||
|
||||
$margin_attributes = [
|
||||
'marginTop',
|
||||
'marginRight',
|
||||
'marginBottom',
|
||||
'marginLeft',
|
||||
];
|
||||
|
||||
foreach ( self::$devices as $device ) {
|
||||
foreach ( $padding_attributes as $attribute ) {
|
||||
$setting_name = $attribute . $device;
|
||||
|
||||
if ( $settings[ $setting_name ] || is_numeric( $settings[ $setting_name ] ) ) {
|
||||
$unit = is_numeric( $settings[ $setting_name ] )
|
||||
? $settings['paddingUnit']
|
||||
: '';
|
||||
|
||||
$settings['spacing'][ $setting_name ] = $settings[ $setting_name ] . $unit;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $margin_attributes as $attribute ) {
|
||||
$setting_name = $attribute . $device;
|
||||
|
||||
if ( $settings[ $setting_name ] || is_numeric( $settings[ $setting_name ] ) ) {
|
||||
$unit = is_numeric( $settings[ $setting_name ] )
|
||||
? $settings['marginUnit']
|
||||
: '';
|
||||
|
||||
$settings['spacing'][ $setting_name ] = $settings[ $setting_name ] . $unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map our old border attributes.
|
||||
*
|
||||
* @param array $settings Existing settings.
|
||||
* @return array Mapped border settings.
|
||||
*/
|
||||
public static function map_borders( $settings ) {
|
||||
if ( ! empty( $settings['borders'] ) ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$border_radius_attributes = [
|
||||
'borderRadiusTopLeft' => 'borderTopLeftRadius',
|
||||
'borderRadiusTopRight' => 'borderTopRightRadius',
|
||||
'borderRadiusBottomRight' => 'borderBottomRightRadius',
|
||||
'borderRadiusBottomLeft' => 'borderBottomLeftRadius',
|
||||
];
|
||||
|
||||
foreach ( self::$devices as $device ) {
|
||||
foreach ( $border_radius_attributes as $old_attribute_name => $new_attribute_name ) {
|
||||
$setting_name = $old_attribute_name . $device;
|
||||
|
||||
if ( $settings[ $setting_name ] || is_numeric( $settings[ $setting_name ] ) ) {
|
||||
$unit = is_numeric( $settings[ $setting_name ] )
|
||||
? $settings['borderRadiusUnit']
|
||||
: '';
|
||||
|
||||
$settings['borders'][ $new_attribute_name . $device ] = $settings[ $setting_name ] . $unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$border_width_attributes = [
|
||||
'borderSizeTop' => 'borderTopWidth',
|
||||
'borderSizeRight' => 'borderRightWidth',
|
||||
'borderSizeBottom' => 'borderBottomWidth',
|
||||
'borderSizeLeft' => 'borderLeftWidth',
|
||||
];
|
||||
|
||||
foreach ( self::$devices as $device ) {
|
||||
foreach ( $border_width_attributes as $old_attribute_name => $new_attribute_name ) {
|
||||
$setting_name = $old_attribute_name . $device;
|
||||
|
||||
if ( $settings[ $setting_name ] || is_numeric( $settings[ $setting_name ] ) ) {
|
||||
$unit = is_numeric( $settings[ $setting_name ] )
|
||||
? 'px'
|
||||
: '';
|
||||
|
||||
$settings['borders'][ $new_attribute_name . $device ] = $settings[ $setting_name ] . $unit;
|
||||
|
||||
$border_style_name = str_replace( 'Width', 'Style', $new_attribute_name );
|
||||
$settings['borders'][ $border_style_name . $device ] = 'solid';
|
||||
|
||||
if ( ! empty( $settings['borderColor'] ) ) {
|
||||
$border_color_name = str_replace( 'Width', 'Color', $new_attribute_name );
|
||||
$settings['borders'][ $border_color_name ] = isset( $settings['borderColorOpacity'] )
|
||||
? generateblocks_hex2rgba( $settings['borderColor'], $settings['borderColorOpacity'] )
|
||||
: $settings['borderColor'];
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['borderColorHover'] ) ) {
|
||||
$border_color_hover_name = str_replace( 'Width', 'ColorHover', $new_attribute_name );
|
||||
$settings['borders'][ $border_color_hover_name ] = isset( $settings['borderColorHoverOpacity'] )
|
||||
? generateblocks_hex2rgba( $settings['borderColorHover'], $settings['borderColorHoverOpacity'] )
|
||||
: $settings['borderColorHover'];
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['borderColorCurrent'] ) ) {
|
||||
$border_color_current_name = str_replace( 'Width', 'ColorCurrent', $new_attribute_name );
|
||||
$settings['borders'][ $border_color_current_name ] = $settings['borderColorCurrent'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map our old typography attributes.
|
||||
*
|
||||
* @param array $settings Existing settings.
|
||||
* @return array Mapped spacing settings.
|
||||
*/
|
||||
public static function map_typography( $settings ) {
|
||||
if ( ! empty( $settings['typography'] ) ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$old_attributes = [
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'lineHeight',
|
||||
'letterSpacing',
|
||||
'fontWeight',
|
||||
'textTransform',
|
||||
'alignment',
|
||||
];
|
||||
|
||||
foreach ( self::$devices as $device ) {
|
||||
foreach ( $old_attributes as $attribute ) {
|
||||
$setting_name = $attribute . $device;
|
||||
|
||||
if ( isset( $settings[ $setting_name ] ) && ( $settings[ $setting_name ] || is_numeric( $settings[ $setting_name ] ) ) ) {
|
||||
$unit = '';
|
||||
|
||||
switch ( $attribute ) {
|
||||
case 'fontSize':
|
||||
$unit = $settings['fontSizeUnit'];
|
||||
break;
|
||||
|
||||
case 'lineHeight':
|
||||
$unit = $settings['lineHeightUnit'];
|
||||
break;
|
||||
|
||||
case 'letterSpacing':
|
||||
$unit = 'em';
|
||||
break;
|
||||
}
|
||||
|
||||
// textAlign used to be called "alignment".
|
||||
if ( 'alignment' === $attribute ) {
|
||||
$settings['typography'][ 'textAlign' . $device ] = $settings[ $setting_name ];
|
||||
continue;
|
||||
}
|
||||
|
||||
$settings['typography'][ $setting_name ] = $settings[ $setting_name ] . $unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,627 @@
|
||||
<?php
|
||||
/**
|
||||
* The retrieve metadata and options.
|
||||
*
|
||||
* @package GenerateBlocks\Meta_Handler
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling dynamic tags.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class GenerateBlocks_Meta_Handler extends GenerateBlocks_Singleton {
|
||||
|
||||
/**
|
||||
* Back-compat proxy so external code can keep referencing this constant.
|
||||
*/
|
||||
const DISALLOWED_KEYS = GenerateBlocks_Dynamic_Tag_Security::DISALLOWED_KEYS;
|
||||
|
||||
/**
|
||||
* Initialize all hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register class REST routes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/meta/get-post-meta',
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => [ $this, 'get_post_meta_rest' ],
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/meta/get-user-meta',
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => [ $this, 'get_user_meta_rest' ],
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/meta/get-term-meta',
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => [ $this, 'get_term_meta_rest' ],
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/meta/get-option',
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => [ $this, 'get_option_rest' ],
|
||||
'permission_callback' => function( $request ) {
|
||||
// Only allow users who can edit posts to access options.
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Admins can access all options.
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$allowed_keys = apply_filters(
|
||||
'generateblocks_allowed_option_keys_rest_api',
|
||||
[
|
||||
'siteurl',
|
||||
'blogname',
|
||||
'blogdescription',
|
||||
'home',
|
||||
'time_format',
|
||||
'user_count',
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! is_array( $allowed_keys ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = $request->get_param( 'key' ) ?? '';
|
||||
|
||||
if ( ! is_string( $key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow access to allowed keys.
|
||||
if ( in_array( $key, $allowed_keys, true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback: check parent key for dot notation.
|
||||
if ( strpos( $key, '.' ) !== false ) {
|
||||
$parent_key = trim( explode( '.', $key )[0] );
|
||||
|
||||
if ( '' !== $parent_key && in_array( $parent_key, $allowed_keys, true ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a value if array-like and if so, get the provided property from it.
|
||||
*
|
||||
* @param mixed $value The value to check the property against.
|
||||
* @param mixed $property The property to retrieve from the value if it exists.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @return mixed The $property value if it exists, otherwise the $value.
|
||||
*/
|
||||
public static function maybe_get_property( $value, $property, $single_only = true ) {
|
||||
if ( ! $property
|
||||
&& ! $single_only
|
||||
&& ( is_array( $value ) || is_object( $value ) )
|
||||
) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
return $value[ $property ] ?? $value;
|
||||
} elseif ( is_object( $value ) ) {
|
||||
return $value->$property ?? $value;
|
||||
}
|
||||
|
||||
// Return the value if it's not an array or object.
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is an array or object.
|
||||
*
|
||||
* @param mixed $value The value to check.
|
||||
* @return bool If the value is an array or object.
|
||||
*/
|
||||
public static function is_array_or_object( $value ) {
|
||||
return is_array( $value ) || is_object( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive or single value retrieval.
|
||||
*
|
||||
* @param string $key The key from the parent value for retrieval.
|
||||
* @param string|int $parent_value The parent value to check the key against.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @param string $fallback The fallback value if the return value is empty.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_value( $key, $parent_value, $single_only = true, $fallback = '' ) {
|
||||
// Stop here if the key is empty, and not "0".
|
||||
if ( empty( $key ) && ! is_numeric( $key ) ) {
|
||||
if ( $single_only ) {
|
||||
$parent_value = self::is_array_or_object( $parent_value ) ? $fallback : (string) $parent_value;
|
||||
|
||||
return '' !== $parent_value ? $parent_value : $fallback;
|
||||
}
|
||||
|
||||
return self::is_array_or_object( $parent_value ) ? $parent_value : $fallback;
|
||||
}
|
||||
|
||||
$parts = explode( '.', $key );
|
||||
$sub_value = self::maybe_get_property( $parent_value, $parts[0], $single_only );
|
||||
|
||||
if ( self::is_array_or_object( $sub_value ) ) {
|
||||
return self::get_value(
|
||||
implode( '.', array_slice( $parts, 1 ) ),
|
||||
$sub_value,
|
||||
$single_only,
|
||||
$fallback
|
||||
);
|
||||
}
|
||||
|
||||
// Coerce simple values to strings.
|
||||
$value = (string) $sub_value;
|
||||
|
||||
return '' !== $value ? $value : $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a meta value.
|
||||
*
|
||||
* @param string|int $id The id of the entity to fetch meta from.
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @param string $callable Function name to call. Should be a native WordPress function (ex: get_post_meta).
|
||||
* @param string $fallback The fallback value to show if the returned value is empty.
|
||||
* @return string|array|object The returned value or an empty string if not found.
|
||||
*/
|
||||
public static function get_meta( $id, $key, $single_only = true, $callable = null, $fallback = '' ) {
|
||||
if ( ! is_string( $callable ) || ! function_exists( $callable ) || ! is_string( $key ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$key_parts = array_map( 'trim', explode( '.', $key ) );
|
||||
$parent_name = $key_parts[0];
|
||||
|
||||
if ( empty( $key ) || in_array( $parent_name, self::DISALLOWED_KEYS, true ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow a filter to set this meta value using some
|
||||
* custom setter function (such as get_field in ACF). If this value returns
|
||||
* something we can skip calling get_post_meta for it and return the value instead.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param string|null $pre_value The pre-filtered value, or null if unset.
|
||||
* @param int $id The entity ID used to fetch the meta value.
|
||||
* @param string $key The meta key to fetch.
|
||||
* @param string $callable Function name to call. Should be a native WordPress function (ex: get_post_meta).
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
*/
|
||||
$pre_value = apply_filters(
|
||||
'generateblocks_get_meta_pre_value',
|
||||
null,
|
||||
$id,
|
||||
$key,
|
||||
$callable,
|
||||
$single_only
|
||||
);
|
||||
|
||||
if ( is_numeric( $id ) ) {
|
||||
$meta = $pre_value ? $pre_value : call_user_func( $callable, $id, $parent_name, true );
|
||||
} else {
|
||||
$meta = $pre_value ? $pre_value : call_user_func( $callable, $parent_name );
|
||||
}
|
||||
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && ! current_user_can( 'manage_options' ) ) {
|
||||
if ( 'get_user_meta' === $callable && is_numeric( $id ) ) {
|
||||
if ( self::should_restrict_user_meta_access( $id, $key ) ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'get_post_meta' === $callable ) {
|
||||
if ( is_numeric( $id ) && ! current_user_can( 'read_post', (int) $id ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( is_protected_meta( $key, 'post' ) ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'get_term_meta' === $callable ) {
|
||||
if ( is_protected_meta( $key, 'term' ) ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some user meta is stored as user data.
|
||||
// If we're looking for user meta and can't find it, let's check for user data as well.
|
||||
if ( ! $meta && 'get_user_meta' === $callable ) {
|
||||
$meta = get_the_author_meta( $parent_name, $id );
|
||||
}
|
||||
|
||||
$meta = apply_filters(
|
||||
'generateblocks_get_meta_object',
|
||||
$meta,
|
||||
$id,
|
||||
$key,
|
||||
$callable
|
||||
);
|
||||
|
||||
// Only send the sub key(s) through. If they're empty this will return the value of $meta.
|
||||
array_shift( $key_parts );
|
||||
$sub_key = implode( '.', $key_parts );
|
||||
$value = self::get_value( $sub_key, $meta, $single_only, $fallback );
|
||||
|
||||
/**
|
||||
* Filter the result of get_value for entity meta.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @param string|int $id The ID of the entity to fetch meta from.
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @param string $callable Function name to call. Should be a native WordPress function (ex: get_post_meta).
|
||||
*/
|
||||
return apply_filters( 'generateblocks_get_meta_value', $value, $id, $key, $single_only, $callable );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post meta.
|
||||
*
|
||||
* @param string|int $id The id of the post to fetch meta from.
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @return string|array|object The returned value or an empty string if not found.
|
||||
*/
|
||||
public static function get_post_meta( $id, $key, $single_only = true ) {
|
||||
return self::get_meta( $id, $key, $single_only, 'get_post_meta' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest handler for get_post_meta
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error The response object.
|
||||
*/
|
||||
public function get_post_meta_rest( $request ) {
|
||||
$id = (int) $request->get_param( 'id' );
|
||||
$single_only = true;
|
||||
|
||||
// Verify user can read this post.
|
||||
if ( ! current_user_can( 'read_post', $id ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to read this post.', 'generateblocks' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( $post ) {
|
||||
$requires_password = ! empty( $post->post_password ) && post_password_required( $post );
|
||||
$can_bypass_pw = current_user_can( 'edit_post', $post->ID ) || get_current_user_id() === (int) $post->post_author;
|
||||
|
||||
if ( $requires_password && ! $can_bypass_pw ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to read meta for this password-protected post.', 'generateblocks' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$key = $request->get_param( 'key' );
|
||||
|
||||
// Block protected meta keys (WordPress convention for private/internal meta).
|
||||
if ( is_string( $key ) && is_protected_meta( $key, 'post' ) ) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to access protected meta fields.', 'generateblocks' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'false' === $request->get_param( 'singleOnly' ) ) {
|
||||
$single_only = false;
|
||||
}
|
||||
|
||||
return rest_ensure_response( self::get_post_meta( $id, $key, $single_only ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the user meta.
|
||||
*
|
||||
* @param string|int $id The id of the user to fetch meta from.
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @return string|array|object The returned value or an empty string if not found.
|
||||
*/
|
||||
public static function get_user_meta( $id, $key, $single_only = true ) {
|
||||
return self::get_meta( $id, $key, $single_only, 'get_user_meta' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if user meta access should be restricted.
|
||||
*
|
||||
* This checks whether security restrictions should apply based on:
|
||||
* - User capabilities (list_users bypasses all restrictions)
|
||||
* - Whether user is viewing their own data (allowed)
|
||||
* - Whether the field is in the safe list
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param int $user_id The user ID being accessed.
|
||||
* @param string $key The meta key being accessed.
|
||||
* @return bool True if access should be restricted, false if allowed.
|
||||
*/
|
||||
public static function should_restrict_user_meta_access( $user_id, $key ) {
|
||||
$current_user_id = get_current_user_id();
|
||||
$is_privileged = current_user_can( 'list_users' );
|
||||
$is_viewing_own = (int) $user_id === $current_user_id && $current_user_id > 0;
|
||||
|
||||
// Privileged users or viewing own data - no restrictions.
|
||||
if ( $is_privileged || $is_viewing_own ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if field is in safe list.
|
||||
$parent_key = '';
|
||||
if ( is_string( $key ) ) {
|
||||
$parent_key = trim( explode( '.', $key )[0] );
|
||||
}
|
||||
|
||||
$safe_keys = [];
|
||||
|
||||
if (
|
||||
class_exists( 'GenerateBlocks_Dynamic_Tag_Security' ) &&
|
||||
method_exists( 'GenerateBlocks_Dynamic_Tag_Security', 'get_safe_user_meta_keys' )
|
||||
) {
|
||||
$safe_keys = GenerateBlocks_Dynamic_Tag_Security::get_safe_user_meta_keys();
|
||||
}
|
||||
|
||||
// If key is in safe list, allow access.
|
||||
if ( $parent_key && in_array( $parent_key, $safe_keys, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter whether to restrict user_meta access.
|
||||
*
|
||||
* WARNING: Returning false disables security restrictions and may
|
||||
* expose sensitive user data to all visitors. Only disable if you
|
||||
* have implemented your own access controls.
|
||||
*
|
||||
* @since 2.1.4
|
||||
*
|
||||
* @param bool $should_restrict Whether to apply restrictions.
|
||||
* @param int $user_id The user ID being accessed.
|
||||
* @param int $current_user_id Current user ID (0 if logged out).
|
||||
*/
|
||||
return apply_filters(
|
||||
'generateblocks_restrict_user_meta_access',
|
||||
true,
|
||||
$user_id,
|
||||
$current_user_id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest handler for get_user_meta
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error The response object.
|
||||
*/
|
||||
public function get_user_meta_rest( $request ) {
|
||||
$requested_id = (int) $request->get_param( 'id' );
|
||||
$current_id = get_current_user_id();
|
||||
|
||||
$id = $requested_id ? $requested_id : $current_id;
|
||||
|
||||
if ( ! $id ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'invalid_user_id',
|
||||
__( 'A valid user ID is required.', 'generateblocks' ),
|
||||
array( 'status' => 400 )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Require list_users capability to access other users' meta.
|
||||
if ( $id !== $current_id && ! current_user_can( 'list_users' ) ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to access this user\'s meta.', 'generateblocks' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$key = $request->get_param( 'key' );
|
||||
$single_only = true;
|
||||
|
||||
// Block protected meta keys (WordPress convention for private/internal meta).
|
||||
if ( is_string( $key ) && is_protected_meta( $key, 'user' ) ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to access protected meta fields.', 'generateblocks' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if access should be restricted (preview/draft context and safe list).
|
||||
if ( self::should_restrict_user_meta_access( $id, $key ) ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to view this user meta field.', 'generateblocks' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'false' === $request->get_param( 'singleOnly' ) ) {
|
||||
$single_only = false;
|
||||
}
|
||||
|
||||
return rest_ensure_response( self::get_user_meta( $id, $key, $single_only ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the term meta.
|
||||
*
|
||||
* @param string|int $id The id of the term to fetch meta from.
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @return string|array|object The returned value or an empty string if not found.
|
||||
*/
|
||||
public static function get_term_meta( $id, $key, $single_only = true ) {
|
||||
return self::get_meta( $id, $key, $single_only, 'get_term_meta' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest handler for get_term_meta
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error The response object.
|
||||
*/
|
||||
public function get_term_meta_rest( $request ) {
|
||||
$id = (int) $request->get_param( 'id' );
|
||||
$key = $request->get_param( 'key' );
|
||||
$single_only = true;
|
||||
|
||||
if ( ! $id ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'invalid_term_id',
|
||||
__( 'A valid term ID is required.', 'generateblocks' ),
|
||||
[ 'status' => 400 ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$term = get_term( $id );
|
||||
|
||||
if ( ! $term || is_wp_error( $term ) ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'term_not_found',
|
||||
__( 'Term not found.', 'generateblocks' ),
|
||||
[ 'status' => 404 ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$can_manage_term = current_user_can( 'edit_term', $term->term_id );
|
||||
$can_edit_posts = current_user_can( 'edit_posts' );
|
||||
|
||||
if ( ! $can_manage_term && ! $can_edit_posts ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to read this term.', 'generateblocks' ),
|
||||
[ 'status' => 403 ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_string( $key ) && is_protected_meta( $key, 'term' ) ) {
|
||||
return rest_ensure_response(
|
||||
new WP_Error(
|
||||
'rest_forbidden',
|
||||
__( 'Sorry, you are not allowed to access protected term meta fields.', 'generateblocks' ),
|
||||
[ 'status' => 403 ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'false' === $request->get_param( 'singleOnly' ) ) {
|
||||
$single_only = false;
|
||||
}
|
||||
|
||||
return rest_ensure_response( self::get_term_meta( $id, $key, $single_only ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an option's value.
|
||||
*
|
||||
* @param string $key The meta key to fetch. May include one or more sub keys separated by a period.
|
||||
* @param bool $single_only If true, only return value if it's a string-like value.
|
||||
* @return string|array|object The returned value or an empty string if not found.
|
||||
*/
|
||||
public static function get_option( $key, $single_only = true ) {
|
||||
return self::get_meta( 'option', $key, $single_only, 'get_option' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest handler for get_option
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error The response object.
|
||||
*/
|
||||
public function get_option_rest( $request ) {
|
||||
$key = $request->get_param( 'key' );
|
||||
$single_only = true;
|
||||
|
||||
if ( 'false' === $request->get_param( 'singleOnly' ) ) {
|
||||
$single_only = false;
|
||||
}
|
||||
|
||||
return rest_ensure_response( self::get_option( $key, $single_only ) );
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Meta_Handler::get_instance()->init();
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles option changes on plugin updates.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Process option updates if necessary.
|
||||
*/
|
||||
class GenerateBlocks_Plugin_Update extends GenerateBlocks_Singleton {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'admin_init', [ $this, 'updates' ], 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement plugin update logic.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function updates() {
|
||||
if ( is_customize_preview() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$saved_version = get_option( 'generateblocks_version', false );
|
||||
|
||||
if ( false === $saved_version ) {
|
||||
update_option( 'generateblocks_version', sanitize_text_field( GENERATEBLOCKS_VERSION ) );
|
||||
|
||||
// Not an existing install, so no need to proceed further.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( version_compare( $saved_version, GENERATEBLOCKS_VERSION, '=' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( version_compare( $saved_version, '2.0.0-alpha.1', '<' ) ) {
|
||||
self::v_2_0_0();
|
||||
}
|
||||
|
||||
// Force regenerate our static CSS files.
|
||||
update_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
|
||||
// Last thing to do is update our version.
|
||||
update_option( 'generateblocks_version', sanitize_text_field( GENERATEBLOCKS_VERSION ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update options for version 2.0.0.
|
||||
*/
|
||||
public static function v_2_0_0() {
|
||||
$settings = get_option( 'generateblocks', [] );
|
||||
|
||||
// `disable_google_fonts` use to be false by default.
|
||||
// If the user has come from 1.x and they haven't set this option,
|
||||
// set it back to false.
|
||||
if ( empty( $settings['disable_google_fonts'] ) ) {
|
||||
$settings['disable_google_fonts'] = false;
|
||||
}
|
||||
|
||||
update_option( 'generateblocks', $settings );
|
||||
|
||||
// Turn on v1 blocks by default for users coming from 1.x.
|
||||
update_option( 'gb_use_v1_blocks', true, false );
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Plugin_Update::get_instance()->init();
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/**
|
||||
* This file handles the Query Loop functions.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Query Loop functions.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
class GenerateBlocks_Query_Loop {
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* @access private
|
||||
* @var object Instance
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Initiator.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @return object initialized object of class.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'generateblocks_attr_grid-wrapper', array( $this, 'add_grid_wrapper_attributes' ), 10, 2 );
|
||||
add_filter( 'generateblocks_attr_container', array( $this, 'add_container_attributes' ), 10, 2 );
|
||||
add_filter( 'generateblocks_attr_grid-item', array( $this, 'add_grid_item_attributes' ), 10, 2 );
|
||||
add_filter( 'generateblocks_attr_button-container', array( $this, 'add_button_wrapper_attributes' ), 10, 2 );
|
||||
add_filter( 'generateblocks_defaults', array( $this, 'add_block_defaults' ) );
|
||||
add_filter( 'generateblocks_query_loop_args', array( $this, 'set_query_loop_defaults' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that constructs a WP_Query args array from
|
||||
* a `Query` block properties.
|
||||
*
|
||||
* @param WP_Block $block Block instance.
|
||||
* @param int $page Current query's page.
|
||||
*
|
||||
* @todo: https://github.com/WordPress/wordpress-develop/blob/44e308c12e68b5c6b63845fd84369ba36985e193/src/wp-includes/blocks.php#L1126
|
||||
*/
|
||||
public static function get_query_args( $block, $page ) {
|
||||
$query_attributes = ( is_array( $block->context ) && isset( $block->context['generateblocks/query'] ) )
|
||||
? $block->context['generateblocks/query']
|
||||
: array();
|
||||
|
||||
// Set up our pagination.
|
||||
$query_attributes['paged'] = $page;
|
||||
|
||||
$query_args = self::map_post_type_attributes( $query_attributes );
|
||||
|
||||
if ( isset( $query_args['tax_query'] ) ) {
|
||||
$query_args['tax_query'] = self::normalize_tax_query_attributes( $query_args['tax_query'] );
|
||||
}
|
||||
|
||||
if ( isset( $query_args['date_query_after'] ) || isset( $query_args['date_query_before'] ) ) {
|
||||
$query_args['date_query'] = self::normalize_date_query_attributes(
|
||||
isset( $query_args['date_query_after'] ) ? $query_args['date_query_after'] : null,
|
||||
isset( $query_args['date_query_before'] ) ? $query_args['date_query_before'] : null
|
||||
);
|
||||
|
||||
unset( $query_args['date_query_after'] );
|
||||
unset( $query_args['date_query_before'] );
|
||||
}
|
||||
|
||||
if ( isset( $query_args['stickyPosts'] ) && 'ignore' === $query_args['stickyPosts'] ) {
|
||||
$query_args['ignore_sticky_posts'] = true;
|
||||
unset( $query_args['stickyPosts'] );
|
||||
}
|
||||
|
||||
if ( isset( $query_args['stickyPosts'] ) && 'exclude' === $query_args['stickyPosts'] ) {
|
||||
$sticky_posts = get_option( 'sticky_posts' );
|
||||
$post_not_in = isset( $query_args['post__not_in'] ) && is_array( $query_args['post__not_in'] ) ? $query_args['post__not_in'] : array();
|
||||
$query_args['post__not_in'] = array_merge( $sticky_posts, $post_not_in );
|
||||
unset( $query_args['stickyPosts'] );
|
||||
}
|
||||
|
||||
if ( isset( $query_args['stickyPosts'] ) && 'only' === $query_args['stickyPosts'] ) {
|
||||
$sticky_posts = get_option( 'sticky_posts' );
|
||||
$query_args['ignore_sticky_posts'] = true;
|
||||
$query_args['post__in'] = $sticky_posts;
|
||||
unset( $query_args['stickyPosts'] );
|
||||
}
|
||||
|
||||
if ( isset( $query_args['tax_query_exclude'] ) ) {
|
||||
$not_in_tax_query = self::normalize_tax_query_attributes( $query_args['tax_query_exclude'], 'NOT IN' );
|
||||
$query_args['tax_query'] = isset( $query_args['tax_query'] )
|
||||
? array_merge( $query_args['tax_query'], $not_in_tax_query )
|
||||
: $not_in_tax_query;
|
||||
|
||||
unset( $query_args['tax_query_exclude'] );
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $query_args['posts_per_page'] ) &&
|
||||
is_numeric( $query_args['posts_per_page'] )
|
||||
) {
|
||||
$per_page = intval( $query_args['posts_per_page'] );
|
||||
$offset = 0;
|
||||
|
||||
if (
|
||||
isset( $query_args['offset'] ) &&
|
||||
is_numeric( $query_args['offset'] )
|
||||
) {
|
||||
$offset = absint( $query_args['offset'] );
|
||||
}
|
||||
|
||||
$query_args['offset'] = ( $per_page * ( $page - 1 ) ) + $offset;
|
||||
$query_args['posts_per_page'] = $per_page;
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $query_args['post_status'] ) &&
|
||||
'publish' !== $query_args['post_status'] &&
|
||||
! current_user_can( 'read_private_posts' )
|
||||
) {
|
||||
// If the user can't read private posts, we'll force the post status to be public.
|
||||
$query_args['post_status'] = 'publish';
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map query parameters to their correct query names.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*/
|
||||
public static function map_post_type_attributes( $attributes ) {
|
||||
$attributes_map = array(
|
||||
'page' => 'paged',
|
||||
'per_page' => 'posts_per_page',
|
||||
'search' => 's',
|
||||
'after' => 'date_query_after',
|
||||
'before' => 'date_query_before',
|
||||
'author' => 'author__in',
|
||||
'exclude' => 'post__not_in',
|
||||
'include' => 'post__in',
|
||||
'order' => 'order',
|
||||
'orderby' => 'orderby',
|
||||
'status' => 'post_status',
|
||||
'parent' => 'post_parent__in',
|
||||
'parent_exclude' => 'post_parent__not_in',
|
||||
'author_exclude' => 'author__not_in',
|
||||
);
|
||||
|
||||
return generateblocks_map_array_keys( $attributes, $attributes_map );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the tax query attributes to be used in the WP_Query
|
||||
*
|
||||
* @param array $raw_tax_query Tax query.
|
||||
* @param string $operator Tax operator.
|
||||
*
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function normalize_tax_query_attributes( $raw_tax_query, $operator = 'IN' ) {
|
||||
return array_map(
|
||||
function( $tax ) use ( $operator ) {
|
||||
return array(
|
||||
'taxonomy' => $tax['taxonomy'],
|
||||
'field' => 'term_id',
|
||||
'terms' => $tax['terms'],
|
||||
'operator' => $operator,
|
||||
'include_children' => isset( $tax['includeChildren'] ) ? $tax['includeChildren'] : true,
|
||||
);
|
||||
},
|
||||
$raw_tax_query
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the date query attributes to be used in the WP_Query
|
||||
*
|
||||
* @param string|null $after The after date.
|
||||
* @param string|null $before The before date.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function normalize_date_query_attributes( $after = null, $before = null ) {
|
||||
$result = array( 'inclusive' => true );
|
||||
|
||||
if ( generateblocks_is_valid_date( $after ) ) {
|
||||
$result['after'] = $after;
|
||||
}
|
||||
|
||||
if ( generateblocks_is_valid_date( $before ) ) {
|
||||
$result['before'] = $before;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add defaults for our Query settings.
|
||||
*
|
||||
* @param array $defaults Block defaults.
|
||||
*/
|
||||
public function add_block_defaults( $defaults ) {
|
||||
$defaults['container']['isQueryLoopItem'] = false;
|
||||
$defaults['container']['isPagination'] = false;
|
||||
$defaults['gridContainer']['isQueryLoop'] = false;
|
||||
$defaults['buttonContainer']['isPagination'] = false;
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTML attributes to the Query Loop wrapper.
|
||||
*
|
||||
* @param array $attributes Existing HTML attributes.
|
||||
* @param array $settings Block settings.
|
||||
*/
|
||||
public function add_grid_wrapper_attributes( $attributes, $settings ) {
|
||||
if ( $settings['isQueryLoop'] ) {
|
||||
$attributes['class'] .= ' gb-query-loop-wrapper';
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTML attributes to the Query Loop Containers.
|
||||
*
|
||||
* @param array $attributes Existing HTML attributes.
|
||||
* @param array $settings Block settings.
|
||||
*/
|
||||
public function add_container_attributes( $attributes, $settings ) {
|
||||
if ( $settings['isPagination'] ) {
|
||||
$attributes['class'] .= ' gb-query-loop-pagination';
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTML attributes to the Query Loop Item wrapper.
|
||||
*
|
||||
* @param array $attributes Existing HTML attributes.
|
||||
* @param array $settings Block settings.
|
||||
*/
|
||||
public function add_grid_item_attributes( $attributes, $settings ) {
|
||||
if ( $settings['isQueryLoopItem'] ) {
|
||||
$attributes['class'] .= ' ' . implode( ' ', get_post_class( 'gb-query-loop-item' ) );
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTML attributes to the Button wrapper.
|
||||
*
|
||||
* @param array $attributes Existing HTML attributes.
|
||||
* @param array $settings Block settings.
|
||||
*/
|
||||
public function add_button_wrapper_attributes( $attributes, $settings ) {
|
||||
if ( $settings['isPagination'] ) {
|
||||
$attributes['class'] .= ' gb-query-loop-pagination';
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default query loop arguments.
|
||||
*
|
||||
* @param array $query_args The query loop arguments.
|
||||
* @return array The query loop arguments with defaults.
|
||||
*/
|
||||
public function set_query_loop_defaults( $query_args ) {
|
||||
if ( ! isset( $query_args['posts_per_page'] ) || '' === $query_args['posts_per_page'] ) {
|
||||
$query_args['posts_per_page'] = 10;
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Query_Loop::get_instance();
|
||||
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
/**
|
||||
* The Libraries class file.
|
||||
*
|
||||
* @package GenerateBlocks\Pattern_Library
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling with libraries.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
class GenerateBlocks_Query_Utils extends GenerateBlocks_Singleton {
|
||||
|
||||
/**
|
||||
* Initialize the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST routes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/get-wp-query',
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'callback' => [ $this, 'get_wp_query' ],
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
'generateblocks/v1',
|
||||
'/get-user-query',
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'callback' => [ $this, 'get_user_query' ],
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets posts and returns an array of WP_Post objects.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response The response.
|
||||
*/
|
||||
public function get_user_query( WP_REST_Request $request ) {
|
||||
$args = $request->get_param( 'args' ) ?? [];
|
||||
|
||||
if ( ! is_array( $args ) ) {
|
||||
$args = [];
|
||||
}
|
||||
|
||||
$args['number'] = min( max( 1, absint( $args['number'] ?? 150 ) ), 150 );
|
||||
$args['paged'] = max( 1, (int) ( $args['paged'] ?? 1 ) );
|
||||
$args['fields'] = 'all';
|
||||
|
||||
$can_list_users = current_user_can( 'list_users' );
|
||||
$can_manage_options = current_user_can( 'manage_options' );
|
||||
|
||||
// Sanitize dangerous query args for users without list_users capability.
|
||||
if ( ! $can_list_users ) {
|
||||
unset( $args['meta_query'] );
|
||||
unset( $args['meta_key'] );
|
||||
unset( $args['meta_value'] );
|
||||
unset( $args['meta_compare'] );
|
||||
unset( $args['role'] );
|
||||
unset( $args['role__in'] );
|
||||
unset( $args['role__not_in'] );
|
||||
unset( $args['capability'] );
|
||||
unset( $args['capability__in'] );
|
||||
unset( $args['capability__not_in'] );
|
||||
|
||||
$args['has_published_posts'] = true;
|
||||
}
|
||||
|
||||
$number = $args['number'];
|
||||
$query = new WP_User_Query( $args );
|
||||
$max_pages = (int) ceil( $query->get_total() / $number );
|
||||
|
||||
// Filter sensitive data from user objects while maintaining structure.
|
||||
$users = array_map(
|
||||
function( $user ) use ( $can_manage_options, $can_list_users ) {
|
||||
// Always remove critical security fields.
|
||||
unset( $user->data->user_pass );
|
||||
unset( $user->data->user_activation_key );
|
||||
|
||||
if ( ! $can_manage_options ) {
|
||||
// Remove sensitive values for non-admin users.
|
||||
unset( $user->data->user_login );
|
||||
unset( $user->data->user_email );
|
||||
|
||||
// Remove capability data to prevent role enumeration.
|
||||
unset( $user->caps );
|
||||
unset( $user->allcaps );
|
||||
}
|
||||
|
||||
if ( ! $can_list_users ) {
|
||||
unset( $user->roles );
|
||||
unset( $user->cap_key );
|
||||
}
|
||||
|
||||
return $user;
|
||||
},
|
||||
$query->get_results()
|
||||
);
|
||||
|
||||
return rest_ensure_response(
|
||||
[
|
||||
'users' => $users,
|
||||
'total' => $query->get_total(),
|
||||
'max_pages' => $max_pages,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets posts and returns an array of WP_Post objects.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response The response.
|
||||
*/
|
||||
public function get_wp_query( $request ) {
|
||||
$args = $request->get_param( 'args' );
|
||||
$args = is_array( $args ) ? $args : [];
|
||||
$page = $args['paged'] ?? $request->get_param( 'page' ) ?? 1;
|
||||
$page = is_scalar( $page ) ? (int) $page : 1;
|
||||
$attributes = $request->get_param( 'attributes' ) ?? [];
|
||||
$current_post = $request->get_param( 'postId' ) ?? null;
|
||||
$current_author = $request->get_param( 'authorId' ) ?? null;
|
||||
$context = $request->get_param( 'context' ) ?? [];
|
||||
$query_type = $request->get_param( 'queryType' ) ?? GenerateBlocks_Block_Query::TYPE_WP_QUERY;
|
||||
$current = [
|
||||
'post_id' => $current_post,
|
||||
'author_id' => $current_author,
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter the args for get-wp-query calls before they're passed to get_wp_query_args.
|
||||
*
|
||||
* @param array $args The WP_Query args to parse.
|
||||
* @param array $props Additional filter properties.
|
||||
*
|
||||
* @return array $args The optimized WP_Query args array.
|
||||
*/
|
||||
$args = apply_filters(
|
||||
'generateblocks_rest_get_wp_query_args',
|
||||
$args,
|
||||
[
|
||||
'page' => $page,
|
||||
'attributes' => $attributes,
|
||||
'context' => $context,
|
||||
'current' => $current,
|
||||
'query_type' => $query_type,
|
||||
]
|
||||
);
|
||||
|
||||
$query = new WP_Query(
|
||||
self::get_wp_query_args(
|
||||
$args,
|
||||
$page,
|
||||
$attributes,
|
||||
null,
|
||||
$current
|
||||
)
|
||||
);
|
||||
|
||||
// Filter sensitive data in-place so consumers still receive the full WP_Query object.
|
||||
$query->posts = array_map(
|
||||
function( $post ) {
|
||||
$requires_password = ! empty( $post->post_password ) && post_password_required( $post );
|
||||
$can_read_post = current_user_can( 'read_post', $post->ID );
|
||||
$can_bypass_pw = current_user_can( 'edit_post', $post->ID ) || get_current_user_id() === (int) $post->post_author;
|
||||
|
||||
unset( $post->post_password );
|
||||
unset( $post->guid );
|
||||
unset( $post->post_content_filtered );
|
||||
|
||||
if ( ! $can_read_post || ( $requires_password && ! $can_bypass_pw ) ) {
|
||||
unset( $post->post_content );
|
||||
unset( $post->post_excerpt );
|
||||
}
|
||||
|
||||
return $post;
|
||||
},
|
||||
$query->posts
|
||||
);
|
||||
|
||||
return rest_ensure_response( $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that optimizes the query attribute from the Query block.
|
||||
*
|
||||
* @param array $args The WP_Query args to parse.
|
||||
* @param int $page Current query's page.
|
||||
* @param array $attributes The query block's attributes. Used for reference in the filters.
|
||||
* @param WP_Block|array $block The current block.
|
||||
* @param array $current Array of current entities (post, author, etc.).
|
||||
*
|
||||
* @return array $query_args The optimized WP_Query args array.
|
||||
*/
|
||||
public static function get_wp_query_args( $args = [], $page = 1, $attributes = [], $block = null, $current = [] ) {
|
||||
$current_post_id = $current['post_id'] ?? get_the_ID();
|
||||
$current_author_id = $current['author_id'] ?? get_the_author_meta( 'ID' );
|
||||
|
||||
// Set up our pagination.
|
||||
if ( ! isset( $args['paged'] ) && -1 < (int) $page ) {
|
||||
$args['paged'] = $page;
|
||||
}
|
||||
|
||||
if ( isset( $args['tax_query'] ) && is_array( $args['tax_query'] ) ) {
|
||||
if ( count( $args['tax_query'] ) > 1 ) {
|
||||
$args['tax_query']['relation'] = 'AND';
|
||||
}
|
||||
}
|
||||
|
||||
// Sticky posts handling.
|
||||
if ( isset( $args['stickyPosts'] ) ) {
|
||||
|
||||
if ( 'ignore' === $args['stickyPosts'] ) {
|
||||
$args['ignore_sticky_posts'] = true;
|
||||
}
|
||||
|
||||
if ( 'exclude' === $args['stickyPosts'] ) {
|
||||
$sticky_posts = get_option( 'sticky_posts' );
|
||||
$post_not_in = isset( $args['post__not_in'] ) && is_array( $args['post__not_in'] ) ? $args['post__not_in'] : array();
|
||||
$args['post__not_in'] = array_merge( $sticky_posts, $post_not_in );
|
||||
}
|
||||
|
||||
if ( 'only' === $args['stickyPosts'] ) {
|
||||
$sticky_posts = get_option( 'sticky_posts' );
|
||||
$args['ignore_sticky_posts'] = true;
|
||||
$args['post__in'] = $sticky_posts;
|
||||
}
|
||||
|
||||
unset( $args['stickyPosts'] );
|
||||
}
|
||||
|
||||
// Ensure offset works correctly with pagination.
|
||||
$posts_per_page = (int) ( $args['posts_per_page'] ?? get_option( 'posts_per_page', -1 ) );
|
||||
if (
|
||||
isset( $args['posts_per_page'] ) &&
|
||||
is_numeric( $args['posts_per_page'] ) &&
|
||||
$posts_per_page > -1
|
||||
) {
|
||||
|
||||
$offset = 0;
|
||||
|
||||
if (
|
||||
isset( $args['offset'] ) &&
|
||||
is_numeric( $args['offset'] )
|
||||
) {
|
||||
$offset = absint( $args['offset'] );
|
||||
}
|
||||
|
||||
$args['offset'] = ( $posts_per_page * ( $page - 1 ) ) + $offset;
|
||||
$args['posts_per_page'] = $posts_per_page;
|
||||
}
|
||||
|
||||
$post_status = $args['post_status'] ?? 'publish';
|
||||
if (
|
||||
'publish' !== $post_status &&
|
||||
! current_user_can( 'read_private_posts' )
|
||||
) {
|
||||
// If the user can't read private posts, we'll force the post status to be public.
|
||||
$args['post_status'] = 'publish';
|
||||
}
|
||||
|
||||
$date_query = $args['date_query'] ?? false;
|
||||
|
||||
if ( is_array( $date_query ) ) {
|
||||
$args['date_query'] = array_map(
|
||||
function( $query ) {
|
||||
$query = array_filter(
|
||||
$query,
|
||||
function( $value ) {
|
||||
return ! empty( $value );
|
||||
}
|
||||
);
|
||||
return $query;
|
||||
},
|
||||
$args['date_query'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy filter for v1 query args compatibility.
|
||||
*
|
||||
* @deprecated 2.0.0.
|
||||
*
|
||||
* @param array $query_args The query arguments.
|
||||
* @param array $attributes An array of block attributes.
|
||||
* @param WP_Block|object $block The block instance.
|
||||
*
|
||||
* @return array The modified query arguments.
|
||||
*/
|
||||
$args = apply_filters(
|
||||
'generateblocks_query_loop_args',
|
||||
$args,
|
||||
$attributes,
|
||||
null === $block ? new stdClass() : $block
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the final calculated query args.
|
||||
*
|
||||
* @param array @query_args The array of args for the WP_Query.
|
||||
* @param array $attributes The block attributes.
|
||||
* @param WP_Block|array $block The current block.
|
||||
* @param array $current The current entities (post, author, etc.).
|
||||
*
|
||||
* @return $args The modified query arguments.
|
||||
*/
|
||||
return apply_filters(
|
||||
'generateblocks_query_wp_query_args',
|
||||
$args,
|
||||
$attributes,
|
||||
null === $block ? new stdClass() : $block,
|
||||
[
|
||||
'post_id' => $current_post_id,
|
||||
'author_id' => $current_author_id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the tax query attributes to be used in the WP_Query
|
||||
*
|
||||
* @param array $raw_tax_query Tax query.
|
||||
* @param string $operator Tax operator.
|
||||
*
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function normalize_tax_query_attributes( $raw_tax_query, $operator = 'IN' ) {
|
||||
return array_map(
|
||||
function( $tax ) use ( $operator ) {
|
||||
return array(
|
||||
'taxonomy' => $tax['taxonomy'],
|
||||
'field' => 'term_id',
|
||||
'terms' => $tax['terms'],
|
||||
'operator' => $operator,
|
||||
'include_children' => isset( $tax['includeChildren'] ) ? $tax['includeChildren'] : true,
|
||||
);
|
||||
},
|
||||
$raw_tax_query
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Query_Utils::get_instance()->init();
|
||||
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
/**
|
||||
* This file handles the dynamic parts of our blocks.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the dynamic aspects of our blocks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class GenerateBlocks_Render_Block {
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* @access private
|
||||
* @var object Instance
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Initiator.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @return object initialized object of class.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'register_blocks' ) );
|
||||
|
||||
if ( version_compare( $GLOBALS['wp_version'], '5.9', '>' ) ) {
|
||||
add_filter( 'render_block', array( $this, 'filter_rendered_blocks' ), 10, 3 );
|
||||
add_filter( 'render_block', array( $this, 'late_filter_rendered_blocks' ), 20, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our dynamic blocks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register_blocks() {
|
||||
$container_args = [
|
||||
'title' => esc_html__( 'Container', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Container', 'render_block' ],
|
||||
];
|
||||
|
||||
if ( version_compare( $GLOBALS['wp_version'], '6.1.0', '<' ) ) {
|
||||
$container_args['editor_script'] = 'generateblocks';
|
||||
$container_args['editor_style'] = 'generateblocks';
|
||||
} else {
|
||||
$container_args['editor_script_handles'] = [ 'generateblocks' ];
|
||||
$container_args['editor_style_handles'] = [ 'generateblocks' ];
|
||||
}
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/container',
|
||||
$container_args
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/grid',
|
||||
array(
|
||||
'title' => esc_html__( 'Grid', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Grid', 'render_block' ],
|
||||
'uses_context' => array(
|
||||
'generateblocks/query',
|
||||
'generateblocks/queryId',
|
||||
'generateblocks/inheritQuery',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/query-loop',
|
||||
array(
|
||||
'title' => esc_html__( 'Query loop', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Grid', 'render_block' ],
|
||||
'provides_context' => array(
|
||||
'generateblocks/query' => 'query',
|
||||
'generateblocks/queryId' => 'uniqueId',
|
||||
'generateblocks/inheritQuery' => 'inheritQuery',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/button-container',
|
||||
array(
|
||||
'title' => esc_html__( 'Buttons', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Button_Container', 'render_block' ],
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/headline',
|
||||
array(
|
||||
'title' => esc_html__( 'Headline', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Headline', 'render_block' ],
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/button',
|
||||
array(
|
||||
'title' => esc_html__( 'Button', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Button', 'render_block' ],
|
||||
'uses_context' => array(
|
||||
'generateblocks/query',
|
||||
'generateblocks/queryId',
|
||||
'generateblocks/inheritQuery',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type(
|
||||
'generateblocks/image',
|
||||
array(
|
||||
'title' => esc_html__( 'Image', 'generateblocks' ),
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Image', 'render_block' ],
|
||||
'uses_context' => array(
|
||||
'generateblocks/query',
|
||||
'generateblocks/queryId',
|
||||
'postType',
|
||||
'postId',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/text',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Text', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/element',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Element', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/media',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Media', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/shape',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Shape', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/query',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Query', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/looper',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Looper', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/query-no-results',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Query_No_Results', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/query-page-numbers',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Query_Page_Numbers', 'render_block' ],
|
||||
]
|
||||
);
|
||||
|
||||
register_block_type_from_metadata(
|
||||
GENERATEBLOCKS_DIR . '/dist/blocks/loop-item',
|
||||
[
|
||||
'render_callback' => [ 'GenerateBlocks_Block_Loop_Item', 'render_block' ],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter existing rendered blocks.
|
||||
*
|
||||
* @since 1.5.0
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block data.
|
||||
* @param WP_Block $instance Block instance.
|
||||
*/
|
||||
public function filter_rendered_blocks( $block_content, $block, $instance ) {
|
||||
$attributes = isset( $block['attrs'] ) ? $block['attrs'] : null;
|
||||
|
||||
// Don't output if no dynamic link exists.
|
||||
if ( isset( $attributes ) && ! empty( $attributes['dynamicLinkType'] ) && ! empty( $attributes['dynamicLinkRemoveIfEmpty'] ) ) {
|
||||
$dynamic_link = GenerateBlocks_Dynamic_Content::get_dynamic_url( $attributes, $instance );
|
||||
|
||||
if ( ! $dynamic_link ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter existing rendered blocks. Fires later than `filter_rendered_blocks`.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @param string $block_content The block content.
|
||||
* @param array $block The block data.
|
||||
* @param WP_Block $instance Block instance.
|
||||
*/
|
||||
public function late_filter_rendered_blocks( $block_content, $block, $instance ) {
|
||||
$block_name = $block['blockName'] ?? '';
|
||||
$attributes = $block['attrs'] ?? [];
|
||||
|
||||
if ( 'generateblocks/media' === $block_name ) {
|
||||
$attachment_id = $attributes['mediaId'] ?? 0;
|
||||
|
||||
if ( ! $attachment_id ) {
|
||||
$p = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
if ( $p->next_tag(
|
||||
[
|
||||
'tag_name' => 'img',
|
||||
]
|
||||
) ) {
|
||||
$attachment_id = (int) $p->get_attribute( 'data-media-id' ) ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $attachment_id > 0 && 'img' === $attributes['tagName'] ) {
|
||||
$context = current_filter();
|
||||
|
||||
if ( ! generateblocks_str_contains( $block_content, ' width=' ) && ! generateblocks_str_contains( $block_content, ' height=' ) ) {
|
||||
$block_content = wp_img_tag_add_width_and_height_attr( $block_content, $context, $attachment_id );
|
||||
}
|
||||
|
||||
// Add 'srcset' and 'sizes' attributes if applicable.
|
||||
if ( ! generateblocks_str_contains( $block_content, ' srcset=' ) ) {
|
||||
$block_content = wp_img_tag_add_srcset_and_sizes_attr( $block_content, $context, $attachment_id );
|
||||
}
|
||||
|
||||
// Add loading optimization attributes if applicable.
|
||||
$block_content = wp_img_tag_add_loading_optimization_attrs( $block_content, $context );
|
||||
}
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Render_Block::get_instance();
|
||||
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
/**
|
||||
* Rest API functions
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class GenerateBlocks_Rest
|
||||
*/
|
||||
class GenerateBlocks_Rest extends WP_REST_Controller {
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* @access private
|
||||
* @var object Instance
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'generateblocks/v';
|
||||
|
||||
/**
|
||||
* Version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '1';
|
||||
|
||||
/**
|
||||
* Onboarding meta key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ONBOARDING_META_KEY = 'generateblocks_onboarding';
|
||||
|
||||
/**
|
||||
* Initiator.
|
||||
*
|
||||
* @return object initialized object of class.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* GenerateBlocks_Rest constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register rest routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
$namespace = $this->namespace . $this->version;
|
||||
|
||||
// Update Settings.
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/settings/',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_settings' ),
|
||||
'permission_callback' => array( $this, 'update_settings_permission' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Update single setting.
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/setting/',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_setting' ),
|
||||
'permission_callback' => array( $this, 'update_settings_permission' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Regenerate CSS Files.
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/regenerate_css_files/',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'regenerate_css_files' ),
|
||||
'permission_callback' => array( $this, 'update_settings_permission' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/onboarding/',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'onboarding' ),
|
||||
'permission_callback' => array( $this, 'onboarding_permission' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/get-attachment-by-url/',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_attachment_by_url' ),
|
||||
'permission_callback' => array( $this, 'can_edit_posts' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get edit options permissions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update_settings_permission() {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize our options.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @param string $name The setting name.
|
||||
* @param mixed $value The value to save.
|
||||
*/
|
||||
public function sanitize_value( $name, $value ) {
|
||||
$callbacks = apply_filters(
|
||||
'generateblocks_option_sanitize_callbacks',
|
||||
array(
|
||||
'container_width' => 'absint',
|
||||
'css_print_method' => 'sanitize_text_field',
|
||||
'sync_responsive_previews' => 'rest_sanitize_boolean',
|
||||
'disable_google_fonts' => 'rest_sanitize_boolean',
|
||||
'gb_use_v1_blocks' => 'rest_sanitize_boolean',
|
||||
)
|
||||
);
|
||||
|
||||
$callback = $callbacks[ $name ];
|
||||
|
||||
if ( ! is_callable( $callback ) ) {
|
||||
return sanitize_text_field( $value );
|
||||
}
|
||||
|
||||
return $callback( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single setting.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
*/
|
||||
public function update_setting( WP_REST_Request $request ) {
|
||||
$name = $request->get_param( 'name' );
|
||||
$value = $request->get_param( 'value' );
|
||||
|
||||
$allowed_settings = [
|
||||
'gb_use_v1_blocks',
|
||||
];
|
||||
|
||||
if ( ! in_array( $name, $allowed_settings, true ) ) {
|
||||
return $this->error( 'rest_forbidden', 'Sorry, you are not allowed to update this setting.' );
|
||||
}
|
||||
|
||||
$value = $this->sanitize_value( $name, $value );
|
||||
|
||||
update_option( $name, $value );
|
||||
|
||||
return $this->success( __( 'Settings saved.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Settings.
|
||||
*
|
||||
* @param WP_REST_Request $request request object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function update_settings( WP_REST_Request $request ) {
|
||||
$current_settings = get_option( 'generateblocks', array() );
|
||||
$new_settings = $request->get_param( 'settings' );
|
||||
|
||||
foreach ( $new_settings as $name => $value ) {
|
||||
// Skip if the option hasn't changed.
|
||||
if ( isset( $current_settings[ $name ] ) && $current_settings[ $name ] === $new_settings[ $name ] ) {
|
||||
unset( $new_settings[ $name ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only save options that we know about.
|
||||
if ( ! array_key_exists( $name, generateblocks_get_option_defaults() ) ) {
|
||||
unset( $new_settings[ $name ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize our value.
|
||||
$new_settings[ $name ] = $this->sanitize_value( $name, $value );
|
||||
}
|
||||
|
||||
if ( empty( $new_settings ) ) {
|
||||
return $this->success( __( 'No changes found.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
if ( is_array( $new_settings ) ) {
|
||||
update_option( 'generateblocks', array_merge( $current_settings, $new_settings ) );
|
||||
}
|
||||
|
||||
return $this->success( __( 'Settings saved.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate CSS Files.
|
||||
*
|
||||
* @param WP_REST_Request $request request object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function regenerate_css_files( WP_REST_Request $request ) {
|
||||
update_option( 'generateblocks_dynamic_css_posts', array() );
|
||||
|
||||
return $this->success( __( 'CSS files regenerated.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an onboard as "viewed" by the user.
|
||||
*
|
||||
* @param WP_REST_Request $request request object.
|
||||
*
|
||||
* @return WP_REST_Response The response.
|
||||
*/
|
||||
public function onboarding( WP_REST_Request $request ) {
|
||||
$user_id = get_current_user_id();
|
||||
$onboard = get_user_meta( $user_id, self::ONBOARDING_META_KEY, true );
|
||||
$key = $request->get_param( 'key' );
|
||||
|
||||
if ( ! $onboard ) {
|
||||
$onboard = array();
|
||||
}
|
||||
|
||||
$onboard[ $key ] = true;
|
||||
|
||||
update_user_meta( get_current_user_id(), self::ONBOARDING_META_KEY, $onboard );
|
||||
|
||||
return new WP_REST_Response( array( 'success' => true ), 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachment object by its URL.
|
||||
*
|
||||
* @param WP_REST_Request $request request object.
|
||||
*
|
||||
* @return WP_REST_Response The response.
|
||||
*/
|
||||
public function get_attachment_by_url( WP_REST_Request $request ) {
|
||||
$url = $request->get_param( 'url' );
|
||||
|
||||
if ( ! $url ) {
|
||||
return $this->error( 'no_url', __( 'No URL provided.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
// Regular expression to remove '-300x300' like size patterns from the URL.
|
||||
|
||||
$pattern = '/-\d+x\d+(?=\.\w{3,4}$)/';
|
||||
$url = preg_replace( $pattern, '', $url );
|
||||
$id = attachment_url_to_postid( $url );
|
||||
|
||||
if ( ! $id ) {
|
||||
return $this->error( 'no_attachment', __( 'No attachment found.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
// Get image metadata, such as width, height, and sizes.
|
||||
$image_metadata = wp_get_attachment_metadata( $id );
|
||||
|
||||
// Get the full URL of the image.
|
||||
$full_url = wp_get_attachment_url( $id );
|
||||
|
||||
// Get URLs for all available sizes.
|
||||
$sizes = [];
|
||||
$image_sizes = get_intermediate_image_sizes();
|
||||
foreach ( $image_sizes as $size ) {
|
||||
$image_src = wp_get_attachment_image_src( $id, $size );
|
||||
if ( $image_src && $image_src[0] !== $full_url ) {
|
||||
$sizes[ $size ] = [
|
||||
'url' => $image_src[0],
|
||||
'width' => $image_src[1],
|
||||
'height' => $image_src[2],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'id' => $id,
|
||||
'full_url' => $full_url,
|
||||
'width' => isset( $image_metadata['width'] ) ? $image_metadata['width'] : '',
|
||||
'height' => isset( $image_metadata['height'] ) ? $image_metadata['height'] : '',
|
||||
'sizes' => $sizes,
|
||||
];
|
||||
|
||||
return $this->success( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get onboarding edit permission.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function onboarding_permission() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can edit posts.
|
||||
*/
|
||||
public function can_edit_posts() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Success rest.
|
||||
*
|
||||
* @param mixed $response response data.
|
||||
* @return mixed
|
||||
*/
|
||||
public function success( $response ) {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error rest.
|
||||
*
|
||||
* @param mixed $code error code.
|
||||
* @param mixed $response response data.
|
||||
* @return mixed
|
||||
*/
|
||||
public function error( $code, $response ) {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'error' => true,
|
||||
'success' => false,
|
||||
'error_code' => $code,
|
||||
'response' => $response,
|
||||
),
|
||||
401
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Rest::get_instance();
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* Our settings page.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Build our settings page.
|
||||
*/
|
||||
class GenerateBlocks_Settings {
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* @access private
|
||||
* @var object Instance
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Initiator.
|
||||
*
|
||||
* @return object initialized object of class.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_menu', array( $this, 'add_menu' ) );
|
||||
add_action( 'generateblocks_settings_area', array( $this, 'add_settings_container' ) );
|
||||
add_action( 'generateblocks_settings_area', array( $this, 'add_blocks_version_settings_container' ), 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our Dashboard menu item.
|
||||
*/
|
||||
public function add_menu() {
|
||||
$settings = add_submenu_page(
|
||||
'generateblocks',
|
||||
__( 'Settings', 'generateblocks' ),
|
||||
__( 'Settings', 'generateblocks' ),
|
||||
'manage_options',
|
||||
'generateblocks-settings',
|
||||
array( $this, 'settings_page' ),
|
||||
1
|
||||
);
|
||||
|
||||
remove_submenu_page( 'generateblocks', 'generateblocks' );
|
||||
|
||||
add_action( "admin_print_scripts-$settings", array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our scripts.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
$generateblocks_deps = array( 'wp-api', 'wp-i18n', 'wp-components', 'wp-element', 'wp-api-fetch' );
|
||||
|
||||
$assets_file = GENERATEBLOCKS_DIR . 'dist/settings.asset.php';
|
||||
$compiled_assets = file_exists( $assets_file )
|
||||
? require $assets_file
|
||||
: false;
|
||||
|
||||
$assets =
|
||||
isset( $compiled_assets['dependencies'] ) &&
|
||||
isset( $compiled_assets['version'] )
|
||||
? $compiled_assets
|
||||
: [
|
||||
'dependencies' => $generateblocks_deps,
|
||||
'version' => filemtime( GENERATEBLOCKS_DIR . 'dist/settings.js' ),
|
||||
];
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-settings',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/settings.js',
|
||||
$assets['dependencies'],
|
||||
$assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( 'generateblocks-settings', 'generateblocks' );
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks-settings',
|
||||
'generateBlocksSettings',
|
||||
array(
|
||||
'settings' => wp_parse_args(
|
||||
get_option( 'generateblocks', array() ),
|
||||
generateblocks_get_option_defaults()
|
||||
),
|
||||
'gpContainerWidth' => function_exists( 'generate_get_option' ) ? generate_get_option( 'container_width' ) : false,
|
||||
'gpContainerWidthLink' => function_exists( 'generate_get_option' ) ?
|
||||
add_query_arg(
|
||||
rawurlencode( 'autofocus[control]' ),
|
||||
rawurlencode( 'generate_settings[container_width]' ),
|
||||
wp_customize_url()
|
||||
) :
|
||||
false,
|
||||
'useV1Blocks' => generateblocks_use_v1_blocks(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add settings container.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function add_settings_container() {
|
||||
echo '<div id="gblocks-block-default-settings"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add blocks version settings container.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public function add_blocks_version_settings_container() {
|
||||
echo '<div id="gblocks-blocks-version-settings"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output our Dashboard HTML.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public function settings_page() {
|
||||
?>
|
||||
<div class="wrap gblocks-dashboard-wrap">
|
||||
<div class="generateblocks-settings-area">
|
||||
<?php do_action( 'generateblocks_settings_area' ); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Settings::get_instance();
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
/**
|
||||
* Our admin Dashboard.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
add_action( 'admin_menu', 'generateblocks_register_dashboard', 9 );
|
||||
/**
|
||||
* Register our Dashboard page.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_register_dashboard() {
|
||||
$dashboard = add_menu_page(
|
||||
__( 'GenerateBlocks', 'generateblocks' ),
|
||||
__( 'GenerateBlocks', 'generateblocks' ),
|
||||
'manage_options',
|
||||
'generateblocks',
|
||||
'generateblocks_do_dashboard'
|
||||
);
|
||||
|
||||
$show_upgrade_menu = apply_filters(
|
||||
'generateblocks_show_upgrade_menu',
|
||||
! defined( 'GENERATEBLOCKS_PRO_VERSION' )
|
||||
);
|
||||
|
||||
if ( $show_upgrade_menu ) {
|
||||
add_submenu_page(
|
||||
'generateblocks',
|
||||
__( 'Upgrade', 'generateblocks' ),
|
||||
__( 'Upgrade', 'generateblocks' ),
|
||||
'manage_options',
|
||||
'generateblocks-upgrade',
|
||||
'generateblocks_do_dashboard'
|
||||
);
|
||||
}
|
||||
|
||||
add_action( "admin_print_styles-$dashboard", 'generateblocks_enqueue_dashboard_scripts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our Dashboard-specific scripts.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function generateblocks_enqueue_dashboard_scripts() {
|
||||
$assets_file = GENERATEBLOCKS_DIR . 'dist/dashboard.asset.php';
|
||||
$compiled_assets = file_exists( $assets_file )
|
||||
? require $assets_file
|
||||
: false;
|
||||
|
||||
$assets =
|
||||
isset( $compiled_assets['dependencies'] ) &&
|
||||
isset( $compiled_assets['version'] )
|
||||
? $compiled_assets
|
||||
: [
|
||||
'dependencies' => [],
|
||||
'version' => filemtime( GENERATEBLOCKS_DIR . 'dist/dashboard.js' ),
|
||||
];
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-dashboard',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/dashboard.js',
|
||||
$assets['dependencies'],
|
||||
$assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( 'generateblocks-dashboard', 'generateblocks' );
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks-dashboard',
|
||||
'generateblocksDashboard',
|
||||
array(
|
||||
'gpImage' => esc_url( GENERATEBLOCKS_DIR_URL ) . 'assets/images/generatepress-sites.png',
|
||||
'gpLogo' => esc_url( GENERATEBLOCKS_DIR_URL ) . 'assets/images/generatepress.svg',
|
||||
'gbVersion' => GENERATEBLOCKS_VERSION,
|
||||
'gbpVersion' => defined( 'GENERATEBLOCKS_PRO_VERSION' )
|
||||
? GENERATEBLOCKS_PRO_VERSION
|
||||
: false,
|
||||
)
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'generateblocks-dashboard',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/dashboard.css',
|
||||
array( 'wp-components' ),
|
||||
GENERATEBLOCKS_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of our Dashboard pages.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
function generateblocks_get_dashboard_pages() {
|
||||
return apply_filters(
|
||||
'generateblocks_dashboard_screens',
|
||||
array(
|
||||
'generateblocks_page_generateblocks-settings',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_filter( 'admin_body_class', 'generateblocks_set_admin_body_classes' );
|
||||
/**
|
||||
* Add admin body classes when needed.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @param string $classes The existing classes.
|
||||
*/
|
||||
function generateblocks_set_admin_body_classes( $classes ) {
|
||||
$dashboard_pages = generateblocks_get_dashboard_pages();
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
if ( in_array( $current_screen->id, $dashboard_pages ) ) {
|
||||
$classes .= ' generateblocks-dashboard-page';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
add_action( 'in_admin_header', 'generateblocks_do_dashboard_headers' );
|
||||
/**
|
||||
* Add our Dashboard headers.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function generateblocks_do_dashboard_headers() {
|
||||
$dashboard_pages = generateblocks_get_dashboard_pages();
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
if ( in_array( $current_screen->id, $dashboard_pages ) ) {
|
||||
generateblocks_do_dashboard_header();
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', 'generateblocks_enqueue_global_dashboard_scripts' );
|
||||
/**
|
||||
* Add our scripts to the page.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_enqueue_global_dashboard_scripts() {
|
||||
wp_enqueue_style(
|
||||
'generateblocks-dashboard-global',
|
||||
GENERATEBLOCKS_DIR_URL . 'assets/css/dashboard-global.css',
|
||||
array(),
|
||||
GENERATEBLOCKS_VERSION
|
||||
);
|
||||
|
||||
$dashboard_pages = generateblocks_get_dashboard_pages();
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
if ( in_array( $current_screen->id, $dashboard_pages ) ) {
|
||||
wp_enqueue_style(
|
||||
'generateblocks-settings-build',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/settings.css',
|
||||
array( 'wp-components' ),
|
||||
GENERATEBLOCKS_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build our Dashboard menu.
|
||||
*/
|
||||
function generateblocks_dashboard_navigation() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
$tabs = apply_filters(
|
||||
'generateblocks_dashboard_tabs',
|
||||
array(
|
||||
'settings' => array(
|
||||
'name' => __( 'Settings', 'generateblocks' ),
|
||||
'url' => admin_url( 'admin.php?page=generateblocks-settings' ),
|
||||
'class' => 'generateblocks_page_generateblocks-settings' === $screen->id ? 'active' : '',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! defined( 'GENERATEBLOCKS_PRO_VERSION' ) ) {
|
||||
$tabs['pro'] = array(
|
||||
'name' => __( 'Get Pro', 'generateblocks' ),
|
||||
'url' => 'https://generatepress.com/blocks/',
|
||||
'class' => '',
|
||||
);
|
||||
}
|
||||
|
||||
// Don't print any markup if we only have one tab.
|
||||
if ( count( $tabs ) === 1 ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="gblocks-navigation">
|
||||
<?php
|
||||
foreach ( $tabs as $tab ) {
|
||||
printf(
|
||||
'<a href="%1$s" class="%2$s">%3$s</a>',
|
||||
esc_url( $tab['url'] ),
|
||||
esc_attr( $tab['class'] ),
|
||||
esc_html( $tab['name'] )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Build our Dashboard header.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
function generateblocks_do_dashboard_header() {
|
||||
?>
|
||||
<div class="gblocks-dashboard-header">
|
||||
<div class="gblocks-dashboard-header-title">
|
||||
<h1>
|
||||
<svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 50 60.12" xml:space="preserve"><path class="st0" d="M6.686 31.622V18.918a.077.077 0 01.05-.072l6.5-2.313 6.5-2.313 9.682-3.445L39.1 7.33a.067.067 0 00.036-.028.074.074 0 00.014-.044V.076a.077.077 0 00-.032-.062.076.076 0 00-.069-.009l-13 4.625-13 4.625-6.5 2.313-6.5 2.313a.067.067 0 00-.036.028.097.097 0 00-.013.046V52.067c0 .026.013.048.032.062s.044.018.069.009l3.267-1.163 3.267-1.163c.015-.005.028-.015.036-.028s.014-.028.014-.044V37.999l.001-6.377c-.001 0 0 0 0 0z"/><path class="st0" d="M23.949 29.976l13-4.625 13-4.625c.015-.005.028-.015.036-.028s.015-.028.015-.044V8.056a.077.077 0 00-.032-.062.076.076 0 00-.069-.009l-13 4.625-13 4.625-6.5 2.313-6.5 2.313a.067.067 0 00-.036.028.074.074 0 00-.014.044V60.045c0 .026.013.048.032.062a.076.076 0 00.069.009l6.475-2.304 6.475-2.304 6.525-2.322 6.525-2.322 6.5-2.313 6.5-2.313c.015-.005.028-.015.036-.028s.014-.025.014-.041V27.193a.077.077 0 00-.032-.062.076.076 0 00-.069-.009l-6.45 2.295L37 31.711a.067.067 0 00-.036.028.074.074 0 00-.014.044v6.272a.077.077 0 01-.05.072l-6.45 2.295L24 42.715a.075.075 0 01-.101-.071V30.046c0-.016.005-.031.014-.044a.08.08 0 01.036-.026z"/></svg>
|
||||
<?php echo esc_html( get_admin_page_title() ); ?>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<?php generateblocks_dashboard_navigation(); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Output our Dashboard HTML.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_do_dashboard() {
|
||||
?>
|
||||
<div class="wrap gblocks-dashboard-wrap">
|
||||
<div id="gblocks-dashboard" />
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
add_action( 'admin_init', 'generateblocks_do_upgrade_redirect' );
|
||||
/**
|
||||
* Redirect to the sales page when landing on the upgrade page.
|
||||
*/
|
||||
function generateblocks_do_upgrade_redirect() {
|
||||
if ( empty( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'generateblocks-upgrade' === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
wp_redirect( 'https://generatepress.com/blocks' ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* Set our block attribute defaults.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Set our block defaults.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function generateblocks_get_block_defaults() {
|
||||
$use_cache = apply_filters( 'generateblocks_use_block_defaults_cache', false );
|
||||
|
||||
if ( $use_cache ) {
|
||||
$cached_data = wp_cache_get(
|
||||
'generateblocks_defaults_cache',
|
||||
'generateblocks_cache_group'
|
||||
);
|
||||
|
||||
if ( $cached_data ) {
|
||||
return $cached_data;
|
||||
}
|
||||
}
|
||||
|
||||
$defaults = apply_filters(
|
||||
'generateblocks_defaults',
|
||||
[
|
||||
'container' => generateblocks_with_global_defaults( GenerateBlocks_Block_Container::defaults() ),
|
||||
'buttonContainer' => generateblocks_with_global_defaults( GenerateBlocks_Block_Button_Container::defaults() ),
|
||||
'button' => generateblocks_with_global_defaults( GenerateBlocks_Block_Button::defaults() ),
|
||||
'gridContainer' => generateblocks_with_global_defaults( GenerateBlocks_Block_Grid::defaults() ),
|
||||
'headline' => generateblocks_with_global_defaults( GenerateBlocks_Block_Headline::defaults() ),
|
||||
'image' => generateblocks_with_global_defaults( GenerateBlocks_Block_Image::defaults() ),
|
||||
]
|
||||
);
|
||||
|
||||
if ( $use_cache ) {
|
||||
wp_cache_set(
|
||||
'generateblocks_defaults_cache',
|
||||
$defaults,
|
||||
'generateblocks_cache_group'
|
||||
);
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get defaults for our general options.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_get_option_defaults() {
|
||||
return apply_filters(
|
||||
'generateblocks_option_defaults',
|
||||
array(
|
||||
'container_width' => 1100,
|
||||
'css_print_method' => 'file',
|
||||
'sync_responsive_previews' => true,
|
||||
'disable_google_fonts' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles to use in the editor/font-end when needed.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
function generateblocks_get_default_styles() {
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$defaultBlockStyles = array(
|
||||
'button' => array(
|
||||
'backgroundColor' => $defaults['button']['backgroundColor'] ? $defaults['button']['backgroundColor'] : '#0366d6',
|
||||
'textColor' => $defaults['button']['textColor'] ? $defaults['button']['textColor'] : '#ffffff',
|
||||
'backgroundColorHover' => $defaults['button']['backgroundColorHover'] ? $defaults['button']['backgroundColorHover'] : '#222222',
|
||||
'textColorHover' => $defaults['button']['textColorHover'] ? $defaults['button']['textColorHover'] : '#ffffff',
|
||||
'spacing' => [
|
||||
'paddingTop' => $defaults['button']['paddingTop'] ? $defaults['button']['paddingTop'] : '15px',
|
||||
'paddingRight' => $defaults['button']['paddingRight'] ? $defaults['button']['paddingRight'] : '20px',
|
||||
'paddingBottom' => $defaults['button']['paddingBottom'] ? $defaults['button']['paddingBottom'] : '15px',
|
||||
'paddingLeft' => $defaults['button']['paddingLeft'] ? $defaults['button']['paddingLeft'] : '20px',
|
||||
],
|
||||
'display' => 'inline-flex',
|
||||
),
|
||||
'container' => array(
|
||||
'gridItemPaddingTop' => '',
|
||||
'gridItemPaddingRight' => '',
|
||||
'gridItemPaddingBottom' => '',
|
||||
'gridItemPaddingLeft' => '',
|
||||
'bgImageSize' => 'full',
|
||||
'shapeDividers' => array(
|
||||
'shape' => 'gb-waves-1',
|
||||
'location' => 'bottom',
|
||||
'height' => 200,
|
||||
'heightTablet' => '',
|
||||
'heightMobile' => '',
|
||||
'width' => 100,
|
||||
'widthTablet' => '',
|
||||
'widthMobile' => '',
|
||||
'flipHorizontally' => false,
|
||||
'zindex' => '',
|
||||
'color' => '#000000',
|
||||
'colorOpacity' => 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if ( function_exists( 'generate_get_default_fonts' ) ) {
|
||||
$font_settings = wp_parse_args(
|
||||
get_option( 'generate_settings', array() ),
|
||||
generate_get_default_fonts()
|
||||
);
|
||||
|
||||
$defaultBlockStyles['headline'] = array(
|
||||
'p' => array(
|
||||
'marginBottom' => $font_settings['paragraph_margin'],
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'em',
|
||||
'fontSize' => $font_settings['body_font_size'],
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h1' => array(
|
||||
'marginBottom' => $font_settings['heading_1_margin_bottom'],
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => $font_settings['heading_1_font_size'],
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => $font_settings['mobile_heading_1_font_size'],
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h2' => array(
|
||||
'marginBottom' => $font_settings['heading_2_margin_bottom'],
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => $font_settings['heading_2_font_size'],
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => $font_settings['mobile_heading_1_font_size'],
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h3' => array(
|
||||
'marginBottom' => $font_settings['heading_3_margin_bottom'],
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => $font_settings['heading_3_font_size'],
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h4' => array(
|
||||
'marginBottom' => '20',
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => '',
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h5' => array(
|
||||
'marginBottom' => '20',
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => '',
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
'h6' => array(
|
||||
'marginBottom' => '20',
|
||||
'marginBottomTablet' => '',
|
||||
'marginBottomMobile' => '',
|
||||
'marginUnit' => 'px',
|
||||
'fontSize' => '',
|
||||
'fontSizeTablet' => '',
|
||||
'fontSizeMobile' => '',
|
||||
'fontSizeUnit' => 'px',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return apply_filters( 'generateblocks_default_block_styles', $defaultBlockStyles );
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Deprecated functions.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to the Dashboard page on single plugin activation.
|
||||
*
|
||||
* @since 0.1
|
||||
* @deprecated 2.0.0
|
||||
*/
|
||||
function generateblocks_dashboard_redirect() {
|
||||
$do_redirect = apply_filters( 'generateblocks_do_activation_redirect', get_option( 'generateblocks_do_activation_redirect', false ) );
|
||||
|
||||
if ( $do_redirect ) {
|
||||
delete_option( 'generateblocks_do_activation_redirect' );
|
||||
wp_safe_redirect( esc_url( admin_url( 'admin.php?page=generateblocks' ) ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a redirect option during plugin activation on non-multisite installs.
|
||||
*
|
||||
* @since 0.1
|
||||
* @deprecated 2.0.0
|
||||
*
|
||||
* @param bool $network_wide Whether or not the plugin is being network activated.
|
||||
*/
|
||||
function generateblocks_do_activate( $network_wide = false ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only used to do a redirect. False positive.
|
||||
if ( ! $network_wide && ! isset( $_GET['activate-multi'] ) ) {
|
||||
update_option( 'generateblocks_do_activation_redirect', true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output permissions for use in admin objects.
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
* @return void
|
||||
*/
|
||||
function generateblocks_admin_head_scripts() {
|
||||
$permissions = apply_filters(
|
||||
'generateblocks_permissions',
|
||||
[
|
||||
'isAdminUser' => current_user_can( 'manage_options' ),
|
||||
'canEditPosts' => current_user_can( 'edit_posts' ),
|
||||
'isGbProActive' => is_plugin_active( 'generateblocks-pro/plugin.php' ),
|
||||
'isGpPremiumActive' => is_plugin_active( 'gp-premium/gp-premium.php' ),
|
||||
]
|
||||
);
|
||||
|
||||
$permission_object = wp_json_encode( $permissions );
|
||||
|
||||
echo sprintf(
|
||||
'<script>
|
||||
const gbPermissions = %s;
|
||||
Object.freeze( gbPermissions );
|
||||
</script>',
|
||||
$permission_object // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
+833
@@ -0,0 +1,833 @@
|
||||
<?php
|
||||
/**
|
||||
* The Dynamic Tag Callbacks class file.
|
||||
*
|
||||
* @package GenerateBlocks\Dynamic_Tags
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for dynamic tag callbacks.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class GenerateBlocks_Dynamic_Tag_Callbacks extends GenerateBlocks_Singleton {
|
||||
|
||||
const DATE_FORMAT_KEY = 'dateFormat';
|
||||
|
||||
|
||||
/**
|
||||
* Wrap a link around the output.
|
||||
*
|
||||
* @param string $output The output.
|
||||
* @param array $options The options.
|
||||
* @param object $instance The block instance.
|
||||
*/
|
||||
private static function with_link( $output, $options, $instance ) {
|
||||
if ( empty( $options['link'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
$link = $options['link'];
|
||||
$parts = explode( ',', $link );
|
||||
$link_to = $parts[0] ?? 'post';
|
||||
$key = $parts[1] ?? null;
|
||||
$url = '';
|
||||
|
||||
switch ( $link_to ) {
|
||||
case 'post':
|
||||
$url = get_permalink( $id );
|
||||
|
||||
break;
|
||||
case 'post_meta':
|
||||
$url = $key ? GenerateBlocks_Meta_Handler::get_post_meta( $id, $key, true ) : '';
|
||||
|
||||
break;
|
||||
case 'comments':
|
||||
$url = get_comments_link( $id );
|
||||
|
||||
break;
|
||||
case 'author_meta':
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$url = $key ? GenerateBlocks_Meta_Handler::get_user_meta( $user_id, $key, true ) : '';
|
||||
|
||||
break;
|
||||
case 'author_archive':
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$url = get_author_posts_url( $user_id );
|
||||
|
||||
break;
|
||||
case 'author_email':
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && ! current_user_can( 'list_users' ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$url = 'mailto:' . get_the_author_meta( 'user_email', $user_id );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $url ) {
|
||||
$output = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( $url ),
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the output by character length.
|
||||
*
|
||||
* @param string $output The tag output.
|
||||
* @param array $options The options.
|
||||
*/
|
||||
private static function with_trunc( $output, $options ) {
|
||||
if ( empty( $options['trunc'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
$trunc_parts = explode( ',', $options['trunc'] );
|
||||
$trunc_words = ! empty( $trunc_parts[1] ) && strpos( $trunc_parts[1], 'words' ) === 0;
|
||||
|
||||
if ( $trunc_words ) {
|
||||
$output = wp_trim_words( $output, (int) $trunc_parts[0], '' );
|
||||
return $output;
|
||||
}
|
||||
|
||||
return substr( $output, 0, (int) $options['trunc'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove leading and trailing whitespace from the output.
|
||||
*
|
||||
* @param string $output The tag output.
|
||||
* @param array $options The options.
|
||||
*/
|
||||
private static function with_trim( $output, $options ) {
|
||||
if ( empty( $options['trim'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
switch ( $options['trim'] ) {
|
||||
case 'left':
|
||||
return ltrim( $output );
|
||||
case 'right':
|
||||
return rtrim( $output );
|
||||
default:
|
||||
return trim( $output ); }
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the case of the output.
|
||||
*
|
||||
* @param string $output The tag output.
|
||||
* @param array $options The options.
|
||||
*/
|
||||
private static function with_case( $output, $options ) {
|
||||
if ( empty( $options['case'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
switch ( $options['case'] ) {
|
||||
case 'lower':
|
||||
return strtolower( $output );
|
||||
case 'upper':
|
||||
return strtoupper( $output );
|
||||
case 'title':
|
||||
return ucwords( $output );
|
||||
default:
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the case of the output.
|
||||
*
|
||||
* @param string $output The tag output.
|
||||
* @param array $options The options.
|
||||
*/
|
||||
private static function with_replace( $output, $options ) {
|
||||
if ( empty( $options['replace'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$replace_parts = array_map(
|
||||
function ( $str ) {
|
||||
$result = '';
|
||||
$escaped = false;
|
||||
$length = strlen( $str );
|
||||
|
||||
for ( $i = 0; $i < $length; $i++ ) {
|
||||
$char = $str[ $i ];
|
||||
|
||||
if ( $escaped ) {
|
||||
$result .= $char;
|
||||
$escaped = false;
|
||||
} elseif ( '\\' === $char ) {
|
||||
$escaped = true;
|
||||
} elseif ( '"' !== $char && "'" !== $char ) {
|
||||
$result .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
explode( ',', $options['replace'] )
|
||||
);
|
||||
if ( count( $replace_parts ) !== 2 ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$search = (string) $replace_parts[0] ?? '';
|
||||
$replace = (string) $replace_parts[1] ?? '';
|
||||
|
||||
return str_replace( $search, $replace, $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add line breaks to content like the classic editor.
|
||||
*
|
||||
* @param string $output The tag output.
|
||||
* @param array $options The options.
|
||||
*/
|
||||
private static function with_wpautop( $output, $options ) {
|
||||
if ( empty( $options['wpautop'] ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
return wpautop( $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the dynamic tag.
|
||||
*
|
||||
* @param string $output The output.
|
||||
* @param array $options The options.
|
||||
* @param object $instance The block instance.
|
||||
*
|
||||
* @return string The tag output.
|
||||
*/
|
||||
public static function output( $output, $options, $instance = null ) {
|
||||
// Store original output for filtering.
|
||||
$raw_output = $output;
|
||||
|
||||
$output = self::with_trunc( $output, $options );
|
||||
$output = self::with_replace( $output, $options );
|
||||
$output = self::with_trim( $output, $options );
|
||||
$output = self::with_case( $output, $options );
|
||||
|
||||
// Any wrapping output filters should go after direct string transformations.
|
||||
$output = self::with_wpautop( $output, $options );
|
||||
$output = self::with_link( $output, $options, $instance );
|
||||
|
||||
$output = apply_filters( 'generateblocks_dynamic_tag_output', $output, $options, $raw_output );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_the_title( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$output = get_the_title( $id );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permalink.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_the_permalink( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$output = get_permalink( $id );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post date.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_post_date( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$type = $options['type'] ?? 'published';
|
||||
$format = $options[ self::DATE_FORMAT_KEY ] ?? '';
|
||||
$output = get_the_date( $format, $id );
|
||||
|
||||
if ( 'modified' === $type ) {
|
||||
$output = get_the_modified_date( $format, $id );
|
||||
}
|
||||
|
||||
if ( 'modifiedOnly' === $type ) {
|
||||
$modified_date = get_the_modified_date( $format, $id );
|
||||
|
||||
if ( $modified_date === $output ) {
|
||||
$output = '';
|
||||
}
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the featured image.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_featured_image( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$image_id = get_post_thumbnail_id( $id );
|
||||
|
||||
if ( ! $image_id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$size = $options['size'] ?? 'full';
|
||||
$image = wp_get_attachment_image_src( $image_id, $size );
|
||||
|
||||
if ( ! $image ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
switch ( $options['key'] ?? '' ) {
|
||||
case 'id':
|
||||
$output = $image_id;
|
||||
break;
|
||||
case 'alt':
|
||||
$output = get_post_meta( $image_id, '_wp_attachment_image_alt', true );
|
||||
break;
|
||||
case 'caption':
|
||||
$output = wp_get_attachment_caption( $image_id );
|
||||
break;
|
||||
case 'description':
|
||||
$output = get_post_field( 'post_content', $image_id );
|
||||
break;
|
||||
case 'url':
|
||||
default:
|
||||
$output = $image[0];
|
||||
break;
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post meta.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_post_meta( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$key = $options['key'] ?? '';
|
||||
|
||||
if ( ! $key ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$value = GenerateBlocks_Meta_Handler::get_post_meta( $id, $key, true );
|
||||
|
||||
if ( ! $value ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
add_filter( 'wp_kses_allowed_html', [ 'GenerateBlocks_Dynamic_Tags', 'expand_allowed_html' ], 10, 2 );
|
||||
$output = wp_kses_post( $value );
|
||||
remove_filter( 'wp_kses_allowed_html', [ 'GenerateBlocks_Dynamic_Tags', 'expand_allowed_html' ], 10, 2 );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comments count.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return int
|
||||
*/
|
||||
public static function get_the_comments_count( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$none = $options['none'] ?? __( 'No comments', 'generateblocks' );
|
||||
$single = $options['single'] ?? __( '1 comment', 'generateblocks' );
|
||||
$multi = $options['multiple'] ?? __( '% comments', 'generateblocks' );
|
||||
$output = '';
|
||||
|
||||
if ( ! post_password_required( $id ) && ( comments_open( $id ) || get_comments_number( $id ) ) ) {
|
||||
if ( '' === $none && get_comments_number( $id ) < 1 ) {
|
||||
return self::output( $none, $options, $instance );
|
||||
}
|
||||
|
||||
$output = get_comments_number_text(
|
||||
$none,
|
||||
$single,
|
||||
$multi,
|
||||
$id
|
||||
);
|
||||
} else {
|
||||
$output = $none;
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comments URL.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_the_comments_url( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$output = get_comments_link( $id );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get filtered userdata with only the relevant keys.
|
||||
*
|
||||
* @param int $user_id The user ID to get data for.
|
||||
* @return array Filtered data array
|
||||
*/
|
||||
private static function get_userdata( $user_id ) {
|
||||
$userdata = get_userdata( $user_id )->data ?? new stdClass();
|
||||
|
||||
return [
|
||||
'user_nicename' => $userdata->user_nicename ?? '',
|
||||
'user_email' => $userdata->user_email ?? '',
|
||||
'display_name' => $userdata->display_name ?? '',
|
||||
'ID' => $userdata->ID ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author meta.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_author_meta( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$key = $options['key'] ?? '';
|
||||
$output = '';
|
||||
|
||||
if ( ! $user_id || ! $key ) {
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
$value = GenerateBlocks_Meta_Handler::get_user_meta( $user_id, $key );
|
||||
|
||||
add_filter( 'wp_kses_allowed_html', [ 'GenerateBlocks_Dynamic_Tags', 'expand_allowed_html' ], 10, 2 );
|
||||
$output = wp_kses_post( $value );
|
||||
remove_filter( 'wp_kses_allowed_html', [ 'GenerateBlocks_Dynamic_Tags', 'expand_allowed_html' ], 10, 2 );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author archive URL.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_author_archive_url( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$output = '';
|
||||
|
||||
if ( ! $user_id ) {
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
$output = get_author_posts_url( $user_id );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author avatar URL.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_author_avatar_url( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
$size = $options['size'] ?? null;
|
||||
$default = $options['default'] ?? null;
|
||||
$user_id = get_post_field( 'post_author', $id );
|
||||
$force_default = $options['forceDefault'] ?? null;
|
||||
$rating = $options['rating'] ?? null;
|
||||
$output = '';
|
||||
|
||||
if ( ! $user_id ) {
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
$args = [];
|
||||
|
||||
if ( $size ) {
|
||||
$args['size'] = $size;
|
||||
}
|
||||
|
||||
if ( $default ) {
|
||||
$args['default'] = $default;
|
||||
}
|
||||
|
||||
// This is false by default so only add the arg if it's true.
|
||||
if ( $force_default ) {
|
||||
$args['force_default'] = $force_default;
|
||||
}
|
||||
|
||||
if ( $rating ) {
|
||||
$args['rating'] = $rating;
|
||||
}
|
||||
|
||||
$output = get_avatar_url( $user_id, $args );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site tagline from settings.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_term_list( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$taxonomy = $options['tax'] ?? '';
|
||||
$separator = ! empty( $options['sep'] ) ? $options['sep'] : '';
|
||||
$link = empty( $options['link'] ) ? false : (bool) $options['link'];
|
||||
$terms = get_the_terms( $id, $taxonomy );
|
||||
|
||||
if ( ! $terms ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$list_items = [];
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
if ( $link ) {
|
||||
$link = get_term_link( $term, $taxonomy );
|
||||
|
||||
if ( is_wp_error( $link ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list_items[] = '<a href="' . esc_url( $link ) . '" rel="tag">' . $term->name . '</a>';
|
||||
|
||||
$list_items = apply_filters( "term_links-{$taxonomy}", $list_items ); // phpcs:ignore
|
||||
} else {
|
||||
$list_items[] = "<span>{$term->name}</span>";
|
||||
}
|
||||
}
|
||||
|
||||
$output = implode( $separator, $list_items );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
/**
|
||||
* Get the post's excerpt, optionally with a custom read more link.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_post_excerpt( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
static $seen_ids = [];
|
||||
|
||||
if ( isset( $seen_ids[ $id ] ) ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
$seen_ids[ $id ] = true;
|
||||
$read_more = $options['readMore'] ?? '';
|
||||
$pre_read_more = $options['pre'] ?? '';
|
||||
$use_theme_read_more = isset( $options['useTheme'] ) ? true : false;
|
||||
$filter_excerpt_length = function( $length ) use ( $options ) {
|
||||
return $options['length'] ?? $length;
|
||||
};
|
||||
|
||||
add_filter(
|
||||
'excerpt_length',
|
||||
$filter_excerpt_length,
|
||||
100
|
||||
);
|
||||
|
||||
if ( ! $use_theme_read_more ) {
|
||||
$filter_more_text = function() use ( $read_more, $pre_read_more ) {
|
||||
if ( ! $read_more ) {
|
||||
return $pre_read_more;
|
||||
}
|
||||
|
||||
return apply_filters(
|
||||
'generateblocks_dynamic_excerpt_more_link',
|
||||
sprintf(
|
||||
'%1$s<a class="gb-dynamic-read-more" href="%2$s" aria-label="%3$s">%4$s</a>',
|
||||
wp_kses_post( $pre_read_more ),
|
||||
esc_url( get_permalink( get_the_ID() ) ),
|
||||
sprintf(
|
||||
/* translators: Aria-label describing the read more button */
|
||||
_x( 'More on %s', 'more on post title', 'generateblocks' ),
|
||||
the_title_attribute( 'echo=0' )
|
||||
),
|
||||
wp_kses_post( $read_more )
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
add_filter(
|
||||
'excerpt_more',
|
||||
$filter_more_text,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
$output = get_the_excerpt( $id );
|
||||
|
||||
if ( isset( $filter_excerpt_length ) ) {
|
||||
remove_filter(
|
||||
'excerpt_length',
|
||||
$filter_excerpt_length,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $filter_more_text ) ) {
|
||||
remove_filter(
|
||||
'excerpt_more',
|
||||
$filter_more_text,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
unset( $seen_ids[ $id ] );
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous post page URL.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_previous_posts_page_url( $options, $block, $instance ) {
|
||||
$query_id = $instance->context['generateblocks/queryData']['id'] ?? '';
|
||||
$page_key = $query_id ? 'query-' . $query_id . '-page' : 'query-page';
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$inherit_query = $instance->context['generateblocks/queryData']['inherit'] ?? false;
|
||||
$has_query = isset( $instance->context['generateblocks/queryData'] );
|
||||
$output = '';
|
||||
|
||||
if ( $inherit_query || ! $has_query ) {
|
||||
global $paged;
|
||||
|
||||
if ( $paged > 1 ) {
|
||||
$output = previous_posts( false );
|
||||
}
|
||||
} elseif ( 1 !== $page ) {
|
||||
$output = esc_url( add_query_arg( $page_key, $page - 1 ) );
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next post page URL.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_next_posts_page_url( $options, $block, $instance ) {
|
||||
$query_id = $instance->context['generateblocks/queryData']['id'] ?? '';
|
||||
$page_key = $query_id ? 'query-' . $query_id . '-page' : 'query-page';
|
||||
$page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ]; // phpcs:ignore -- No data processing happening.
|
||||
$args = $instance->context['generateblocks/queryData']['args'] ?? [];
|
||||
$inherit_query = $instance->context['generateblocks/queryData']['inherit'] ?? false;
|
||||
$has_query = isset( $instance->context['generateblocks/queryData'] );
|
||||
$per_page = $args['posts_per_page'] ?? apply_filters( 'generateblocks_query_per_page_default', 10, $args );
|
||||
$output = '';
|
||||
|
||||
if ( $inherit_query || ! $has_query ) {
|
||||
global $wp_query, $paged;
|
||||
|
||||
if ( ! $paged ) {
|
||||
$paged = 1; // phpcs:ignore -- Need to overrite global here.
|
||||
}
|
||||
|
||||
$next_page = (int) $paged + 1;
|
||||
|
||||
if ( $next_page <= $wp_query->max_num_pages ) {
|
||||
$output = next_posts( $wp_query->max_num_pages, false );
|
||||
}
|
||||
} else {
|
||||
$query_data = $instance->context['generateblocks/queryData']['data'] ?? null;
|
||||
$query_type = $instance->context['generateblocks/queryData']['type'] ?? null;
|
||||
$is_wp_query = GenerateBlocks_Block_Query::TYPE_WP_QUERY === $query_type;
|
||||
|
||||
if ( ! $query_data || ( ! $is_wp_query && ! is_array( $query_data ) ) ) {
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
$next_page = $page + 1;
|
||||
$custom_query_max_pages = $is_wp_query
|
||||
? (int) $query_data->max_num_pages
|
||||
: ceil( count( $query_data ) / $per_page );
|
||||
|
||||
if ( $custom_query_max_pages < $next_page ) {
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
if ( $custom_query_max_pages && $custom_query_max_pages !== $page ) {
|
||||
$output = esc_url( add_query_arg( $page_key, $page + 1 ) );
|
||||
}
|
||||
|
||||
wp_reset_postdata(); // Restore original Post Data.
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media.
|
||||
*
|
||||
* @param array $options The options.
|
||||
* @param array $block The block.
|
||||
* @param object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_media( $options, $block, $instance ) {
|
||||
$id = GenerateBlocks_Dynamic_Tags::get_id( $options, 'post', $instance );
|
||||
|
||||
if ( ! $id ) {
|
||||
return self::output( '', $options, $instance );
|
||||
}
|
||||
|
||||
switch ( $options['key'] ?? '' ) {
|
||||
case 'title':
|
||||
$output = get_the_title( $id );
|
||||
break;
|
||||
case 'id':
|
||||
$output = $id;
|
||||
break;
|
||||
case 'alt':
|
||||
$output = get_post_meta( $id, '_wp_attachment_image_alt', true );
|
||||
break;
|
||||
case 'caption':
|
||||
$output = wp_get_attachment_caption( $id );
|
||||
break;
|
||||
case 'description':
|
||||
$output = get_post_field( 'post_content', $id );
|
||||
break;
|
||||
case 'url':
|
||||
default:
|
||||
$output = wp_get_attachment_url( $id );
|
||||
break;
|
||||
}
|
||||
|
||||
return self::output( $output, $options, $instance );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+233
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
/**
|
||||
* The Register Dynamic Tag class file.
|
||||
*
|
||||
* @package GenerateBlocks\Dynamic_Tags
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for registering dynamic tags.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
class GenerateBlocks_Register_Dynamic_Tag {
|
||||
/**
|
||||
* The tags.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $tags = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $args The arguments.
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
if ( ! isset( $args['tag'] ) || ! isset( $args['return'] ) || ! isset( $args['title'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $args['type'] ) ) {
|
||||
$args['type'] = 'post';
|
||||
}
|
||||
|
||||
self::$tags[ $args['tag'] ] = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse options.
|
||||
*
|
||||
* @param string $options_string The options string.
|
||||
* @param string $tag_name The tag name.
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_options( $options_string, $tag_name ) {
|
||||
$pairs = $options_string ? preg_split( '/(?<!\\\\)\|/', $options_string, -1, PREG_SPLIT_NO_EMPTY ) : [];
|
||||
$result = [
|
||||
'tag_name' => $tag_name, // Make it so the tag name is available to us in $options.
|
||||
];
|
||||
|
||||
if ( empty( $pairs ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ( $pairs as $pair ) {
|
||||
$pair = str_replace( [ '\\:', '\\|' ], [ ':', '|' ], $pair );
|
||||
|
||||
if ( generateblocks_str_contains( $pair, ':' ) ) {
|
||||
list( $key, $value ) = explode( ':', $pair, 2 );
|
||||
} else {
|
||||
$key = $pair;
|
||||
$value = true; // Default value if no colon is present.
|
||||
}
|
||||
|
||||
$result[ $key ] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tags.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_tags() {
|
||||
return self::$tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find matches.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @param array $availableTags The available tags.
|
||||
* @return array
|
||||
*/
|
||||
public static function find_matches( $content, $availableTags ) {
|
||||
$pattern = '/\{{(' . implode( '|', array_keys( $availableTags ) ) . ')(\s+[^}]+)?}}/';
|
||||
preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER );
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace tags.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @param array $block The block.
|
||||
* @param Object $instance The block instance.
|
||||
* @return string
|
||||
*/
|
||||
public static function replace_tags( $content, $block, $instance ) {
|
||||
$block_html = ! empty( $block['innerHTML'] )
|
||||
? $block['innerHTML']
|
||||
: $content;
|
||||
|
||||
if ( ! generateblocks_str_contains( $block_html, '{{' ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$matches = self::find_matches( $block_html, self::$tags );
|
||||
|
||||
foreach ( $matches as $match ) {
|
||||
$tag_name = $match[1] ?? '';
|
||||
|
||||
if ( ! isset( self::$tags[ $tag_name ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = self::$tags[ $tag_name ];
|
||||
$full_tag = $match[0];
|
||||
$full_tag = self::maybe_prepend_protocol( $block_html, $full_tag );
|
||||
$options_string = isset( $match[2] ) ? ltrim( $match[2], ' ' ) : '';
|
||||
$options = self::parse_options( $options_string, $tag_name );
|
||||
$replacement = $data['return']( $options, $block, $instance );
|
||||
$og_replacement = $replacement;
|
||||
$supports = $data['supports'];
|
||||
$required = ! isset( $options['required'] ) || 'false' !== $options['required'];
|
||||
|
||||
/**
|
||||
* Allow developers to filter the replacement.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param string $replacement The replacement.
|
||||
* @param string $full_tag The full tag.
|
||||
* @param mixed $content The replacement.
|
||||
* @param array $block The block.
|
||||
* @param Object $instance The block instance.
|
||||
* @param array $options The options.
|
||||
* @param array $supports The supports.
|
||||
*/
|
||||
$replacement = apply_filters(
|
||||
'generateblocks_dynamic_tag_replacement',
|
||||
$replacement,
|
||||
[
|
||||
'tag' => $tag_name,
|
||||
'full_tag' => $full_tag,
|
||||
'content' => $content,
|
||||
'block' => $block,
|
||||
'instance' => $instance,
|
||||
'options' => $options,
|
||||
'supports' => $supports,
|
||||
]
|
||||
);
|
||||
|
||||
// If this tag is required for the block to render and there is no replacement, bail.
|
||||
if ( $required && ! $replacement ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow developers to filter the content before dynamic tag replacement.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @param string $full_tag The full tag.
|
||||
* @param string $tag The tag.
|
||||
* @param mixed $replacement The replacement.
|
||||
* @param mixed $og_replacement The original replacement.
|
||||
* @param array $block The block.
|
||||
* @param Object $instance The block instance.
|
||||
* @param array $options The options.
|
||||
* @param array $supports The supports.
|
||||
*/
|
||||
$content = apply_filters(
|
||||
'generateblocks_before_dynamic_tag_replace',
|
||||
$content,
|
||||
[
|
||||
'full_tag' => $full_tag,
|
||||
'tag' => $tag_name,
|
||||
'replacement' => $replacement,
|
||||
'og_replacement' => $og_replacement,
|
||||
'block' => $block,
|
||||
'instance' => $instance,
|
||||
'options' => $options,
|
||||
'supports' => $supports,
|
||||
]
|
||||
);
|
||||
|
||||
$content = str_replace( $full_tag, (string) $replacement, $content );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe prepend the protocol to our dynamic tag.
|
||||
* Some core blocks automatically prepend the protocol to URLs, so we need to account for that.
|
||||
* This function checks if the protocol is already prepended and if so, prepends it to the tag so the entire thing is replaced.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @param string $tag The tag.
|
||||
* @return string
|
||||
*/
|
||||
public static function maybe_prepend_protocol( $content, $tag ) {
|
||||
if ( generateblocks_str_contains( $content, 'http://' . $tag ) ) {
|
||||
$tag = 'http://' . $tag;
|
||||
}
|
||||
|
||||
if ( generateblocks_str_contains( $content, 'https://' . $tag ) ) {
|
||||
$tag = 'https://' . $tag;
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the details of a specific registered tag.
|
||||
*
|
||||
* @param string $tag The dynamic tag used for lookup.
|
||||
* @return array|null The tag details or null if not found.
|
||||
*/
|
||||
public static function get_tag_details( $tag ) {
|
||||
return self::$tags[ $tag ] ?? null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,734 @@
|
||||
<?php
|
||||
/**
|
||||
* General actions and filters.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
add_action( 'enqueue_block_editor_assets', 'generateblocks_do_block_editor_assets' );
|
||||
/**
|
||||
* Enqueue Gutenberg block assets for backend editor.
|
||||
*
|
||||
* @uses {wp-blocks} for block type registration & related functions.
|
||||
* @uses {wp-element} for WP Element abstraction — structure of blocks.
|
||||
* @uses {wp-i18n} to internationalize the block's text.
|
||||
* @uses {wp-editor} for WP editor styles.
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_do_block_editor_assets() {
|
||||
wp_localize_script(
|
||||
'generateblocks-media-editor-script',
|
||||
'generateblocksBlockMedia',
|
||||
[
|
||||
'standardPlaceholder' => GENERATEBLOCKS_DIR_URL . 'assets/images/placeholder1280x720.png',
|
||||
'squarePlaceholder' => GENERATEBLOCKS_DIR_URL . 'assets/images/placeholder800x.png',
|
||||
]
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks-text-editor-script',
|
||||
'generateblocksBlockText',
|
||||
[
|
||||
'defaultButtonAttributes' => apply_filters(
|
||||
'generateblocks_default_button_attributes',
|
||||
[
|
||||
'styles' => [
|
||||
'display' => 'inline-flex',
|
||||
'alignItems' => 'center',
|
||||
'backgroundColor' => '#215bc2',
|
||||
'color' => '#ffffff',
|
||||
'paddingTop' => '1rem',
|
||||
'paddingRight' => '2rem',
|
||||
'paddingBottom' => '1rem',
|
||||
'paddingLeft' => '2rem',
|
||||
'textDecoration' => 'none',
|
||||
'&:is(:hover, :focus)' => [
|
||||
'backgroundColor' => '#1a4a9b',
|
||||
'color' => '#ffffff',
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
global $pagenow;
|
||||
|
||||
$generateblocks_deps = array( 'wp-blocks', 'wp-i18n', 'wp-editor', 'wp-element', 'wp-compose', 'wp-data' );
|
||||
|
||||
if ( 'widgets.php' === $pagenow ) {
|
||||
unset( $generateblocks_deps[2] );
|
||||
}
|
||||
|
||||
$assets_file = GENERATEBLOCKS_DIR . 'dist/blocks.asset.php';
|
||||
$compiled_assets = file_exists( $assets_file )
|
||||
? require $assets_file
|
||||
: false;
|
||||
|
||||
$assets =
|
||||
isset( $compiled_assets['dependencies'] ) &&
|
||||
isset( $compiled_assets['version'] )
|
||||
? $compiled_assets
|
||||
: [
|
||||
'dependencies' => $generateblocks_deps,
|
||||
'version' => filemtime( GENERATEBLOCKS_DIR . 'dist/blocks.js' ),
|
||||
];
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/blocks.js',
|
||||
$assets['dependencies'],
|
||||
$assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( 'generateblocks', 'generateblocks' );
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'generateblocks',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/blocks.css',
|
||||
array( 'wp-edit-blocks', 'generateblocks-packages' ),
|
||||
filemtime( GENERATEBLOCKS_DIR . 'dist/blocks.css' )
|
||||
);
|
||||
|
||||
$image_sizes = get_intermediate_image_sizes();
|
||||
$image_sizes = array_diff( $image_sizes, array( '1536x1536', '2048x2048' ) );
|
||||
$image_sizes[] = 'full';
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks',
|
||||
'generateBlocksInfo',
|
||||
array(
|
||||
'imageSizes' => $image_sizes,
|
||||
'svgShapes' => generateblocks_get_svg_shapes(),
|
||||
'syncResponsivePreviews' => generateblocks_get_option( 'sync_responsive_previews' ),
|
||||
'excerptLength' => apply_filters( 'excerpt_length', 55 ), // phpcs:ignore -- Core filter.
|
||||
'excerptMore' => apply_filters( 'excerpt_more', ' ' . '[…]' ), // phpcs:ignore -- Core filter.
|
||||
'imagePlaceholders' => array(
|
||||
'standard' => GENERATEBLOCKS_DIR_URL . 'assets/images/image-placeholder.png',
|
||||
'square' => GENERATEBLOCKS_DIR_URL . 'assets/images/square-image-placeholder.png',
|
||||
),
|
||||
'globalContainerWidth' => generateblocks_get_global_container_width(),
|
||||
'queryLoopEditorPostsCap' => apply_filters( 'generateblocks_query_loop_editor_posts_cap', 50 ),
|
||||
'disableGoogleFonts' => generateblocks_get_option( 'disable_google_fonts' ),
|
||||
'typographyFontFamilyList' => generateblocks_get_font_family_list(),
|
||||
'useV1Blocks' => generateblocks_use_v1_blocks(),
|
||||
)
|
||||
);
|
||||
|
||||
if ( function_exists( 'generate_get_color_defaults' ) ) {
|
||||
$color_settings = wp_parse_args(
|
||||
get_option( 'generate_settings', array() ),
|
||||
generate_get_color_defaults()
|
||||
);
|
||||
|
||||
$generatepressDefaultStyling = apply_filters(
|
||||
'generateblocks_gp_default_styling',
|
||||
array(
|
||||
'buttonBackground' => $color_settings['form_button_background_color'],
|
||||
'buttonBackgroundHover' => $color_settings['form_button_background_color_hover'],
|
||||
'buttonText' => $color_settings['form_button_text_color'],
|
||||
'buttonTextHover' => $color_settings['form_button_text_color_hover'],
|
||||
'buttonPaddingTop' => '10px',
|
||||
'buttonPaddingRight' => '20px',
|
||||
'buttonPaddingBottom' => '10px',
|
||||
'buttonPaddingLeft' => '20px',
|
||||
)
|
||||
);
|
||||
|
||||
$css = sprintf(
|
||||
'.gb-button.button {
|
||||
background-color: %1$s;
|
||||
color: %2$s;
|
||||
padding-top: %3$s;
|
||||
padding-right: %4$s;
|
||||
padding-bottom: %5$s;
|
||||
padding-left: %6$s;
|
||||
}',
|
||||
$generatepressDefaultStyling['buttonBackground'],
|
||||
$generatepressDefaultStyling['buttonText'],
|
||||
$generatepressDefaultStyling['buttonPaddingTop'],
|
||||
$generatepressDefaultStyling['buttonPaddingRight'],
|
||||
$generatepressDefaultStyling['buttonPaddingBottom'],
|
||||
$generatepressDefaultStyling['buttonPaddingLeft']
|
||||
);
|
||||
|
||||
$css .= sprintf(
|
||||
'.gb-button.button:active, .gb-button.button:hover, .gb-button.button:focus {
|
||||
background-color: %1$s;
|
||||
color: %2$s;
|
||||
}',
|
||||
$generatepressDefaultStyling['buttonBackgroundHover'],
|
||||
$generatepressDefaultStyling['buttonTextHover']
|
||||
);
|
||||
|
||||
wp_add_inline_style( 'generateblocks', $css );
|
||||
}
|
||||
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks',
|
||||
'generateBlocksDefaults',
|
||||
$defaults
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks',
|
||||
'generateBlocksStyling',
|
||||
generateblocks_get_default_styles()
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks',
|
||||
'generateBlocksLegacyDefaults',
|
||||
array(
|
||||
'v_1_4_0' => GenerateBlocks_Legacy_Attributes::get_defaults( '1.4.0' ),
|
||||
)
|
||||
);
|
||||
|
||||
$editor_sidebar_assets = generateblocks_get_enqueue_assets( 'editor-sidebar' );
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-editor-sidebar',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/editor-sidebar.js',
|
||||
$editor_sidebar_assets['dependencies'],
|
||||
$editor_sidebar_assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( 'generateblocks-editor-sidebar', 'generateblocks' );
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'generateblocks-editor-sidebar',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/editor-sidebar.css',
|
||||
array( 'wp-components' ),
|
||||
filemtime( GENERATEBLOCKS_DIR . 'dist/editor-sidebar.css' )
|
||||
);
|
||||
|
||||
$packages_asset_info = generateblocks_get_enqueue_assets( 'packages' );
|
||||
wp_register_style(
|
||||
'generateblocks-packages',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/packages.css',
|
||||
'',
|
||||
$packages_asset_info['version']
|
||||
);
|
||||
|
||||
$edge22_packages = [
|
||||
'block-styles',
|
||||
'components',
|
||||
'styles-builder',
|
||||
];
|
||||
|
||||
foreach ( $edge22_packages as $name ) {
|
||||
$package_asset_info = generateblocks_get_enqueue_assets( "{$name}-imported" );
|
||||
$script_asset_info = generateblocks_get_enqueue_assets( $name );
|
||||
|
||||
wp_register_script(
|
||||
"generateblocks-$name",
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/' . $name . '.js',
|
||||
$package_asset_info['dependencies'],
|
||||
$script_asset_info['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
"generateblocks-$name",
|
||||
false,
|
||||
[ 'generateblocks-packages' ],
|
||||
$packages_asset_info['version']
|
||||
);
|
||||
}
|
||||
|
||||
$editor_assets = generateblocks_get_enqueue_assets( 'editor' );
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-editor',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/editor.js',
|
||||
$editor_assets['dependencies'],
|
||||
$editor_assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
$tags = GenerateBlocks_Register_Dynamic_Tag::get_tags();
|
||||
$tag_list = [];
|
||||
|
||||
foreach ( $tags as $tag => $data ) {
|
||||
$relevant_data = $data;
|
||||
unset( $relevant_data['return'] );
|
||||
if ( $data ) {
|
||||
$tag_list[] = $relevant_data;
|
||||
}
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks-editor',
|
||||
'generateBlocksEditor',
|
||||
[
|
||||
'useV1Blocks' => generateblocks_use_v1_blocks(),
|
||||
/**
|
||||
* Filters whether WordPress 7.0's core "Additional CSS" textarea is
|
||||
* shown on GenerateBlocks blocks. We disable it by default because our
|
||||
* blocks have their own CSS editing; return true to re-enable it.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param bool $enabled Whether to allow the core Additional CSS field.
|
||||
*/
|
||||
'enableCoreAdditionalCss' => (bool) apply_filters( 'generateblocks_enable_core_additional_css', false ),
|
||||
'dynamicTags' => $tag_list,
|
||||
'hasGPFontLibrary' => function_exists( 'generatepress_is_module_active' )
|
||||
? generatepress_is_module_active( 'generate_package_font_library', 'GENERATE_FONT_LIBRARY' )
|
||||
: false,
|
||||
'dateFormat' => get_option( 'date_format' ),
|
||||
'wpContentUrl' => content_url(),
|
||||
'typographyFontFamilyList' => generateblocks_get_font_family_list(),
|
||||
'dynamicTagsPreview' => apply_filters( 'generateblocks_dynamic_tags_preview', true ) ? 'enabled' : 'disabled',
|
||||
]
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'generateblocks-editor',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/editor.css',
|
||||
array( 'wp-edit-blocks', 'generateblocks-packages' ),
|
||||
filemtime( GENERATEBLOCKS_DIR . 'dist/editor.css' )
|
||||
);
|
||||
}
|
||||
|
||||
add_filter( 'block_categories_all', 'generateblocks_do_category' );
|
||||
/**
|
||||
* Add GeneratePress category to Gutenberg.
|
||||
*
|
||||
* @param array $categories Existing categories.
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_do_category( $categories ) {
|
||||
array_unshift(
|
||||
$categories,
|
||||
[
|
||||
'slug' => 'generateblocks',
|
||||
'title' => __( 'GenerateBlocks', 'generateblocks' ),
|
||||
]
|
||||
);
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
add_action( 'wp_enqueue_scripts', 'generateblocks_do_google_fonts' );
|
||||
add_action( 'enqueue_block_editor_assets', 'generateblocks_do_google_fonts' );
|
||||
/**
|
||||
* Do Google Fonts.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function generateblocks_do_google_fonts() {
|
||||
if ( generateblocks_get_option( 'disable_google_fonts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fonts_url = generateblocks_get_google_fonts_uri();
|
||||
|
||||
if ( $fonts_url ) {
|
||||
wp_enqueue_style( 'generateblocks-google-fonts', $fonts_url, array(), null, 'all' ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
}
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_css_print_method', 'generateblocks_set_css_print_method' );
|
||||
/**
|
||||
* Set our CSS print method.
|
||||
*
|
||||
* @param string $method Existing method.
|
||||
*/
|
||||
function generateblocks_set_css_print_method( $method ) {
|
||||
$method = generateblocks_get_option( 'css_print_method' );
|
||||
|
||||
if ( is_single() ) {
|
||||
$method = 'inline';
|
||||
}
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
add_filter( 'excerpt_allowed_blocks', 'generateblocks_set_excerpt_allowed_blocks' );
|
||||
/**
|
||||
* Add blocks that can be displayed in post excerpts.
|
||||
*
|
||||
* @param array $allowed Existing allowed blocks.
|
||||
* @since 1.0
|
||||
*/
|
||||
function generateblocks_set_excerpt_allowed_blocks( $allowed ) {
|
||||
$allowed[] = 'generateblocks/headline';
|
||||
$allowed[] = 'generateblocks/container';
|
||||
$allowed[] = 'generateblocks/text';
|
||||
$allowed[] = 'generateblocks/element';
|
||||
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
add_filter( 'excerpt_allowed_wrapper_blocks', 'generateblocks_set_excerpt_allowed_wrapper_blocks' );
|
||||
/**
|
||||
* Allows excerpts to be generated from the `innerBlocks` of these wrappers.
|
||||
*
|
||||
* @param array $allowed Existing allowed wrapper blocks.
|
||||
* @since 1.5.0
|
||||
*/
|
||||
function generateblocks_set_excerpt_allowed_wrapper_blocks( $allowed ) {
|
||||
$allowed[] = 'generateblocks/container';
|
||||
$allowed[] = 'generateblocks/element';
|
||||
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_before_container_close', 'generateblocks_do_shape_divider', 10, 2 );
|
||||
/**
|
||||
* Add shape divider to Container.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @param string $output The current block output.
|
||||
* @param array $attributes The current block attributes.
|
||||
*/
|
||||
function generateblocks_do_shape_divider( $output, $attributes ) {
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['container']
|
||||
);
|
||||
|
||||
if ( ! empty( $settings['shapeDividers'] ) ) {
|
||||
$shapes = generateblocks_get_svg_shapes();
|
||||
$shape_values = array();
|
||||
|
||||
foreach ( $shapes as $group => $data ) {
|
||||
if ( ! empty( $data['svgs'] ) && is_array( $data['svgs'] ) ) {
|
||||
foreach ( $data['svgs'] as $key => $shape ) {
|
||||
$shape_values[ $key ] = $shape['icon'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '<div class="gb-shapes">';
|
||||
|
||||
foreach ( (array) $settings['shapeDividers'] as $index => $option ) {
|
||||
if ( ! empty( $option['shape'] ) ) {
|
||||
if ( isset( $shape_values[ $option['shape'] ] ) ) {
|
||||
$shapeNumber = $index + 1;
|
||||
|
||||
$output .= sprintf(
|
||||
'<div class="gb-shape gb-shape-' . $shapeNumber . '">%s</div>',
|
||||
$shape_values[ $option['shape'] ]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_do_content', 'generateblocks_do_widget_styling' );
|
||||
/**
|
||||
* Process all widget content for potential styling.
|
||||
*
|
||||
* @since 1.3.4
|
||||
* @param string $content The existing content to process.
|
||||
*/
|
||||
function generateblocks_do_widget_styling( $content ) {
|
||||
$widget_blocks = get_option( 'widget_block' );
|
||||
|
||||
foreach ( (array) $widget_blocks as $block ) {
|
||||
if ( isset( $block['content'] ) ) {
|
||||
$content .= $block['content'];
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_attr_container', 'generateblocks_set_inline_background_style', 10, 2 );
|
||||
/**
|
||||
* Add our background image attribute to the Container.
|
||||
*
|
||||
* @since 1.5.0
|
||||
* @param array $attributes Existing attributes.
|
||||
* @param array $settings Block settings.
|
||||
*/
|
||||
function generateblocks_set_inline_background_style( $attributes, $settings ) {
|
||||
if ( generateblocks_has_background_image( $settings ) && $settings['bgImageInline'] ) {
|
||||
$url = generateblocks_get_background_image_url( $settings );
|
||||
|
||||
if ( $url ) {
|
||||
$attribute_name = 'background-image';
|
||||
|
||||
if ( 'element' !== $settings['bgOptions']['selector'] ) {
|
||||
$attribute_name = '--' . $attribute_name;
|
||||
}
|
||||
|
||||
$attributes['style'] = $attribute_name . ': url(' . esc_url( $url ) . ');';
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_block_css_selector', 'generateblocks_set_block_css_selectors', 10, 3 );
|
||||
/**
|
||||
* Change our block selectors if needed.
|
||||
*
|
||||
* @param string $selector Existing selector.
|
||||
* @param string $name The block name.
|
||||
* @param array $attributes The block attributes.
|
||||
*/
|
||||
function generateblocks_set_block_css_selectors( $selector, $name, $attributes ) {
|
||||
$blockVersion = ! empty( $attributes['blockVersion'] ) ? $attributes['blockVersion'] : 1;
|
||||
$defaults = generateblocks_get_block_defaults();
|
||||
|
||||
if ( 'button' === $name ) {
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['button']
|
||||
);
|
||||
|
||||
if ( $blockVersion < 3 ) {
|
||||
// Old versions of the this block used this backwards logic
|
||||
// to determine whether to remove the "a" to the selector.
|
||||
$clean_selector = $selector;
|
||||
$selector = 'a' . $selector;
|
||||
|
||||
if ( isset( $attributes['hasUrl'] ) && ! $attributes['hasUrl'] ) {
|
||||
$selector = $clean_selector;
|
||||
}
|
||||
} else {
|
||||
$is_link = (
|
||||
! empty( $settings['hasUrl'] ) ||
|
||||
! empty( $settings['dynamicLinkType'] )
|
||||
) && 'link' === $settings['buttonType'];
|
||||
|
||||
if ( $is_link ) {
|
||||
$selector = 'a' . $selector;
|
||||
}
|
||||
|
||||
if ( 'button' === $settings['buttonType'] ) {
|
||||
$selector = 'button' . $selector;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $settings['hasButtonContainer'] || $blockVersion < 3 ) {
|
||||
$selector = '.gb-button-wrapper ' . $selector;
|
||||
} elseif ( isset( $settings['isPagination'] ) && $settings['isPagination'] ) {
|
||||
$selector = '.gb-query-loop-pagination ' . $selector;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'headline' === $name ) {
|
||||
$settings = wp_parse_args(
|
||||
$attributes,
|
||||
$defaults['headline']
|
||||
);
|
||||
|
||||
if ( apply_filters( 'generateblocks_headline_selector_tagname', true, $attributes ) ) {
|
||||
$selector = $settings['element'] . $selector;
|
||||
}
|
||||
}
|
||||
|
||||
return $selector;
|
||||
}
|
||||
|
||||
add_action( 'init', 'generateblocks_register_user_meta' );
|
||||
/**
|
||||
* Register GenerateBlocks custom user meta fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function generateblocks_register_user_meta() {
|
||||
register_meta(
|
||||
'user',
|
||||
GenerateBlocks_Rest::ONBOARDING_META_KEY,
|
||||
array(
|
||||
'type' => 'object',
|
||||
'single' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'insert_inner_container' => array( 'type' => 'boolean' ),
|
||||
),
|
||||
'additionalProperties' => array(
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_filter( 'block_editor_settings_all', 'generateblocks_do_block_css_reset', 15 );
|
||||
/**
|
||||
* This resets the `max-width`, `margin-left`, and `margin-right` properties for our blocks in the editor.
|
||||
* We have to do this as most themes use `.wp-block` to set a `max-width` and auto margins.
|
||||
*
|
||||
* We used to do this directly in the block CSS if those block attributes didn't exist, but this allows us
|
||||
* to overwrite the reset in the `block_editor_settings_all` filter with a later priority.
|
||||
*
|
||||
* @param array $editor_settings The existing editor settings.
|
||||
*/
|
||||
function generateblocks_do_block_css_reset( $editor_settings ) {
|
||||
$css = '.gb-container, .gb-headline, .gb-button {max-width:unset;margin-left:0;margin-right:0;}';
|
||||
$editor_settings['styles'][] = [ 'css' => $css ];
|
||||
|
||||
$blocks_to_reset = [
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-text:where(:not(h1, h2, h3, h4, h5, h6, p))',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-element',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-shape',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-media',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-query',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-query-no-results',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-query-page-numbers',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-looper',
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-loop-item',
|
||||
];
|
||||
|
||||
$heading_blocks_to_reset = [
|
||||
'.editor-styles-wrapper .wp-block-generateblocks-text:where(h1, h2, h3, h4, h5, h6, p)',
|
||||
];
|
||||
|
||||
$css = implode( ',', $blocks_to_reset ) . '{max-width:unset;margin:0;}';
|
||||
$css .= implode( ',', $heading_blocks_to_reset ) . '{max-width:unset;margin-left:0;margin-right:0;}';
|
||||
$editor_settings['styles'][] = [ 'css' => $css ];
|
||||
|
||||
return $editor_settings;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_css_output', 'generateblocks_add_general_css' );
|
||||
/**
|
||||
* Add general CSS that doesn't apply to our own blocks.
|
||||
*
|
||||
* @param string $css Existing CSS.
|
||||
*/
|
||||
function generateblocks_add_general_css( $css ) {
|
||||
$container_width = generateblocks_get_global_container_width();
|
||||
|
||||
if ( $container_width ) {
|
||||
$css .= ':root{--gb-container-width:' . $container_width . ';}';
|
||||
}
|
||||
|
||||
$css .= '.gb-container .wp-block-image img{vertical-align:middle;}';
|
||||
$css .= '.gb-grid-wrapper .wp-block-image{margin-bottom:0;}';
|
||||
$css .= '.gb-highlight{background:none;}';
|
||||
$css .= '.gb-shape{line-height:0;}';
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
add_filter( 'block_editor_settings_all', 'generateblocks_do_block_editor_styles', 15 );
|
||||
/**
|
||||
* Add our block editor styles.
|
||||
*
|
||||
* @param array $editor_settings The existing editor settings.
|
||||
*/
|
||||
function generateblocks_do_block_editor_styles( $editor_settings ) {
|
||||
$container_width = generateblocks_get_global_container_width();
|
||||
|
||||
$editor_settings['styles'][] = array(
|
||||
'css' => ':root{--gb-container-width:' . $container_width . ';}',
|
||||
);
|
||||
|
||||
$editor_settings['styles'][] = array(
|
||||
'css' => '.gb-shape{line-height:0;}',
|
||||
);
|
||||
|
||||
return $editor_settings;
|
||||
}
|
||||
|
||||
add_action( 'enqueue_block_editor_assets', 'generateblocks_set_editor_permissions', 0 );
|
||||
/**
|
||||
* Output permissions for use in the editor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function generateblocks_set_editor_permissions() {
|
||||
$permissions = apply_filters(
|
||||
'generateblocks_permissions',
|
||||
[
|
||||
'isAdminUser' => current_user_can( 'manage_options' ),
|
||||
'canEditPosts' => current_user_can( 'edit_posts' ),
|
||||
'isGbProActive' => is_plugin_active( 'generateblocks-pro/plugin.php' ),
|
||||
'isGpPremiumActive' => is_plugin_active( 'gp-premium/gp-premium.php' ),
|
||||
]
|
||||
);
|
||||
|
||||
$permission_object = wp_json_encode( $permissions );
|
||||
wp_register_script( 'generateblocks-editor-permissions', '', [], '1.0', false );
|
||||
wp_enqueue_script( 'generateblocks-editor-permissions' );
|
||||
$script = sprintf(
|
||||
'const gbPermissions = %s;
|
||||
Object.freeze( gbPermissions );',
|
||||
$permission_object
|
||||
);
|
||||
wp_add_inline_script( 'generateblocks-editor-permissions', $script );
|
||||
}
|
||||
|
||||
add_filter( 'render_block', 'generateblocks_do_html_attributes_escaping', 20, 2 );
|
||||
/**
|
||||
* Filter the rendered block content and escape HTML attributes.
|
||||
*
|
||||
* @param string $content The block content about to be appended to the post content.
|
||||
* @param array $block The full block, including name and attributes.
|
||||
* @return string
|
||||
*/
|
||||
function generateblocks_do_html_attributes_escaping( $content, $block ) {
|
||||
$html_attributes = $block['attrs']['htmlAttributes'] ?? [];
|
||||
$link_attributes = $block['attrs']['linkHtmlAttributes'] ?? [];
|
||||
|
||||
if ( empty( $html_attributes ) && empty( $link_attributes ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$v1_block_names = generateblocks_get_v1_block_names();
|
||||
$block_name = $block['blockName'] ?? '';
|
||||
|
||||
// Only do this for our non-v1 blocks.
|
||||
if (
|
||||
! generateblocks_str_starts_with( $block_name, 'generateblocks' ) ||
|
||||
in_array( $block_name, $v1_block_names, true )
|
||||
) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content = generateblocks_with_escaped_attributes(
|
||||
$content,
|
||||
[
|
||||
'block_html_attrs' => $html_attributes,
|
||||
'link_html_attrs' => $link_attributes,
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
add_filter( 'generateblocks_allowed_option_keys_rest_api', 'generateblocks_allow_additional_option_keys_rest_api' );
|
||||
/**
|
||||
* Allow additional option keys to be accessible via the REST API.
|
||||
*
|
||||
* @param array $allowed_keys Existing allowed keys.
|
||||
*/
|
||||
function generateblocks_allow_additional_option_keys_rest_api( $allowed_keys ) {
|
||||
if ( ! is_array( $allowed_keys ) ) {
|
||||
$allowed_keys = [];
|
||||
}
|
||||
|
||||
$acf_option_keys = generateblocks_get_acf_option_field_keys();
|
||||
$allowed_keys = array_merge( $allowed_keys, $acf_option_keys );
|
||||
|
||||
return $allowed_keys;
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
/**
|
||||
* The Libraries class file.
|
||||
*
|
||||
* @package GenerateBlocks\Pattern_Library
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling with libraries.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
class GenerateBlocks_Libraries extends GenerateBlocks_Singleton {
|
||||
/**
|
||||
* Initialize all hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'query_vars', [ $this, 'add_query_vars' ] );
|
||||
add_action( 'init', [ $this, 'rewrite_endpoints' ] );
|
||||
add_action( 'template_include', [ $this, 'template_viewer' ] );
|
||||
add_filter( 'show_admin_bar', [ $this, 'hide_admin_bar' ] );
|
||||
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue block editor assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_block_editor_assets() {
|
||||
$assets = generateblocks_get_enqueue_assets( 'pattern-library' );
|
||||
|
||||
wp_enqueue_script(
|
||||
'generateblocks-pattern-library',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/pattern-library.js',
|
||||
$assets['dependencies'],
|
||||
$assets['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations(
|
||||
'generateblocks-pattern-library',
|
||||
'generateblocks'
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'generateblocks-pattern-library',
|
||||
'generateBlocksPatternLibrary',
|
||||
array(
|
||||
'patternPreviewUrl' => site_url() . '?gb-template-viewer=1',
|
||||
'defaultOpenLibrary' => apply_filters(
|
||||
'generateblocks_default_open_pattern_library',
|
||||
'gb_default_pro_library'
|
||||
),
|
||||
'defaultOpenCategory' => apply_filters(
|
||||
'generateblocks_default_open_pattern_category',
|
||||
''
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'generateblocks-pattern-library',
|
||||
GENERATEBLOCKS_DIR_URL . 'dist/pattern-library.css',
|
||||
array( 'wp-components' ),
|
||||
GENERATEBLOCKS_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default id.
|
||||
*
|
||||
* @var string The default library id.
|
||||
*/
|
||||
protected $default_library_id = 'gb_default_free_library';
|
||||
|
||||
/**
|
||||
* Returns all the registered libraries.
|
||||
*
|
||||
* @param bool $enabled_only Filter disabled libraries.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_all( bool $enabled_only = true ): array {
|
||||
// Get saved library data.
|
||||
$saved_libraries = get_option( 'generateblocks_pattern_libraries', [] );
|
||||
|
||||
if ( ! is_array( $saved_libraries ) ) {
|
||||
$saved_libraries = [];
|
||||
}
|
||||
|
||||
// Create library instances for any remote sites that provide the complete set of data.
|
||||
$remote_libraries = array_map(
|
||||
[ $this, 'create' ],
|
||||
array_filter(
|
||||
$saved_libraries,
|
||||
function( $saved_library ) {
|
||||
return is_array( $saved_library ) &&
|
||||
isset( $saved_library['isLocal'] ) &&
|
||||
! rest_sanitize_boolean( $saved_library['isLocal'] ) &&
|
||||
! empty( $saved_library['id'] ) &&
|
||||
is_scalar( $saved_library['id'] ) &&
|
||||
! empty( $saved_library['domain'] ) &&
|
||||
is_string( $saved_library['domain'] );
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Allow other libraries to be added.
|
||||
$libraries = apply_filters(
|
||||
'generateblocks_pattern_libraries',
|
||||
$remote_libraries
|
||||
);
|
||||
|
||||
if ( ! is_array( $libraries ) ) {
|
||||
$libraries = [];
|
||||
}
|
||||
|
||||
$libraries = array_filter(
|
||||
$libraries,
|
||||
function( $library ) {
|
||||
return $this->is_valid_library( $library );
|
||||
}
|
||||
);
|
||||
|
||||
// Add our default library at the start of the list.
|
||||
if ( ! self::exists( $libraries, $this->default_library_id ) ) {
|
||||
$default_library = self::get_default();
|
||||
$libraries = array_merge( array( $default_library ), $libraries );
|
||||
}
|
||||
|
||||
// Loop our libraries and set their status based on their saved value.
|
||||
foreach ( $libraries as $key => $library ) {
|
||||
$saved_data = array_filter(
|
||||
$saved_libraries,
|
||||
function( $saved_library ) use ( $library ) {
|
||||
if ( ! is_array( $saved_library ) || ! isset( $saved_library['id'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $saved_library['id'] === $library->id;
|
||||
}
|
||||
);
|
||||
|
||||
$saved_data = array_values( $saved_data );
|
||||
|
||||
if ( isset( $saved_data[0]['isEnabled'] ) ) {
|
||||
$library->setStatus( $this->normalize_boolean_value( $saved_data[0]['isEnabled'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$libraries,
|
||||
function( GenerateBlocks_Library_DTO $library ) use ( $enabled_only ) {
|
||||
if ( $enabled_only ) {
|
||||
return $library->is_enabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single library by id.
|
||||
*
|
||||
* @param string $id The id.
|
||||
*
|
||||
* @return GenerateBlocks_Library_DTO|null
|
||||
*/
|
||||
public function get_one( string $id ): ?GenerateBlocks_Library_DTO {
|
||||
$libraries = self::get_all( false );
|
||||
|
||||
return array_reduce(
|
||||
$libraries,
|
||||
function( $result, GenerateBlocks_Library_DTO $library ) use ( $id ) {
|
||||
if ( $id === $library->id ) {
|
||||
return $library;
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a library from array.
|
||||
*
|
||||
* @param array $data The library in array format.
|
||||
*
|
||||
* @return GenerateBlocks_Library_DTO
|
||||
*/
|
||||
public function create( array $data ): GenerateBlocks_Library_DTO {
|
||||
$data = wp_parse_args(
|
||||
$data,
|
||||
array(
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'domain' => '',
|
||||
'publicKey' => '',
|
||||
'isEnabled' => false,
|
||||
'isDefault' => false,
|
||||
'isLocal' => false,
|
||||
)
|
||||
);
|
||||
|
||||
return ( new GenerateBlocks_Library_DTO() )
|
||||
->set( 'id', $this->normalize_string_value( $data['id'] ) )
|
||||
->set( 'name', $this->normalize_string_value( $data['name'] ) )
|
||||
->set( 'domain', $this->normalize_string_value( $data['domain'] ) )
|
||||
->set( 'public_key', $this->normalize_string_value( $data['publicKey'] ) )
|
||||
->set( 'is_enabled', $this->normalize_boolean_value( $data['isEnabled'] ) )
|
||||
->set( 'is_default', $this->normalize_boolean_value( $data['isDefault'] ) )
|
||||
->set( 'is_local', $this->normalize_boolean_value( $data['isLocal'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize values that are expected to be strings.
|
||||
*
|
||||
* @param mixed $value The value to normalize.
|
||||
* @return string
|
||||
*/
|
||||
private function normalize_string_value( $value ): string {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize values that are expected to be booleans.
|
||||
*
|
||||
* @param mixed $value The value to normalize.
|
||||
* @return bool
|
||||
*/
|
||||
private function normalize_boolean_value( $value ): bool {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rest_sanitize_boolean( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a library has values safe to access through the DTO.
|
||||
*
|
||||
* @param mixed $library The library object to validate.
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_library( $library ): bool {
|
||||
if ( ! $library instanceof GenerateBlocks_Library_DTO ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $library->serialize();
|
||||
|
||||
if ( empty( $data['id'] ) || ! is_scalar( $data['id'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( array( 'name', 'domain', 'publicKey' ) as $key ) {
|
||||
if ( isset( $data[ $key ] ) && ! is_scalar( $data[ $key ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( array( 'isEnabled', 'isDefault', 'isLocal' ) as $key ) {
|
||||
if ( isset( $data[ $key ] ) && ! is_scalar( $data[ $key ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default library.
|
||||
*
|
||||
* @return GenerateBlocks_Library_DTO
|
||||
*/
|
||||
protected function get_default(): GenerateBlocks_Library_DTO {
|
||||
$use_legacy_pattern_library = generateblocks_use_v1_blocks();
|
||||
$domain = 'https://patterns.generatepress.com';
|
||||
$public_key = 'betRRDVi8W0Td2eKAS52oVkG4HxqEj9r';
|
||||
|
||||
if (
|
||||
$use_legacy_pattern_library ||
|
||||
apply_filters( 'generateblocks_force_v1_pattern_library', false )
|
||||
) {
|
||||
$domain = 'https://patterns.generateblocks.com';
|
||||
$public_key = 'GxroZpidKoLZ2ofWNJdXtanAK9ZozWKo';
|
||||
}
|
||||
|
||||
return ( new GenerateBlocks_Library_DTO() )
|
||||
->set( 'id', $this->default_library_id )
|
||||
->set( 'name', __( 'Free', 'generateblocks' ) )
|
||||
->set( 'domain', $domain )
|
||||
->set( 'public_key', $public_key )
|
||||
->set( 'is_enabled', true )
|
||||
->set( 'is_default', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if exists the library.
|
||||
*
|
||||
* @param array $libraries Array of libraries.
|
||||
* @param string $id The library id.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists( array $libraries, string $id ): bool {
|
||||
return array_reduce(
|
||||
$libraries,
|
||||
function( $result, GenerateBlocks_Library_DTO $library ) use ( $id ) {
|
||||
if ( $id === $library->id ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our cached library data by collection.
|
||||
*
|
||||
* @param string $cache_key The key to look up.
|
||||
* @param array $query_args Args to filter the results with.
|
||||
* @param string $collection The collection to check.
|
||||
*/
|
||||
public static function get_cached_data( $cache_key = '', $query_args = [], $collection = '' ) {
|
||||
if ( ! $cache_key ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( 'patterns' !== $collection ) {
|
||||
return get_transient( $cache_key );
|
||||
}
|
||||
|
||||
$cached_data = [];
|
||||
$has_cache = false;
|
||||
$index = 0;
|
||||
|
||||
while ( true ) {
|
||||
$option_key = $cache_key . '_' . $index;
|
||||
$chunk = get_transient( $option_key );
|
||||
|
||||
if ( false === $chunk ) {
|
||||
// No more chunks found, exit the loop.
|
||||
break;
|
||||
}
|
||||
|
||||
// Merge the chunk into the cached data.
|
||||
$cached_data += $chunk;
|
||||
$has_cache = true;
|
||||
|
||||
// Increment the index for the next iteration.
|
||||
$index++;
|
||||
}
|
||||
|
||||
// If we have no cache, return false.
|
||||
// This allows empty arrays to be cached.
|
||||
if ( ! $has_cache ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( $query_args['categoryId'] ) ) {
|
||||
$cached_data = array_filter(
|
||||
$cached_data,
|
||||
function( $data ) use ( $query_args ) {
|
||||
if ( ! isset( $data['categories'] ) || ! is_array( $data['categories'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array( $query_args['categoryId'], $data['categories'] );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $query_args['search'] ) ) {
|
||||
$cached_data = array_filter(
|
||||
$cached_data,
|
||||
function( $data ) use ( $query_args ) {
|
||||
if ( ! is_array( $data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( is_string( $value ) && stripos( $value, $query_args['search'] ) !== false ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$cached_data = array_values( $cached_data );
|
||||
return $cached_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the collection cache expiry.
|
||||
*/
|
||||
public static function get_cache_expiry() {
|
||||
return 86400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set our cached data. This function will split our patterns into chunks.
|
||||
*
|
||||
* @param array $data The data to cache.
|
||||
* @param string $cache_key The key to set.
|
||||
* @param string $collection The collection to check.
|
||||
*/
|
||||
public static function set_cached_data( $data = [], $cache_key = '', $collection = '' ) {
|
||||
if ( ! $cache_key ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_array( $data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expiration = self::get_cache_expiry();
|
||||
|
||||
if ( 'patterns' === $collection ) {
|
||||
self::delete_cached_data( $cache_key, $collection );
|
||||
|
||||
if ( ! empty( $data ) ) {
|
||||
$chunks = array_chunk( $data, 20, true );
|
||||
|
||||
foreach ( $chunks as $index => $chunk ) {
|
||||
$option_key = $cache_key . '_' . $index;
|
||||
set_transient( $option_key, $chunk, $expiration );
|
||||
}
|
||||
} else {
|
||||
set_transient( $cache_key . '_0', $data, $expiration );
|
||||
}
|
||||
} else {
|
||||
set_transient( $cache_key, $data, $expiration );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cached library data by collection.
|
||||
*
|
||||
* @param string $cache_key The key to delete.
|
||||
* @param string $collection The collection to delete.
|
||||
*/
|
||||
public static function delete_cached_data( $cache_key = '', $collection = '' ) {
|
||||
if ( ! $cache_key ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'patterns' !== $collection ) {
|
||||
delete_transient( $cache_key );
|
||||
return;
|
||||
}
|
||||
|
||||
$index = 0;
|
||||
|
||||
while ( false !== get_transient( $cache_key . '_' . $index ) ) {
|
||||
delete_transient( $cache_key . '_' . $index );
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds GB custom query variables.
|
||||
*
|
||||
* @param array $vars The variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_query_vars( array $vars ): array {
|
||||
$vars[] = 'gb-template-viewer';
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds GB custom rewrite endpoints.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewrite_endpoints(): void {
|
||||
add_rewrite_endpoint( 'gb-template-viewer', EP_ROOT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the template viewer template.
|
||||
*
|
||||
* @param string $template The current template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function template_viewer( string $template ): string {
|
||||
if ( false !== get_query_var( 'gb-template-viewer', false ) ) {
|
||||
return GENERATEBLOCKS_DIR . 'includes/pattern-library/templates/gb-template-viewer.php';
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the admin bar if we are on the template viewer.
|
||||
*
|
||||
* @param bool $show_admin_bar Whether to show the admin bar.
|
||||
* @return bool
|
||||
*/
|
||||
public function hide_admin_bar( $show_admin_bar ) {
|
||||
if ( ! isset( $GLOBALS['wp_query'] ) ) {
|
||||
return $show_admin_bar;
|
||||
}
|
||||
|
||||
if ( false !== get_query_var( 'gb-template-viewer', false ) ) {
|
||||
$show_admin_bar = false;
|
||||
}
|
||||
|
||||
return $show_admin_bar;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Libraries::get_instance()->init();
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* The Libraries class file.
|
||||
*
|
||||
* @package GenerateBlocks\Pattern_Library
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Library data transfer object.
|
||||
*
|
||||
* @property string id The library id.
|
||||
* @property string name The library name.
|
||||
* @property string domain The library domain.
|
||||
* @property string public_key The library public key.
|
||||
* @property bool is_default The library is default.
|
||||
* @property bool is_local The library is local.
|
||||
* @property bool is_enabled The library is enabled.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class GenerateBlocks_Library_DTO extends GenerateBlocks_DTO {
|
||||
/**
|
||||
* The data.
|
||||
*
|
||||
* @var array The library data.
|
||||
*/
|
||||
protected $data = array(
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'domain' => '',
|
||||
'public_key' => '',
|
||||
'is_enabled' => false,
|
||||
'is_default' => false,
|
||||
'is_local' => false,
|
||||
);
|
||||
|
||||
/**
|
||||
* Set the status for a library.
|
||||
*
|
||||
* @param boolean $newStatus The status to set.
|
||||
*/
|
||||
public function setStatus( $newStatus ) {
|
||||
$this->data['is_enabled'] = $newStatus;
|
||||
}
|
||||
}
|
||||
+508
@@ -0,0 +1,508 @@
|
||||
<?php
|
||||
/**
|
||||
* The Pattern library class file.
|
||||
*
|
||||
* @package GenerateBlocks\Pattern_Library
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Main class for the Pattern Library Rest functions.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class GenerateBlocks_Pattern_Library_Rest extends GenerateBlocks_Singleton {
|
||||
|
||||
/**
|
||||
* Initialize all hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the REST routes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
$namespace = 'generateblocks/v1';
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/libraries',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'list_libraries' ),
|
||||
'permission_callback' => function() {
|
||||
return apply_filters(
|
||||
'generateblocks_can_view_pattern_library',
|
||||
$this->edit_posts_permission()
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/libraries/save',
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'save_libraries' ),
|
||||
'permission_callback' => array( $this, 'manage_options_permission' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/categories',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'list_categories' ),
|
||||
'permission_callback' => function() {
|
||||
return apply_filters(
|
||||
'generateblocks_can_view_pattern_library',
|
||||
$this->edit_posts_permission()
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/patterns',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'list_patterns' ),
|
||||
'permission_callback' => function() {
|
||||
return apply_filters(
|
||||
'generateblocks_can_view_pattern_library',
|
||||
$this->edit_posts_permission()
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/get-cache-data',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_cache_data' ),
|
||||
'permission_callback' => function() {
|
||||
return apply_filters(
|
||||
'generateblocks_can_view_pattern_library',
|
||||
$this->edit_posts_permission()
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/pattern-library/clear-cache',
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'clear_cache' ),
|
||||
'permission_callback' => array( $this, 'edit_posts_permission' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage options permission callback.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function manage_options_permission(): bool {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage options permission callback.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function edit_posts_permission(): bool {
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of registered libraries.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function list_libraries( WP_REST_Request $request ): WP_REST_Response {
|
||||
$is_enabled = $request->get_param( 'is_enabled' );
|
||||
$libraries = GenerateBlocks_Libraries::get_instance();
|
||||
$data = $libraries->get_all( rest_sanitize_boolean( $is_enabled ) );
|
||||
$data = array_values( $data ); // Fix indexes.
|
||||
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'error' => false,
|
||||
'data' => $data,
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the list of libraries.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function save_libraries( WP_REST_Request $request ): WP_REST_Response {
|
||||
$data = $request->get_param( 'data' );
|
||||
|
||||
if ( ! is_array( $data ) ) {
|
||||
return $this->error( 400, __( 'Invalid library data.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
$libraries = array_values(
|
||||
array_filter(
|
||||
array_map(
|
||||
[ $this, 'sanitize_library_data' ],
|
||||
$data
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
update_option( 'generateblocks_pattern_libraries', $libraries );
|
||||
return $this->success( $libraries );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of categories.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function list_categories( WP_REST_Request $request ): WP_REST_Response {
|
||||
$library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
|
||||
|
||||
if ( ! $library_id ) {
|
||||
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
||||
}
|
||||
|
||||
$libraries = GenerateBlocks_Libraries::get_instance();
|
||||
$library = $libraries->get_one( $library_id );
|
||||
|
||||
if ( ! is_null( $library ) ) {
|
||||
return self::remote_fetch( $library, 'categories' );
|
||||
}
|
||||
|
||||
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of patterns.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function list_patterns( WP_REST_Request $request ): WP_REST_Response {
|
||||
$library_id = $this->sanitize_request_string( $request->get_param( 'libraryId' ) );
|
||||
$search = $this->sanitize_request_string( $request->get_param( 'search' ) );
|
||||
$category_id = $this->sanitize_request_string( $request->get_param( 'categoryId' ) );
|
||||
|
||||
if ( ! $library_id ) {
|
||||
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
||||
}
|
||||
|
||||
$libraries = GenerateBlocks_Libraries::get_instance();
|
||||
$library = $libraries->get_one( $library_id );
|
||||
|
||||
if ( ! is_null( $library ) ) {
|
||||
return self::remote_fetch(
|
||||
$library,
|
||||
'patterns',
|
||||
array(
|
||||
'search' => $search,
|
||||
'categoryId' => $category_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->error( 404, "Library of id \"$library_id\" was not found." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch pattern library remotely.
|
||||
*
|
||||
* @param GenerateBlocks_Library_DTO $library The library to fetch data.
|
||||
* @param string $collection The collection type. Either 'categories' or 'patterns'.
|
||||
* @param array $query_args The extra query arguments.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function remote_fetch(
|
||||
GenerateBlocks_Library_DTO $library,
|
||||
string $collection,
|
||||
array $query_args = array()
|
||||
): WP_REST_Response {
|
||||
if ( ! in_array( $collection, array( 'categories', 'patterns' ), true ) ) {
|
||||
return $this->error( 400, __( 'Invalid library collection.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
$domain = esc_url_raw( $library->domain );
|
||||
|
||||
if ( ! $domain ) {
|
||||
return $this->error( 400, __( 'Invalid library domain.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
$endpoint = trailingslashit( $domain ) . "wp-json/generateblocks-pro/v1/pattern-library/$collection";
|
||||
$url = add_query_arg( $query_args, $endpoint );
|
||||
$cache_key = $library->id . '-' . $collection;
|
||||
$cache = GenerateBlocks_Libraries::get_cached_data( $cache_key, $query_args, $collection );
|
||||
|
||||
if ( false !== $cache ) {
|
||||
return $this->success( $cache );
|
||||
}
|
||||
|
||||
$request = wp_remote_get(
|
||||
$url,
|
||||
array(
|
||||
'headers' => array(
|
||||
'X-GB-Public-Key' => $library->public_key,
|
||||
),
|
||||
'timeout' => 15,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $request ) ) {
|
||||
return $this->error( 500, "Unable to request from $endpoint" );
|
||||
}
|
||||
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $request );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
return $this->error( $response_code ? $response_code : 500, "Unable to request from $endpoint" );
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $request );
|
||||
$body = json_decode( $body, true );
|
||||
|
||||
if ( ! is_array( $body ) ) {
|
||||
return $this->error( 500, __( 'Invalid library response.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
$data = $body['response']['data'] ?? [];
|
||||
|
||||
if ( ! is_array( $data ) ) {
|
||||
return $this->error( 500, __( 'Invalid library response data.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
// Cache our data.
|
||||
GenerateBlocks_Libraries::set_cached_data( $data, $cache_key, $collection );
|
||||
|
||||
return $this->success( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expiry time of a cache.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function get_cache_data( WP_REST_Request $request ): WP_REST_Response {
|
||||
$id = $this->sanitize_request_string( $request->get_param( 'id' ) );
|
||||
|
||||
if ( ! $id ) {
|
||||
return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
|
||||
return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
$expiration_time = get_option( '_transient_timeout_' . $id . '-patterns_0' );
|
||||
|
||||
if ( ! $expiration_time ) {
|
||||
return $this->failed( 'no_cache' );
|
||||
}
|
||||
|
||||
$current_time = time();
|
||||
$cache_made_time = $expiration_time - GenerateBlocks_Libraries::get_cache_expiry();
|
||||
$can_clear_cache = $current_time > ( $cache_made_time + 300 );
|
||||
|
||||
return $this->success(
|
||||
[
|
||||
'expiry_time_raw' => $expiration_time,
|
||||
'expiry_time' => gmdate( 'Y-m-d H:i:s', $expiration_time ),
|
||||
'can_clear' => $can_clear_cache,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches for a specific collection.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function clear_cache( WP_REST_Request $request ): WP_REST_Response {
|
||||
$id = $this->sanitize_request_string( $request->get_param( 'id' ) );
|
||||
|
||||
if ( ! $id ) {
|
||||
return $this->error( 400, __( 'Invalid library id.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
if ( is_null( GenerateBlocks_Libraries::get_instance()->get_one( $id ) ) ) {
|
||||
return $this->error( 404, __( 'Library not found.', 'generateblocks' ) );
|
||||
}
|
||||
|
||||
GenerateBlocks_Libraries::delete_cached_data( $id . '-categories' );
|
||||
GenerateBlocks_Libraries::delete_cached_data( $id . '-patterns', 'patterns' );
|
||||
GenerateBlocks_Libraries::delete_cached_data( $id . '_global-style-data' );
|
||||
|
||||
return $this->success( [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize scalar REST request values.
|
||||
*
|
||||
* @param mixed $value The value to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize_request_string( $value ): string {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sanitize_text_field( (string) $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize scalar REST request URLs.
|
||||
*
|
||||
* @param mixed $value The value to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize_request_url( $value ): string {
|
||||
if ( ! is_string( $value ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return esc_url_raw( $value, array( 'http', 'https' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a saved library row.
|
||||
*
|
||||
* @param mixed $library The library data.
|
||||
* @return array
|
||||
*/
|
||||
private function sanitize_library_data( $library ): array {
|
||||
if ( ! is_array( $library ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$id = $this->sanitize_request_string( $library['id'] ?? '' );
|
||||
|
||||
if ( ! $id ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$is_local = rest_sanitize_boolean( $library['isLocal'] ?? false );
|
||||
$is_default = rest_sanitize_boolean( $library['isDefault'] ?? false );
|
||||
$is_enabled = rest_sanitize_boolean( $library['isEnabled'] ?? false );
|
||||
|
||||
if ( ! $is_local && ! $is_default ) {
|
||||
$domain = $this->sanitize_request_url( $library['domain'] ?? '' );
|
||||
|
||||
if ( ! $domain ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Save all data if this is a remote library.
|
||||
return [
|
||||
'id' => $id,
|
||||
'name' => $this->sanitize_request_string( $library['name'] ?? '' ),
|
||||
'domain' => $domain,
|
||||
'publicKey' => $this->sanitize_request_string( $library['publicKey'] ?? '' ),
|
||||
'isEnabled' => $is_enabled,
|
||||
'isDefault' => $is_default,
|
||||
'isLocal' => $is_local,
|
||||
];
|
||||
}
|
||||
|
||||
// Only save the ID and status for local and default libraries.
|
||||
// The rest of the data will be supplied via the PHP filter.
|
||||
return [
|
||||
'id' => $id,
|
||||
'isEnabled' => $is_enabled,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a success response.
|
||||
*
|
||||
* @param array $data The data.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function success( array $data ): WP_REST_Response {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'success' => true,
|
||||
'response' => array(
|
||||
'data' => $data,
|
||||
),
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a success response.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function failed( string $message ): WP_REST_Response {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'success' => false,
|
||||
'response' => $message,
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a error response.
|
||||
*
|
||||
* @param int $code Error code.
|
||||
* @param string $message Error message.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function error( int $code, string $message ): WP_REST_Response {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'error' => true,
|
||||
'success' => false,
|
||||
'error_code' => $code,
|
||||
'response' => $message,
|
||||
),
|
||||
$code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateBlocks_Pattern_Library_Rest::get_instance()->init();
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* The GB template viewer file.
|
||||
*
|
||||
* @package GenerateBlocks
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
?><!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||
<?php wp_head(); ?>
|
||||
</head>
|
||||
<body style="overflow-y: hidden;">
|
||||
<?php do_action( 'wp_footer' ); // phpcs:ignore -- Need to use core action. ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* The data transfer object class file.
|
||||
*
|
||||
* @package GenerateBlocks\Utils
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The GenerateBlocks Data Transfer Object class.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
class GenerateBlocks_DTO implements JsonSerializable {
|
||||
/**
|
||||
* The data.
|
||||
*
|
||||
* @var array The DTO data.
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Returns the data values if exists.
|
||||
*
|
||||
* @param string $name The called name.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function __get( string $name ): ?string {
|
||||
return $this->data[ $name ] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value for the DTO data.
|
||||
*
|
||||
* @param string $key The name.
|
||||
* @param mixed $value The value.
|
||||
*
|
||||
* @return GenerateBlocks_DTO
|
||||
*/
|
||||
public function set( string $key, $value ): GenerateBlocks_DTO {
|
||||
if ( isset( $this->data[ $key ] ) ) {
|
||||
$this->data[ $key ] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize this class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function serialize(): array {
|
||||
$result = array();
|
||||
|
||||
foreach ( $this->data as $key => $value ) {
|
||||
$k = generateblocks_to_camel_case( $key );
|
||||
$result[ $k ] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize this class.
|
||||
*
|
||||
* @param array $data The data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unserialize( array $data ): void {
|
||||
foreach ( $data as $key => $value ) {
|
||||
$k = generateblocks_to_snake_case( $key );
|
||||
$this->data[ $k ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialize function.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function JsonSerialize(): array {
|
||||
return $this->serialize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* The singleton class file.
|
||||
*
|
||||
* @package GenerateBlocks\Utils
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The GenerateBlocks Singleton class.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*/
|
||||
class GenerateBlocks_Singleton {
|
||||
|
||||
/**
|
||||
* Child class instances.
|
||||
*
|
||||
* @var array<static>
|
||||
*/
|
||||
private static $instances = [];
|
||||
|
||||
/**
|
||||
* The singleton constructor can not be public.
|
||||
*/
|
||||
final protected function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Not allowed to clone a singleton.
|
||||
*/
|
||||
protected function __clone() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Not allowed to un-serialize a singleton.
|
||||
*
|
||||
* @throws Exception Cannot un-serialize a singleton.
|
||||
*/
|
||||
public function __wakeup() {
|
||||
throw new Exception( 'Cannot un-serialize singleton' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function get_instance(): GenerateBlocks_Singleton {
|
||||
$subclass = static::class;
|
||||
|
||||
if ( ! isset( self::$instances[ $subclass ] ) ) {
|
||||
self::$instances[ $subclass ] = new static();
|
||||
}
|
||||
|
||||
return self::$instances[ $subclass ];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user