PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 All 211 releases
charitable / includes / admin / campaign-builder / fields / class-campaign-hero.php

class-campaign-hero.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/admin/campaign-builder/fields/class-campaign-hero.php

630 lines 28.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class to add the Campaign Hero block to a campaign in the builder.
4 *
5 * A composite block that renders a hero banner with background image, optional
6 * avatar/logo, campaign title (or override), an inline raised band (progress),
7 * and the mini donation widget. Designed to be the locked structural element
8 * of the Beacon family of templates, but reusable in any template.
9 *
10 * @package Charitable
11 * @author David Bisset
12 * @copyright Copyright (c) 2026, WP Charitable LLC
13 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
14 * @since 1.8.11.1
15 */
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 if ( ! class_exists( 'Charitable_Field_Campaign_Hero' ) ) :
22
23 /**
24 * Charitable_Field_Campaign_Hero class.
25 *
26 * @since 1.8.11.1
27 */
28 class Charitable_Field_Campaign_Hero extends Charitable_Builder_Field {
29
30 /**
31 * Primary class constructor.
32 *
33 * @since 1.8.11.1
34 */
35 public function init() {
36
37 $this->name = esc_html__( 'Campaign Hero', 'charitable' );
38 $this->type = 'campaign-hero';
39 $this->icon = 'fa-star';
40 $this->order = 5;
41 $this->group = 'standard';
42
43 $this->can_be_edited = true;
44 $this->can_be_deleted = true;
45 $this->can_be_duplicated = false;
46 $this->edit_label = esc_html__( 'Edit Campaign Hero', 'charitable' );
47 $this->edit_type = 'campaign-hero';
48 $this->edit_section = 'standard';
49
50 $this->tooltip = '';
51 }
52
53 /**
54 * Field options panel inside the builder.
55 *
56 * @since 1.8.11.1
57 *
58 * @param array $field Field settings.
59 */
60 public function field_options( $field ) {
61 // Intentionally empty; all options surface through settings_display().
62 }
63
64 /**
65 * Expand a bare filename (e.g. "hero.jpg") to a full URL pointing into the
66 * template's assets folder. Full URLs pass through unchanged.
67 *
68 * @since 1.8.11.1
69 *
70 * @param string $image The stored image value (URL or filename).
71 * @param string $template_id The campaign's template slug.
72 * @return string The resolved URL, or empty string if no image.
73 */
74 protected function resolve_template_image( $image, $template_id ) {
75 if ( empty( $image ) ) {
76 return '';
77 }
78 $basename = basename( $image );
79 $template_id = preg_replace( '/[^a-z0-9\-]/', '', strtolower( (string) $template_id ) );
80 if ( $basename === $image && ! empty( $template_id ) ) {
81 return charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $template_id . '/' . $basename;
82 }
83 return $image;
84 }
85
86 /**
87 * Render the field in the builder preview as a styled placeholder.
88 *
89 * @since 1.8.11.1
90 *
91 * @param array $field_data Field data.
92 * @param array $campaign_data Campaign data and settings.
93 * @param integer $field_id Field ID.
94 * @param string $mode 'preview' or 'template'.
95 * @param array $template_data Template data.
96 * @return string
97 */
98 public function render( $field_data = false, $campaign_data = false, $field_id = false, $mode = 'template', $template_data = false ) {
99
100 $html = '';
101
102 if ( 'preview' !== $mode ) {
103 return $html;
104 }
105
106 $template_id = is_array( $campaign_data ) && ! empty( $campaign_data['template_id'] ) ? $campaign_data['template_id'] : '';
107 $bg_raw = ! empty( $field_data['background_image'] ) ? $field_data['background_image'] : '';
108 $avatar_raw = ! empty( $field_data['avatar_image'] ) ? $field_data['avatar_image'] : '';
109 $bg_url = esc_url( $this->resolve_template_image( $bg_raw, $template_id ) );
110 $avatar_url = esc_url( $this->resolve_template_image( $avatar_raw, $template_id ) );
111 $title_text = ! empty( $field_data['title_override'] ) ? esc_html( $field_data['title_override'] ) : esc_html__( 'Campaign Title', 'charitable' );
112 $cta_label = ! empty( $field_data['cta_label'] ) ? esc_html( $field_data['cta_label'] ) : esc_html__( 'Donate Now', 'charitable' );
113
114 // Read theme colors: primary drives the raised band + selected amount; button drives the CTA.
115 $theme_primary = is_array( $campaign_data ) && ! empty( $campaign_data['layout']['advanced']['theme_color_primary'] )
116 ? $campaign_data['layout']['advanced']['theme_color_primary']
117 : ( is_array( $campaign_data ) && ! empty( $campaign_data['color_base_primary'] ) ? $campaign_data['color_base_primary'] : '#1d3a8a' );
118 if ( false === strpos( $theme_primary, '#' ) ) {
119 $theme_primary = '#' . ltrim( $theme_primary, '#' );
120 }
121 $theme_button = is_array( $campaign_data ) && ! empty( $campaign_data['layout']['advanced']['theme_color_button'] )
122 ? $campaign_data['layout']['advanced']['theme_color_button']
123 : ( is_array( $campaign_data ) && ! empty( $campaign_data['color_base_button'] ) ? $campaign_data['color_base_button'] : $theme_primary );
124 if ( false === strpos( $theme_button, '#' ) ) {
125 $theme_button = '#' . ltrim( $theme_button, '#' );
126 }
127 $accent = sanitize_hex_color( $theme_primary );
128 $accent = $accent ? $accent : '#1d3a8a';
129 $button_color = sanitize_hex_color( $theme_button );
130 $button_color = $button_color ? $button_color : $accent;
131 $show_raised = ! isset( $field_data['show_raised'] ) || ! empty( $field_data['show_raised'] );
132 $amounts_csv = ! empty( $field_data['onetime_amounts'] ) ? $field_data['onetime_amounts'] : '50,150,500,1000';
133 $amounts = array_filter( array_map( 'trim', explode( ',', $amounts_csv ) ) );
134 $show_other = ! isset( $field_data['onetime_show_other'] ) || ! empty( $field_data['onetime_show_other'] );
135
136 // Pull the actual goal + donated amount for the raised band. Falls back to 0 for new campaigns.
137 $preview_goal = isset( $campaign_data['settings']['general']['goal'] ) ? floatval( $campaign_data['settings']['general']['goal'] ) : 0;
138 $preview_donated = 0;
139 if ( ! empty( $campaign_data['id'] ) && function_exists( 'charitable_get_campaign' ) ) {
140 $preview_campaign = charitable_get_campaign( intval( $campaign_data['id'] ) );
141 if ( $preview_campaign ) {
142 $preview_donated = (float) $preview_campaign->get_donated_amount();
143 // If the goal wasn't in settings (e.g., reading saved meta directly), fall back to the object's goal.
144 if ( 0.0 === $preview_goal ) {
145 $preview_goal = (float) $preview_campaign->get_goal();
146 }
147 }
148 }
149 $preview_donated_fmt = function_exists( 'charitable_format_money' ) ? charitable_format_money( $preview_donated ) : '$' . number_format( $preview_donated, 2 );
150 $preview_goal_fmt = function_exists( 'charitable_format_money' ) ? charitable_format_money( $preview_goal ) : '$' . number_format( $preview_goal, 2 );
151 $preview_percent = $preview_goal > 0 ? min( 100, ( $preview_donated / $preview_goal ) * 100 ) : 0;
152 $show_raised_resolved = $show_raised && $preview_goal > 0;
153
154 $base_style_props = '--charitable-hero-accent:' . $accent . ';--charitable-hero-button:' . $button_color . ';';
155 $bg_style = $bg_url
156 ? ' style="' . $base_style_props . 'background-image:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.5)),url(\'' . $bg_url . '\');"'
157 : ' style="' . $base_style_props . 'background:#2a2a2a;"';
158
159 ob_start();
160 ?>
161 <div class="charitable-hero-preview"<?php echo $bg_style; // phpcs:ignore ?>>
162 <div class="charitable-hero-preview__overlay">
163 <div class="charitable-hero-preview__identity">
164 <?php if ( $avatar_url ) : ?>
165 <div class="charitable-hero-preview__avatar">
166 <img src="<?php echo esc_url( $avatar_url ); ?>" alt="" />
167 </div>
168 <?php endif; ?>
169 <h1 class="charitable-hero-preview__title<?php echo $avatar_url ? '' : ' charitable-hero-preview__title--no-logo'; ?>"><?php echo esc_html( $title_text ); ?></h1>
170 </div>
171 <div class="charitable-hero-preview__widget">
172 <?php if ( $show_raised_resolved ) : ?>
173 <div class="charitable-hero-preview__raised">
174 <span>
175 <?php
176 printf(
177 /* translators: 1: amount raised, 2: goal amount */
178 esc_html__( '%1$s raised toward our %2$s goal.', 'charitable' ),
179 esc_html( $preview_donated_fmt ),
180 esc_html( $preview_goal_fmt )
181 );
182 ?>
183 </span>
184 <div class="charitable-hero-preview__bar"><span style="width:<?php echo esc_attr( $preview_percent ); ?>%;background:#fff;"></span></div>
185 </div>
186 <?php endif; ?>
187 <div class="charitable-hero-preview__amounts" aria-hidden="true">
188 <?php foreach ( $amounts as $i => $amount ) : ?>
189 <button type="button" tabindex="-1" class="charitable-hero-preview__amount<?php echo 0 === $i ? ' is-selected' : ''; ?>"<?php echo 0 === $i ? ' style="background:' . esc_attr( $accent ) . ';color:#fff;border-color:' . esc_attr( $accent ) . '"' : ''; ?>>$<?php echo esc_html( $amount ); ?></button>
190 <?php endforeach; ?>
191 <?php if ( $show_other ) : ?>
192 <button type="button" tabindex="-1" class="charitable-hero-preview__amount charitable-hero-preview__amount--other"><?php esc_html_e( 'Other', 'charitable' ); ?></button>
193 <?php endif; ?>
194 </div>
195 <button type="button" tabindex="-1" class="charitable-hero-preview__cta" aria-hidden="true"><?php echo esc_html( $cta_label ); ?></button>
196 </div>
197 </div>
198 </div>
199 <?php
200 $html = ob_get_clean();
201
202 return $html;
203 }
204
205 /**
206 * Builder preview wrapper.
207 *
208 * @since 1.8.11.1
209 *
210 * @param array $field_data Field data.
211 * @param array $campaign_data Campaign data and settings.
212 * @param integer $field_id Field ID.
213 * @param string $theme Template data.
214 */
215 public function field_preview( $field_data = false, $campaign_data = false, $field_id = false, $theme = '' ) {
216
217 $html = $this->field_title( $this->name );
218 $html .= $this->field_wrapper( $this->render( $field_data, $campaign_data, $field_id, 'preview' ), $field_data );
219
220 echo $html; // phpcs:ignore
221 }
222
223 /**
224 * The display on the campaign front-end.
225 *
226 * @since 1.8.11.1
227 *
228 * @param string $field The field type.
229 * @param array $field_data Field data.
230 * @param array $campaign_data Campaign data and settings.
231 */
232 public function field_display( $field, $field_data = false, $campaign_data = false ) {
233
234 $campaign_id = isset( $campaign_data['id'] ) ? intval( $campaign_data['id'] ) : 0;
235 if ( ! $campaign_id ) {
236 return;
237 }
238
239 $campaign = charitable_get_campaign( $campaign_id );
240 if ( ! $campaign ) {
241 return;
242 }
243
244 $template_id = ! empty( $campaign_data['template_id'] ) ? $campaign_data['template_id'] : '';
245 $bg_raw = ! empty( $field_data['background_image'] ) ? $field_data['background_image'] : '';
246 $avatar_raw = ! empty( $field_data['avatar_image'] ) ? $field_data['avatar_image'] : '';
247 $bg_url = esc_url( $this->resolve_template_image( $bg_raw, $template_id ) );
248 $avatar_url = esc_url( $this->resolve_template_image( $avatar_raw, $template_id ) );
249 $title_text = ! empty( $field_data['title_override'] )
250 ? $field_data['title_override']
251 : get_the_title( $campaign_id );
252
253 // Accent for the hero block IS the template's primary theme color. The CTA uses a
254 // separate Button color (also from layout advanced settings). Both fall back through
255 // layout → campaign base → Beacon defaults.
256 $theme_primary = '';
257 if ( ! empty( $campaign_data['layout']['advanced']['theme_color_primary'] ) ) {
258 $theme_primary = $campaign_data['layout']['advanced']['theme_color_primary'];
259 } elseif ( ! empty( $campaign_data['color_base_primary'] ) ) {
260 $theme_primary = $campaign_data['color_base_primary'];
261 }
262 if ( $theme_primary && false === strpos( $theme_primary, '#' ) ) {
263 $theme_primary = '#' . ltrim( $theme_primary, '#' );
264 }
265 $theme_button = '';
266 if ( ! empty( $campaign_data['layout']['advanced']['theme_color_button'] ) ) {
267 $theme_button = $campaign_data['layout']['advanced']['theme_color_button'];
268 } elseif ( ! empty( $campaign_data['color_base_button'] ) ) {
269 $theme_button = $campaign_data['color_base_button'];
270 }
271 if ( $theme_button && false === strpos( $theme_button, '#' ) ) {
272 $theme_button = '#' . ltrim( $theme_button, '#' );
273 }
274 $accent = $theme_primary ? sanitize_hex_color( $theme_primary ) : '';
275 $button_color = $theme_button ? sanitize_hex_color( $theme_button ) : '';
276 $show_raised = ! isset( $field_data['show_raised'] ) || ! empty( $field_data['show_raised'] );
277 $enable_recur = ! empty( $field_data['enable_recurring'] );
278
279 // Raised band data.
280 $raised = (float) $campaign->get_donated_amount();
281 $goal = (float) $campaign->get_goal();
282 $raised_fmt = charitable_format_money( $raised );
283 $goal_fmt = $goal > 0 ? charitable_format_money( $goal ) : '';
284 $percent = $goal > 0 ? min( 100, ( $raised / $goal ) * 100 ) : 0;
285
286 // Build the mini-widget args.
287 $widget_args = array(
288 'campaign_id' => $campaign_id,
289 'cta_mode' => 'redirect',
290 'cta_label' => ! empty( $field_data['cta_label'] ) ? $field_data['cta_label'] : __( 'Donate Now', 'charitable' ),
291 'recurring' => $enable_recur ? 'true' : 'false',
292 'accent_color' => $accent,
293 'onetime_amounts' => ! empty( $field_data['onetime_amounts'] ) ? $field_data['onetime_amounts'] : '50,150,500,1000',
294 'onetime_default' => ! empty( $field_data['onetime_default'] ) ? $field_data['onetime_default'] : '',
295 'onetime_show_other' => ( ! isset( $field_data['onetime_show_other'] ) || ! empty( $field_data['onetime_show_other'] ) ) ? 'true' : 'false',
296 'monthly_amounts' => ! empty( $field_data['monthly_amounts'] ) ? $field_data['monthly_amounts'] : '',
297 'monthly_default' => ! empty( $field_data['monthly_default'] ) ? $field_data['monthly_default'] : '',
298 'monthly_show_other' => ! empty( $field_data['monthly_show_other'] ) ? 'true' : 'false',
299 'min_amount' => ! empty( $field_data['min_amount'] ) ? $field_data['min_amount'] : '',
300 'max_amount' => ! empty( $field_data['max_amount'] ) ? $field_data['max_amount'] : '',
301 'widget_align' => 'left',
302 'widget_width' => '100%',
303 );
304
305 $accent_or_default = $accent ? $accent : '#1d3a8a';
306 $button_or_default = $button_color ? $button_color : $accent_or_default;
307
308 $bg_style = $bg_url
309 ? ' style="background-image:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.5)),url(\'' . $bg_url . '\');"'
310 : ' style="background-color:' . esc_attr( $accent_or_default ) . ';"';
311
312 $accent_style = ' style="--charitable-hero-accent:' . esc_attr( $accent_or_default ) . ';--charitable-hero-button:' . esc_attr( $button_or_default ) . ';"';
313
314 $title_extra_class = $avatar_url ? '' : ' charitable-campaign-hero__title--no-logo';
315
316 ob_start();
317 ?>
318 <section class="charitable-campaign-hero"<?php echo $accent_style; // phpcs:ignore ?>>
319 <div class="charitable-campaign-hero__banner"<?php echo $bg_style; // phpcs:ignore ?>></div>
320 <div class="charitable-campaign-hero__overlay">
321 <div class="charitable-campaign-hero__identity">
322 <?php if ( $avatar_url ) : ?>
323 <div class="charitable-campaign-hero__avatar">
324 <img src="<?php echo esc_url( $avatar_url ); ?>" alt="" />
325 </div>
326 <?php endif; ?>
327 <h1 class="charitable-campaign-hero__title<?php echo esc_attr( $title_extra_class ); ?>"><?php echo esc_html( $title_text ); ?></h1>
328 </div>
329 <div class="charitable-campaign-hero__widget-shell">
330 <?php if ( $show_raised && $goal > 0 ) : ?>
331 <div class="charitable-campaign-hero__raised">
332 <div class="charitable-campaign-hero__raised-text">
333 <?php
334 printf(
335 /* translators: 1: amount raised, 2: goal amount */
336 esc_html__( '%1$s raised toward our %2$s goal.', 'charitable' ),
337 esc_html( $raised_fmt ),
338 esc_html( $goal_fmt )
339 );
340 ?>
341 </div>
342 <div class="charitable-campaign-hero__progress">
343 <span style="width:<?php echo esc_attr( $percent ); ?>%;"></span>
344 </div>
345 </div>
346 <?php endif; ?>
347 <div class="charitable-campaign-hero__widget">
348 <?php echo Charitable_Mini_Widget::render( $widget_args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
349 </div>
350 </div>
351 </div>
352 </section>
353 <?php
354 $html = ob_get_clean();
355
356 $html = $this->field_display_wrapper( $html, $field_data );
357
358 echo apply_filters( 'charitable_campaign_builder_' . $this->type . '_field_display', $html, $campaign_data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
359 }
360
361 /**
362 * Settings panel rendered inside the builder's right sidebar.
363 *
364 * @since 1.8.11.1
365 *
366 * @param integer $field_id Field ID.
367 * @param array $campaign_data Campaign data.
368 * @return string
369 */
370 public function settings_display( $field_id = false, $campaign_data = false ) {
371
372 $ff = new Charitable_Builder_Form_Fields();
373 $settings = isset( $campaign_data['fields'][ $field_id ] ) ? $campaign_data['fields'][ $field_id ] : array();
374
375 $has_recurring_addon = class_exists( 'Charitable_Recurring' );
376
377 // Resolve bare filenames so the uploader UI shows the actual asset URL.
378 $template_id = ! empty( $campaign_data['template_id'] ) ? $campaign_data['template_id'] : '';
379 if ( ! empty( $settings['background_image'] ) ) {
380 $settings['background_image'] = $this->resolve_template_image( $settings['background_image'], $template_id );
381 }
382 if ( ! empty( $settings['avatar_image'] ) ) {
383 $settings['avatar_image'] = $this->resolve_template_image( $settings['avatar_image'], $template_id );
384 }
385
386 // Look up the template's original default values for the hero block (used by Reset buttons).
387 $template_default_bg = '';
388 $template_default_avatar = '';
389 if ( $template_id && class_exists( 'Charitable_Campaign_Builder_Templates' ) ) {
390 $template_data = ( new Charitable_Campaign_Builder_Templates() )->get_template_data( $template_id );
391 if ( ! empty( $template_data['layout'] ) && is_array( $template_data['layout'] ) ) {
392 foreach ( $template_data['layout'] as $row ) {
393 if ( empty( $row['columns'] ) || ! is_array( $row['columns'] ) ) {
394 continue;
395 }
396 foreach ( $row['columns'] as $column ) {
397 if ( ! is_array( $column ) ) {
398 continue;
399 }
400 foreach ( $column as $section ) {
401 if ( empty( $section['fields'] ) || ! is_array( $section['fields'] ) ) {
402 continue;
403 }
404 foreach ( $section['fields'] as $field_cfg ) {
405 if ( is_array( $field_cfg ) && isset( $field_cfg['type'] ) && 'campaign-hero' === $field_cfg['type'] ) {
406 $template_default_bg = ! empty( $field_cfg['background_image'] ) ? $field_cfg['background_image'] : '';
407 $template_default_avatar = ! empty( $field_cfg['avatar_image'] ) ? $field_cfg['avatar_image'] : '';
408 break 3;
409 }
410 }
411 }
412 }
413 }
414 }
415 }
416 $template_default_bg_url = $this->resolve_template_image( $template_default_bg, $template_id );
417 $template_default_avatar_url = $this->resolve_template_image( $template_default_avatar, $template_id );
418
419 ob_start();
420 ?>
421 <h4 class="charitable-panel-field" data-field-id="<?php echo intval( $field_id ); ?>"><?php echo esc_html( $this->name ); ?> (ID: <?php echo intval( $field_id ); ?>)</h4>
422 <?php
423
424 // Background image.
425 echo $ff->generate_uploader( // phpcs:ignore
426 isset( $settings['background_image'] ) ? $settings['background_image'] : '',
427 esc_html__( 'Background Image', 'charitable' ),
428 array(
429 'id' => 'field_' . esc_attr( $this->type ) . '_background_image_' . intval( $field_id ),
430 'name' => array( '_fields', intval( $field_id ), 'background_image' ),
431 'field_id' => esc_attr( $field_id ),
432 'button_label' => esc_html__( 'Upload', 'charitable' ),
433 'placeholder' => 'https://',
434 'tooltip' => esc_html__( 'The hero banner image. Renders edge-to-edge behind the title and donation widget. Clear to show a solid accent color; Reset to restore the template default.', 'charitable' ),
435 )
436 );
437
438 if ( ! empty( $template_default_bg_url ) ) {
439 ?>
440 <div class="charitable-campaign-hero-reset-row">
441 <button type="button" class="charitable-campaign-hero-reset-image charitable-campaign-builder-button-secondary" data-default="<?php echo esc_url( $template_default_bg_url ); ?>" data-target-label="background_image"><?php esc_html_e( 'Reset', 'charitable' ); ?></button>
442 </div>
443 <?php
444 }
445
446 // Logo image.
447 echo $ff->generate_uploader( // phpcs:ignore
448 isset( $settings['avatar_image'] ) ? $settings['avatar_image'] : '',
449 esc_html__( 'Logo Image', 'charitable' ),
450 array(
451 'id' => 'field_' . esc_attr( $this->type ) . '_avatar_image_' . intval( $field_id ),
452 'name' => array( '_fields', intval( $field_id ), 'avatar_image' ),
453 'field_id' => esc_attr( $field_id ),
454 'button_label' => esc_html__( 'Upload', 'charitable' ),
455 'placeholder' => 'https://',
456 'tooltip' => esc_html__( 'Optional small logo overlapping the bottom-left of the hero. Clear to remove the logo entirely (the title shifts left); Reset to restore the template default.', 'charitable' ),
457 )
458 );
459
460 if ( ! empty( $template_default_avatar_url ) ) {
461 ?>
462 <div class="charitable-campaign-hero-reset-row">
463 <button type="button" class="charitable-campaign-hero-reset-image charitable-campaign-builder-button-secondary" data-default="<?php echo esc_url( $template_default_avatar_url ); ?>" data-target-label="avatar_image"><?php esc_html_e( 'Reset', 'charitable' ); ?></button>
464 </div>
465 <?php
466 }
467
468 // Title override.
469 echo $ff->generate_text( // phpcs:ignore
470 isset( $settings['title_override'] ) ? $settings['title_override'] : '',
471 esc_html__( 'Title Override', 'charitable' ),
472 array(
473 'id' => 'field_' . esc_attr( $this->type ) . '_title_override_' . intval( $field_id ),
474 'name' => array( '_fields', intval( $field_id ), 'title_override' ),
475 'field_id' => intval( $field_id ),
476 'tooltip' => esc_html__( 'Optional. Leave blank to use the campaign post title.', 'charitable' ),
477 )
478 );
479
480 // Show raised amount + progress bar.
481 // Hidden fallback ensures an unchecked toggle still submits a value (0). Without it the
482 // key is absent from the POST and the read-side `! isset()` fallback re-defaults to ON,
483 // making "off" silently revert on save.
484 echo '<input type="hidden" name="_fields[' . intval( $field_id ) . '][show_raised]" value="0" />';
485 echo $ff->generate_toggle( // phpcs:ignore
486 ( ! isset( $settings['show_raised'] ) || ! empty( $settings['show_raised'] ) ) ? '1' : false,
487 esc_html__( 'Show Raised Amount + Progress Bar', 'charitable' ),
488 array(
489 'id' => 'field_' . esc_attr( $this->type ) . '_show_raised_' . intval( $field_id ),
490 'name' => array( '_fields', intval( $field_id ), 'show_raised' ),
491 'field_id' => intval( $field_id ),
492 'tooltip' => esc_html__( 'Displays the "$X raised toward our $Y goal" headline and progress bar above the donation widget. Only appears when a campaign goal is set (Settings → General Settings → Goal).', 'charitable' ),
493 )
494 );
495
496 // Enable recurring — disabled when the Recurring Donations addon isn't active.
497 echo '<input type="hidden" name="_fields[' . intval( $field_id ) . '][enable_recurring]" value="0" />';
498 echo $ff->generate_toggle( // phpcs:ignore
499 ( $has_recurring_addon && ! empty( $settings['enable_recurring'] ) ) ? '1' : false,
500 esc_html__( 'Enable Recurring Donations', 'charitable' ),
501 array(
502 'id' => 'field_' . esc_attr( $this->type ) . '_enable_recurring_' . intval( $field_id ),
503 'name' => array( '_fields', intval( $field_id ), 'enable_recurring' ),
504 'field_id' => intval( $field_id ),
505 'disabled' => ! $has_recurring_addon,
506 'tooltip' => $has_recurring_addon
507 ? esc_html__( 'Show monthly/one-time tabs in the donation widget.', 'charitable' )
508 : esc_html__( 'Requires the Charitable Recurring Donations addon. Install and activate the addon to enable this option.', 'charitable' ),
509 )
510 );
511
512 // One-time amounts.
513 echo $ff->generate_text( // phpcs:ignore
514 isset( $settings['onetime_amounts'] ) ? $settings['onetime_amounts'] : '50,150,500,1000',
515 esc_html__( 'One-Time Amounts', 'charitable' ),
516 array(
517 'id' => 'field_' . esc_attr( $this->type ) . '_onetime_amounts_' . intval( $field_id ),
518 'name' => array( '_fields', intval( $field_id ), 'onetime_amounts' ),
519 'field_id' => intval( $field_id ),
520 'tooltip' => esc_html__( 'Comma-separated list of suggested amounts, e.g. 50,150,500,1000.', 'charitable' ),
521 )
522 );
523
524 // One-time default.
525 echo $ff->generate_text( // phpcs:ignore
526 isset( $settings['onetime_default'] ) ? $settings['onetime_default'] : '',
527 esc_html__( 'One-Time Default', 'charitable' ),
528 array(
529 'id' => 'field_' . esc_attr( $this->type ) . '_onetime_default_' . intval( $field_id ),
530 'name' => array( '_fields', intval( $field_id ), 'onetime_default' ),
531 'field_id' => intval( $field_id ),
532 'tooltip' => esc_html__( 'Pre-selected amount on page load. Must match one of the amounts above. Leave blank to use the first amount.', 'charitable' ),
533 )
534 );
535
536 // Show "Other" toggle.
537 echo '<input type="hidden" name="_fields[' . intval( $field_id ) . '][onetime_show_other]" value="0" />';
538 echo $ff->generate_toggle( // phpcs:ignore
539 ( isset( $settings['onetime_show_other'] ) ? ! empty( $settings['onetime_show_other'] ) : true ) ? '1' : false,
540 esc_html__( 'Show "Other" Amount Option', 'charitable' ),
541 array(
542 'id' => 'field_' . esc_attr( $this->type ) . '_onetime_show_other_' . intval( $field_id ),
543 'name' => array( '_fields', intval( $field_id ), 'onetime_show_other' ),
544 'field_id' => intval( $field_id ),
545 'tooltip' => esc_html__( 'Show a custom amount input alongside the suggested amounts.', 'charitable' ),
546 )
547 );
548
549 // Donate button text.
550 echo $ff->generate_text( // phpcs:ignore
551 isset( $settings['cta_label'] ) ? $settings['cta_label'] : '',
552 esc_html__( 'Donate Button Text', 'charitable' ),
553 array(
554 'id' => 'field_' . esc_attr( $this->type ) . '_cta_label_' . intval( $field_id ),
555 'name' => array( '_fields', intval( $field_id ), 'cta_label' ),
556 'field_id' => intval( $field_id ),
557 'placeholder' => esc_attr__( 'Donate Now', 'charitable' ),
558 'tooltip' => esc_html__( 'Label shown on the donation button. Leave blank to use "Donate Now". The button color follows the campaign theme\'s Button color (Layout Options → Theme Colors → Button).', 'charitable' ),
559 )
560 );
561
562 // CSS class.
563 echo $ff->generate_text( // phpcs:ignore
564 isset( $settings['css_class'] ) ? $settings['css_class'] : '',
565 esc_html__( 'CSS Class', 'charitable' ),
566 array(
567 'id' => 'field_' . esc_attr( $this->type ) . '_css_class_' . intval( $field_id ),
568 'name' => array( '_fields', intval( $field_id ), 'css_class' ),
569 'field_id' => intval( $field_id ),
570 'tooltip' => esc_html__( 'Add CSS classes (space-separated) to customize this hero in your theme.', 'charitable' ),
571 )
572 );
573
574 return ob_get_clean();
575 }
576
577 /**
578 * Display settings for AJAX requests.
579 *
580 * @since 1.8.11.1
581 * @return void
582 */
583 public function settings_display_ajax() {
584
585 check_ajax_referer( 'charitable-builder', 'nonce' );
586
587 if ( ! current_user_can( 'edit_posts' ) ) {
588 wp_send_json_error();
589 }
590
591 $field_id = isset( $_POST['field_id'] ) ? intval( wp_unslash( $_POST['field_id'] ) ) : 0;
592 $campaign_id = isset( $_POST['campaign_id'] ) ? intval( wp_unslash( $_POST['campaign_id'] ) ) : 0;
593
594 $campaign_data = get_post_meta( $campaign_id, 'campaign_settings_v2', true );
595
596 $html = $this->settings_display( $field_id, $campaign_data );
597
598 wp_send_json_success( array( 'html' => $html ) );
599 exit;
600 }
601
602 /**
603 * Format and sanitize submitted field data.
604 *
605 * @since 1.8.11.1
606 *
607 * @param int $field_id Field ID.
608 * @param mixed $field_submit Submitted value.
609 * @param array $campaign_data Campaign data.
610 */
611 public function format( $field_id, $field_submit, $campaign_data ) {
612 }
613
614 /**
615 * Validate field on form submit.
616 *
617 * @since 1.8.11.1
618 *
619 * @param int $field_id Field ID.
620 * @param mixed $field_submit Submitted value.
621 * @param array $campaign_data Campaign data.
622 */
623 public function validate( $field_id, $field_submit, $campaign_data ) {
624 }
625 }
626
627 new Charitable_Field_Campaign_Hero();
628
629 endif;
630