*/ public function get_keywords() { return [ 'suredonation', 'donation', 'campaign' ]; } /** * Load the campaign block styles on the front end and in the builder canvas. * The block render enqueues the same handle itself; this covers the builder, * where the element markup is rendered before that enqueue can take effect. * * @since 1.2.0 * @return void */ public function enqueue_scripts() { wp_enqueue_style( 'suredonation-campaign-blocks' ); } /** * Render the element by emitting the matching block's server-side output. * * @since 1.2.0 * @return void */ public function render() { $settings = $this->settings; $resolved = Campaign_Page::resolve_campaign_id( [ 'campaignId' => $this->setting_int( $settings, 'campaignId' ) ] ); if ( ! $resolved ) { // Builder-only info box; on the front end this renders nothing, // matching the block's empty output (self-gated on is_frontend). $this->render_element_placeholder( [ 'icon-class' => $this->icon, 'description' => esc_html__( 'Select a campaign, or place this element on a campaign page.', 'suredonation' ), ] ); return; } $attrs = $this->get_block_attrs( $settings ); $attrs['campaignId'] = $resolved; echo '
render_attributes( '_root' ) . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Bricks root attributes are escaped by the builder. // render_block() runs the block's registered render_callback, producing the // exact same (already-escaped) markup as the Gutenberg block. echo render_block( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Block render output is escaped in the block's render callback. [ 'blockName' => $this->block_name(), 'attrs' => $attrs, 'innerBlocks' => [], 'innerHTML' => '', 'innerContent' => [], ] ); echo '
'; } /** * The SureDonation block this element renders (e.g. `suredonation/campaign-stats`). * * @since 1.2.0 * @return string */ abstract protected function block_name(); /** * Map the element settings to the block's attributes (camelCase, matching * the block's Gutenberg attributes). * * @since 1.2.0 * @param array $settings Element settings. * @return array */ abstract protected function get_block_attrs( $settings ); /** * Add the shared "Campaign" selector control (published campaigns). * * @since 1.2.0 * @return void */ protected function add_campaign_control() { $options = Page_Builders::get_campaign_options(); unset( $options[''] ); // Bricks selects use 'placeholder', not an empty option. $this->controls['campaignId'] = [ 'tab' => 'content', 'label' => esc_html__( 'Campaign', 'suredonation' ), 'type' => 'select', 'options' => $options, 'searchable' => true, 'clearable' => true, 'placeholder' => esc_html__( 'Select a campaign', 'suredonation' ), 'description' => esc_html__( 'Leave empty to use the current campaign when placed on a campaign page.', 'suredonation' ), ]; } /** * Read a setting as a string (settings values are mixed). * * @since 1.2.0 * @param array $settings Element settings. * @param string $key Setting key. * @param string $fallback Fallback when empty/unset. * @return string */ protected function setting_string( $settings, $key, $fallback = '' ) { if ( ! isset( $settings[ $key ] ) || '' === $settings[ $key ] ) { return $fallback; } return Helper::get_string_value( $settings[ $key ] ); } /** * Read a setting as a non-negative integer. * * @since 1.2.0 * @param array $settings Element settings. * @param string $key Setting key. * @param int $fallback Fallback when unset. * @return int */ protected function setting_int( $settings, $key, $fallback = 0 ) { return isset( $settings[ $key ] ) ? absint( Helper::get_string_value( $settings[ $key ] ) ) : $fallback; } /** * Read a Bricks checkbox setting as a boolean. Bricks stores `true` when * checked and omits the key when unchecked — and it NEVER seeds a control's * 'default' into settings, so absence always reads as false. * * Contract: this helper is safe only for controls whose Gutenberg block * default is false. A default-true block option MUST be modeled as an * inverted, default-off control (e.g. hideProgressBar → ! setting_bool()), * otherwise an untouched element passes an explicit false to render_block() * and flips the block.json default (which only fills MISSING attributes). * * @since 1.2.0 * @param array $settings Element settings. * @param string $key Setting key. * @return bool */ protected function setting_bool( $settings, $key ) { return isset( $settings[ $key ] ) && false !== $settings[ $key ]; } }