PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
1.8.13.1 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 All 212 releases
charitable / includes / admin / campaign-builder / templates / class-templates.php

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

3,935 lines 154.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main class for template management for campaign builder.
4 *
5 * @package Charitable
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.8.0
10 * @version 1.8.0
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Campaign_Builder_Templates' ) ) :
19
20 /**
21 * Sets up the WordPress customizer.
22 *
23 * @since 1.8.0
24 */
25 class Charitable_Campaign_Builder_Templates {
26
27 /**
28 * One is the loneliest number that you'll ever do.
29 *
30 * @since 1.8.0
31 *
32 * @var object
33 */
34 private static $instance;
35
36 /**
37 * Field ID.
38 *
39 * @since 1.8.0
40 *
41 * @var integer
42 */
43 public $field_id;
44
45 /**
46 * Prepared templates list.
47 *
48 * @since 1.8.0
49 *
50 * @var array
51 */
52 private $prepared_templates = array();
53
54 /**
55 * Template categories data.
56 *
57 * @since 1.8.0
58 *
59 * @var array
60 */
61 private $categories;
62
63 /**
64 * Template addon labels data.
65 *
66 * @since 1.8.0
67 *
68 * @var array
69 */
70 private $addon_labels;
71
72 /**
73 * Is addon templates available?
74 *
75 * @since 1.8.0
76 *
77 * @var bool
78 */
79 private $is_addon_templates_available = false;
80
81 /**
82 * Is custom templates available?
83 *
84 * @since 1.8.0
85 *
86 * @var bool
87 */
88 private $is_custom_templates_available = false;
89
90 /**
91 * Storing template data (for now).
92 *
93 * @since 1.8.0
94 *
95 * @var array
96 */
97 private $templates_data = array();
98
99 /**
100 * Main Instance.
101 *
102 * @since 1.8.0
103 *
104 * @return Charitable_Builder
105 */
106 public static function get_instance() {
107
108 if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Charitable_Campaign_Builder_Templates ) ) {
109
110 self::$instance = new Charitable_Campaign_Builder();
111
112 add_action( 'admin_init', array( self::$instance, 'init' ), 10 );
113 }
114
115 return self::$instance;
116 }
117
118 /**
119 * Create object instance.
120 *
121 * @since 1.8.0
122 */
123 public function __construct() {
124
125 if ( ! is_admin() ) {
126 return;
127 }
128
129 $this->field_id = 0;
130
131 $this->templates_data = $this->get_source_template_data();
132
133 $this->init();
134 }
135
136 /**
137 * Init things.
138 *
139 * @since 1.8.0
140 */
141 public function init() {
142
143 $this->hooks();
144 }
145
146 /**
147 * Hook things.
148 *
149 * @since 1.8.0
150 */
151 public function hooks() {
152
153 add_action( 'admin_print_scripts', array( $this, 'upgrade_banner_template' ) );
154 }
155
156 /**
157 * Returns the starting HTML element for a given template type.
158 *
159 * @since 1.8.0
160 *
161 * @param string $type The type of template element to retrieve.
162 * @param int $counter The counter for the template element.
163 * @param string $additional_css Additional CSS classes to add to the template element.
164 * @param array $campaign_data An array of campaign data to pass to the template element.
165 * @param string $background_image Optional background image URL for the row.
166 * @param bool $locked Optional. Whether the row is locked (no move/delete).
167 * @return string The starting HTML element for the given template type.
168 */
169 public function get_template_element_start( $type = 'row', $counter = 1, $additional_css = '', $campaign_data = array(), $background_image = '', $locked = false ) {
170
171 ob_start();
172
173 $row_bg_url = '' !== $background_image ? esc_url( $background_image ) : '';
174 $row_style_attr = '' !== $row_bg_url ? ' style="background-image:url(\'' . $row_bg_url . '\');"' : '';
175 $row_locked_css = $locked ? ' is-locked' : '';
176 $row_locked_attr = $locked ? ' data-row-locked="1"' : '';
177 $row_bg_css = '' !== $row_bg_url ? ' has-background-image' : '';
178
179 switch ( $type ) {
180 case 'header':
181 ?>
182 <!-- charitable header start -->
183 <header id="charitable-preview-header-<?php echo intval( $counter ); ?>"<?php echo $row_locked_attr; // phpcs:ignore ?> class="charitable-preview-header <?php echo esc_attr( $additional_css ); ?><?php echo esc_attr( $row_bg_css . $row_locked_css ); ?>"<?php echo $row_style_attr; // phpcs:ignore ?>>
184 <div class="row" data-row-id="<?php echo intval( $counter ); ?>" data-row-type="<?php echo esc_attr( $type ); ?>" data-row-css="<?php echo esc_attr( $additional_css ); ?>">
185 <?php
186 break;
187
188 case 'tabs':
189 $enable_tabs_css = isset( $campaign_data['layout']['advanced']['enable_tabs'] ) && '' !== trim( $campaign_data['layout']['advanced']['enable_tabs'] ) ? 'disabled' : '';
190 ?>
191 <!-- charitable tabs start -->
192 <article id="charitable-preview-tab-container" class="charitable-preview-tab-container <?php echo $enable_tabs_css; ?>"> <?php // phpcs:ignore ?>
193 <a href="#" class="charitable-hover-button charitable-field-edit" title="<?php echo esc_html__( 'Edit Area', 'charitable' ); ?>"><i class="fa fa-pencil"></i></a>
194 <?php
195 break;
196
197 default:
198 // likely a row.
199 ?>
200 <!-- charitable row/default start -->
201 <div id="charitable-preview-row-<?php echo intval( $counter ); ?>"<?php echo $row_locked_attr; // phpcs:ignore ?> class="charitable-preview-row <?php echo esc_attr( $additional_css ); ?><?php echo esc_attr( $row_bg_css . $row_locked_css ); ?>"<?php echo $row_style_attr; // phpcs:ignore ?>>
202 <div class="row" data-row-id="<?php echo intval( $counter ); ?>" data-row-type="<?php echo esc_attr( $type ); ?>" data-row-css="<?php echo esc_attr( $additional_css ); ?>">
203 <?php
204 break;
205 }
206
207 return ob_get_clean();
208 }
209
210 /**
211 * Create closing HTML tags based on the type.
212 *
213 * @since 1.8.0
214 * @param string $type The type of template element to retrieve.
215 * @param int $counter The counter for the template element.
216 *
217 * @return string The closing HTML tags for the given template type.
218 */
219 public function get_template_element_end( $type = 'row', $counter = 1 ) { // phpcs:ignore
220
221 ob_start();
222
223 switch ( $type ) {
224 case 'header':
225 ?>
226 </div>
227 </header>
228 <!-- charitable header end -->
229 <?php
230 break;
231
232 case 'tabs':
233 ?>
234 </article>
235 <!-- charitable tabs end -->
236 <?php
237 break;
238
239 default:
240 // likely a row.
241 ?>
242 </div>
243 </div>
244 <!-- charitable row/default end -->
245 <?php
246 break;
247 }
248
249 return ob_get_clean();
250 }
251
252 /**
253 * Create fields for preview area based on an array, usually for a column of the layout..
254 *
255 * @since 1.8.0
256 *
257 * @param array $fields Fields.
258 * @param array $theme Theme data.
259 * @param array $campaign_data Campaign data.
260 * @param int $last_field_id Last field ID.
261 *
262 * @return string
263 */
264 public function render_fields( $fields, $theme, $campaign_data, $last_field_id = 0, $row_locked = false ) {
265
266 if ( empty( $fields ) ) {
267 return;
268 }
269
270 $field_id = $last_field_id;
271
272 foreach ( $fields as $key => $field_type_data ) :
273
274 $field_type = is_array( $field_type_data ) && ! empty( $field_type_data['type'] ) ? esc_html( $field_type_data['type'] ) : esc_html( $key );
275
276 $type = 'Charitable_Field_' . str_replace( ' ', '_', ( ucwords( str_replace( '-', ' ', $field_type ) ) ) );
277
278 if ( class_exists( $type ) ) :
279
280 $class = new $type();
281
282 $field_settings = is_array( $field_type_data ) ? $field_type_data : array();
283
284 // define a default photo for the photo type block if it's passed from the theme JSON data.
285 if ( $field_type == 'photo' && ! empty( $field_settings['default'] ) && ! empty( $theme['meta']['slug'] ) ) {
286
287 $campaign_template_slug = esc_attr( $theme['meta']['slug'] );
288
289 // if the "default" parameter for the photo is passed then we attempt to pull that photo.
290 $default_filename = ! empty( $field_type_data['default'] ) ? esc_html( $field_type_data['default'] ) : 'photo.jpg';
291 $theme_thumbnail = ( false !== $default_filename ) && $campaign_template_slug ? charitable()->get_path( 'assets', true ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $default_filename : false;
292
293 // see if the thumbnail or default photo exists.
294 if ( false !== $theme_thumbnail && file_exists( $theme_thumbnail ) ) {
295 $field_settings['default'] = charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $default_filename;
296 }
297 }
298
299 // Expand template-relative filenames for the campaign-hero block.
300 if ( $field_type === 'campaign-hero' && ! empty( $theme['meta']['slug'] ) ) {
301 $campaign_template_slug = esc_attr( $theme['meta']['slug'] );
302 foreach ( array( 'background_image', 'avatar_image' ) as $img_key ) {
303 if ( ! empty( $field_settings[ $img_key ] ) ) {
304 $image_value = $field_settings[ $img_key ];
305 $image_basename = basename( $image_value );
306 if ( $image_basename === $image_value ) {
307 $asset_path = charitable()->get_path( 'assets', true ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $image_basename;
308 if ( file_exists( $asset_path ) ) {
309 $field_settings[ $img_key ] = charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $image_basename;
310 }
311 }
312 }
313 }
314 }
315
316 $field_settings['id'] = $field_id;
317
318 $charitable_field_css_classes = array(
319 'charitable-field',
320 'charitable-field-' . $field_type,
321 $class->can_be_edited ? 'charitable-can-edit' : 'charitable-no-edit',
322 $class->can_be_duplicated ? 'charitable-can-duplicate' : 'charitable-no-duplicate',
323 $class->can_be_deleted ? 'charitable-can-delete' : 'charitable-no-delete',
324 );
325
326 if ( $row_locked ) {
327 $charitable_field_css_classes[] = 'charitable-field-not-draggable';
328 }
329
330 echo '<div class="' . esc_attr( implode( ' ', $charitable_field_css_classes ) ) . '" id="charitable-field-' . intval( $field_id ) . '" data-field-id="' . intval( $field_id ) . '" data-field-type="' . esc_attr( $field_type ) . '" data-field-max="' . intval( $class->max_allowed ) . '" style="">'; // phpcs:ignore
331 if ( $class->can_be_edited ) :
332 echo '<a href="#" class="charitable-field-edit" data-type="' . esc_attr( $class->edit_type ) . '" data-section="' . esc_attr( $class->edit_section ) . '" data-edit-field-id="' . esc_attr( $class->edit_field_id ) . '" title="' . esc_attr( $class->edit_label ) . '"><i class="fa fa-pencil"></i></a>';
333 endif;
334 if ( $class->can_be_duplicated && ! $row_locked ) :
335 echo '<a href="#" class="charitable-field-duplicate" title="Duplicate Field"><i class="fa fa-files-o" aria-hidden="true"></i></a>';
336 endif;
337 if ( $class->can_be_deleted && ! $row_locked ) :
338 echo '<a href="#" class="charitable-field-delete" title="Delete Field"><i class="fa fa-trash-o"></i></a>';
339 endif;
340
341 echo $class->field_preview( $field_settings, $campaign_data, $field_id, $theme ); // phpcs:ignore
342 echo '</div>';
343
344 else :
345
346 $this->render_missing_addon_field_preview( $field_type, $field_id, $field_type_data, $campaign_data );
347
348 endif;
349
350 ++$field_id;
351
352 endforeach;
353
354 return $field_id;
355 }
356
357 /**
358 * Sorts an array by the order of another array.
359 *
360 * @param array $tabs The array to be sorted.
361 * @param array $tab_order The array that defines the order.
362 *
363 * @return array The sorted array.
364 */
365 public function sort_array_by_array( $tabs, $tab_order ) {
366
367 $ordered = array();
368
369 foreach ( $tab_order as $key ) {
370 if ( array_key_exists( $key, $tabs ) ) {
371 $ordered[ $key ] = $tabs[ $key ];
372 unset( $tabs[ $key ] );
373 }
374 }
375
376 return $ordered + $tabs;
377 }
378
379 /**
380 * Create tabs based on the template data.
381 *
382 * @since 1.8.0
383 *
384 * @param string $tabs Tab info.
385 * @param string $theme The current theme.
386 * @param array $campaign_data Campaign data..
387 *
388 * @return string
389 */
390 public function get_template_tab_nav( $tabs = false, $theme = false, $campaign_data = false ) {
391
392 if ( ! is_admin() ) {
393 return;
394 }
395
396 if ( false === $tabs || false === $theme ) {
397 return;
398 }
399
400 ob_start();
401
402 $counter = 0;
403 $tab_id = 0;
404 $advanced = ( ! empty( $campaign_data['layout']['advanced'] ) ) ? $campaign_data['layout']['advanced'] : $theme['advanced'];
405 $tab_style = isset( $advanced['tab_style'] ) && '' !== trim( $advanced['tab_style'] ) ? $advanced['tab_style'] : 'boxed';
406 $tab_size = isset( $advanced['tab_size'] ) && '' !== trim( $advanced['tab_size'] ) ? $advanced['tab_size'] : 'medium';
407 $tab_order = ! empty( $campaign_data['tab_order'] ) ? $campaign_data['tab_order'] : false;
408
409 // sort the multi-dimenstional array of $tabs based on the values of $tab_order array.
410 if ( is_array( $tabs ) && ! empty( $tabs ) && ! empty( $tab_order ) ) {
411 $tabs = $this->sort_array_by_array( $tabs, $tab_order );
412 }
413
414 ?>
415
416 <nav class="charitable-campaign-preview-nav tab-style-<?php echo esc_attr( $tab_style ); ?> tab-size-<?php echo esc_attr( $tab_size ); ?>">
417
418 <ul class="charitable-campaign-preview-nav-list">
419
420 <?php if ( is_array( $tabs ) && ! empty( $tabs ) ) : ?>
421 <?php
422
423 $counter = 1;
424
425 /*
426 The initial structure of this array looks something like:
427
428 'title' => 'My Campaign',
429 'type' => 'html',
430 'slug' => 'default',
431 'fields' => array (
432 'campaign-description',
433 )
434
435 */
436
437 foreach ( $tabs as $tab_id => $tab_info ) :
438
439 $title = ! empty( $tab_info['title'] ) ? esc_html( $tab_info['title'] ) : false;
440 $title = false === $title && ! empty( $campaign_data['tabs'][ $tab_id ]['title'] ) ? esc_html( $campaign_data['tabs'][ $tab_id ]['title'] ) : $title;
441 $type = ( isset( $tab_info['type'] ) && '' !== trim( $tab_info['type'] ) ) ? $tab_info['type'] : 'html';
442 $class = 'tab_type_' . $type . ' ';
443 $class = ( $counter === 1 ) ? 'active' : false;
444 $show_hide_css = ! empty( $campaign_data['tabs'][ $tab_id ]['visible_nav'] ) && $campaign_data['tabs'][ $tab_id ]['visible_nav'] === 'invisible' ? 'charitable-tab-hide' : false;
445
446 ?>
447 <li id="tab_<?php echo esc_attr( $tab_id ); ?>_title" data-tab-id="<?php echo esc_attr( $tab_id ); ?>" data-tab-type="<?php echo esc_attr( $type ); ?>" class="tab_title <?php echo esc_attr( $class ); ?> <?php echo esc_attr( $show_hide_css ); ?>"><a href="#"><?php echo esc_html( $title ); ?></a></li>
448 <?php
449
450 ++$counter;
451
452 endforeach;
453
454 endif;
455 ?>
456
457 </ul>
458
459 </nav>
460
461 <?php
462
463 return ob_get_clean();
464 }
465
466 /**
467 * Create tab content based on the template data.
468 *
469 * @since 1.8.0
470 *
471 * @param array $tabs Tab info.
472 * @param string $theme The current theme.
473 * @param array $campaign_data Campaign data.
474 * @param array $row_fields Row fields.
475 * @param int $last_field_id Last field ID.
476 *
477 * @return string
478 */
479 public function get_template_tab_content( $tabs = false, $theme = false, $campaign_data = array(), $row_fields = false, $last_field_id = 0 ) {
480
481 if ( ! is_admin() ) {
482 return;
483 }
484
485 if ( false === $tabs || false === $theme || ! is_array( $tabs ) ) {
486 return;
487 }
488
489 ob_start();
490
491 $counter = 0;
492 $tab_id = 0;
493 $field_id = $last_field_id;
494 $no_tab_class = ( empty( $tabs ) || false === $tabs ) ? 'empty-tabs' : false;
495
496 ?>
497
498 <div class="tab-content <?php echo esc_attr( $no_tab_class ); ?>">
499 <ul>
500 <?php if ( $tabs ) : ?>
501 <?php
502
503 $counter = 1;
504
505 foreach ( $tabs as $tab_id => $tab_info ) :
506
507 $type = ( isset( $tab_info['type'] ) && '' !== trim( $tab_info['type'] ) ) ? $tab_info['type'] : 'html';
508 $class = 'tab_type_' . $type . ' ';
509 $class .= ( $counter === 1 ) ? 'active' : false;
510 $fields = ( ! empty( $tab_info['fields'] ) ) ? $tab_info['fields'] : false;
511 $class .= ( false === $fields ) ? ' empty-tab' : false;
512
513 ?>
514
515 <li id="tab_<?php echo intval( $tab_id ); ?>_content" class="tab_content_item <?php echo esc_attr( $class ); ?>" data-tab-type="<?php echo esc_attr( $type ); ?>" data-tab-id="<?php echo esc_attr( $tab_id ); ?>">
516
517 <div class="charitable-tab-wrap ui-sortable">
518
519 <?php echo $this->tab_empty_notice(); // phpcs:ignore ?>
520
521 <?php echo charitable_builder_tab_content_preview_by_type( $type ); // phpcs:ignore ?>
522
523 <?php
524
525 if ( ! empty( $fields ) ) :
526
527 foreach ( $fields as $key => $field_info_or_id ) :
528
529 // field_info could be just field_id OR an array of information from template JSON (type, default, headline, etc.).
530
531 if ( is_array( $field_info_or_id ) ) {
532 $field_info = $field_info_or_id;
533 $field_type = ! empty( $field_info['type'] ) ? esc_attr( $field_info['type'] ) : false;
534 $field_type_data = $field_info_or_id;
535 } else {
536 $field_id = intval( $field_info_or_id );
537 $field_type = is_array( $row_fields ) && ! empty( $row_fields[ $field_id ] ) ? esc_attr( $row_fields[ $field_id ] ) : false;
538 $field_type_data = ! empty( $campaign_data['fields'][ $field_id ] ) ? $campaign_data['fields'][ $field_id ] : false;
539 }
540
541 if ( false !== $field_type ) :
542
543 $class_type = 'Charitable_Field_' . str_replace( ' ', '_', ( ucwords( str_replace( '-', ' ', $field_type ) ) ) );
544
545 if ( class_exists( $class_type ) ) :
546
547 $class = new $class_type();
548 $field_settings = is_array( $field_type_data ) ? $field_type_data : array();
549
550 // define a default photo for the photo type block if it's passed from the theme JSON data.
551 if ( $field_type === 'photo' && ! empty( $field_settings['default'] ) && ! empty( $theme['meta']['slug'] ) ) {
552
553 $campaign_template_slug = esc_attr( $theme['meta']['slug'] );
554
555 // if the "default" parameter for the photo is passed then we attempt to pull that photo.
556 $default_filename = ! empty( $field_type_data['default'] ) ? esc_html( $field_type_data['default'] ) : 'photo.jpg';
557 $theme_thumbnail = ( false !== $default_filename ) && $campaign_template_slug ? charitable()->get_path( 'assets', true ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $default_filename : false;
558
559 // see if the thumbnail or default photo exists.
560 if ( false !== $theme_thumbnail && file_exists( $theme_thumbnail ) ) {
561 $field_settings['default'] = charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $default_filename;
562 }
563 }
564
565 // Expand template-relative filenames for the campaign-hero block.
566 if ( $field_type === 'campaign-hero' && ! empty( $theme['meta']['slug'] ) ) {
567 $campaign_template_slug = esc_attr( $theme['meta']['slug'] );
568 foreach ( array( 'background_image', 'avatar_image' ) as $img_key ) {
569 if ( ! empty( $field_settings[ $img_key ] ) ) {
570 $image_value = $field_settings[ $img_key ];
571 $image_basename = basename( $image_value );
572 if ( $image_basename === $image_value ) {
573 $asset_path = charitable()->get_path( 'assets', true ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $image_basename;
574 if ( file_exists( $asset_path ) ) {
575 $field_settings[ $img_key ] = charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/' . $image_basename;
576 }
577 }
578 }
579 }
580 }
581 $field_settings['id'] = $field_id;
582
583 $charitable_field_css_classes = array(
584 'charitable-field',
585 'charitable-field-' . $field_type,
586 $class->can_be_edited ? 'charitable-can-edit' : 'charitable-no-edit',
587 $class->can_be_duplicated ? 'charitable-can-duplicate' : 'charitable-no-duplicate',
588 $class->can_be_deleted ? 'charitable-can-delete' : 'charitable-no-delete',
589 );
590
591 echo '<div class="' . implode( ' ', $charitable_field_css_classes ) . '" id="charitable-field-' . intval( $field_id ) . '" data-field-id="' . intval( $field_id ) . '" data-field-type="' . esc_attr( $field_type ) . '" data-field-max="' . esc_attr( $class->max_allowed ) . '" style="">'; // phpcs:ignore
592
593 if ( $class->can_be_edited ) :
594 echo '<a href="#" class="charitable-field-edit" data-type="' . esc_attr( $class->edit_type ) . '" data-section="' . esc_attr( $class->edit_section ) . '" data-edit-field-id="' . esc_attr( $class->edit_field_id ) . '" title="' . esc_html( $class->edit_label ) . '"><i class="fa fa-pencil"></i></a>';
595 endif;
596 if ( $class->can_be_duplicated ) :
597 echo '<a href="#" class="charitable-field-duplicate" title="Duplicate Field"><i class="fa fa-files-o" aria-hidden="true"></i></a>';
598 endif;
599 if ( $class->can_be_deleted ) :
600 echo '<a href="#" class="charitable-field-delete" title="Delete Field"><i class="fa fa-trash-o"></i></a>';
601 endif;
602 echo $class->field_preview( $field_settings, $campaign_data, $field_id, 'preview', $theme ); // phpcs:ignore
603 echo '</div>';
604
605 else :
606
607 $this->render_missing_addon_field_preview( $field_type, $field_id, $field_type_data, $campaign_data );
608
609 endif; // class exists.
610
611 endif; // if type isn't false.
612
613 ++$field_id;
614
615 endforeach;
616
617 endif;
618
619 ?>
620 </div>
621
622 </li>
623
624 <?php
625
626 ++$counter;
627
628 endforeach;
629 ?>
630 <?php else : ?>
631 <li id="tab_<?php echo esc_attr( $tab_id ); ?>_content" class="tab_content_item active" data-tab-id="0" data-tab-type="html"></li>
632 <?php endif; ?>
633 </ul>
634 <?php if ( $no_tab_class ) : ?>
635 <p class="no-tab-notice"><?php echo $this->no_tab_empty_notice(); // phpcs:ignore ?></p>
636 <?php endif; ?>
637 </div>
638
639 <?php
640
641 echo ob_get_clean(); // phpcs:ignore
642
643 return absint( $field_id - 1 );
644 }
645
646 /**
647 * Display missing addon message.
648 *
649 * @since 1.8.0
650 *
651 * @param string $field_type Tab info.
652 * @param integer $field_id The current theme.
653 * @param array $field_type_data The field type data.
654 * @param array $campaign_data The campaign data.
655 *
656 * @return void
657 */
658 public function render_missing_addon_field_preview( $field_type = false, $field_id = false, $field_type_data = false, $campaign_data = false ) {
659
660 $field_label = ( false !== $field_type ) ? (string) $this->get_addon_field_label( $field_type ) : false;
661 $field_label = ( false === $field_label || '' === trim( $field_label ) ) ? 'unknown' : $field_label;
662
663 $addon_label = ( false !== $field_type ) ? (string) $this->get_addon_label( $field_type ) : false;
664 $addon_label = ( false === $addon_label || '' === trim( $addon_label ) ) ? 'unknown' : $addon_label;
665
666 $template_id = ! empty( $campaign_data['template_id'] ) ? esc_attr( $campaign_data['template_id'] ) : false;
667 $template_id = $template_id === false & ! empty( $_POST['id'] ) ? esc_attr( $_POST['id'] ) : $template_id; // phpcs:ignore
668
669 // Get a background image, preferrabbly supplied.
670 $background_image_url = $this->get_addon_placeholder_image( $field_type );
671 $css_style = false !== $background_image_url ? 'background-image: url(' . $background_image_url . ');' : false;
672 $search_term = $this->get_addon_search_term( $field_type );
673
674 $field_id = intval( $field_id );
675
676 echo '<div class="charitable-field charitable-field-missing charitable-field-' . esc_attr( $field_type ) . '" id="charitable-field-' . intval( $field_id ) . '" data-field-id="' . intval( $field_id ) . '" data-field-type="' . esc_attr( $field_type ) . '">';
677
678 echo '<a href="#" class="charitable-field-delete" title="Delete Field"><i class="fa fa-trash-o"></i></a>';
679
680 echo '<div class="charitable-missing-addon-bg" style="' . esc_attr( $css_style ) . '"></div>';
681
682 echo '<div class="charitable-missing-addon-content">';
683
684 if ( charitable_is_pro() ) {
685
686 $display_html = $this->get_addon_install_or_activate_buttons( $field_type );
687
688 if ( ! empty( $display_html['title'] ) ) {
689 echo $display_html['title']; // phpcs:ignore
690 } else {
691 echo '<h2>We\'re sorry, the <strong>' . esc_html( $field_label ) . '</strong> does not seem to be available.</h2>';
692 }
693
694 if ( ! empty( $display_html['description'] ) ) {
695 echo $display_html['description']; // phpcs:ignore
696 } else {
697 echo '<p>Please make sure that you have the latest version of the <strong>' . esc_html( $addon_label ) . '</strong> addon installed and activated.</p>';
698 }
699
700 echo '<div class="education-buttons">';
701
702 if ( ! empty( $display_html['buttons'] ) ) {
703 echo $display_html['buttons']; // phpcs:ignore
704 } else {
705 echo '<a class="button-link" target="_blank" href="' . esc_url( admin_url( 'admin.php?page=charitable-addons&search=' . $search_term ) ) . '">' . esc_html__( 'View Addons', 'charitable' ) . '</a>';
706 }
707
708 echo '</div>';
709
710 } else {
711
712 echo '<h2>We\'re sorry, the <strong>' . esc_html( $field_label ) . '</strong> is not available on your plan.</h2> <p>Please upgrade to the PRO plan to unlock all these awesome features.</p>';
713
714 echo '<div class="education-buttons">
715 <a class="button-link" target="_blank" href="https://wpcharitable.com/lite-vs-pro/">' . esc_html__( 'Learn More', 'charitable' ) . '</a> <button type="button" class="btn btn-confirm update-to-pro-link">' . esc_html__( 'Upgrade to PRO', 'charitable' ) . '</button>
716 </div>';
717
718 }
719
720 echo '<a href="#" class="charitable-field-delete" title="Delete Field"><i class="fa fa-trash-o"></i></a>';
721 echo '</div>';
722 echo '</div>';
723 }
724
725 /**
726 * Display missing addon message and button(s).
727 *
728 * @since 1.8.0.4
729 *
730 * @param string $field_type The field type.
731 *
732 * @return array|bool
733 */
734 public function get_addon_install_or_activate_buttons( $field_type = false ) {
735
736 if ( false === $field_type ) {
737 return false;
738 }
739
740 // get the list of installed plugins and charitable addon information.
741 $installed_plugins = get_plugins();
742 $addon_information = $this->get_addon_information( $field_type );
743
744 // grab the list of active plugins only once, don't need it in the loop.
745 $active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- 'active_plugins' is a WordPress core hook.
746
747 if ( false === $addon_information ) {
748 return false;
749 }
750
751 $slug = ! empty( $addon_information['slug'] ) ? esc_attr( $addon_information['slug'] ) : false;
752
753 if ( false === $slug ) {
754 return false;
755 }
756
757 // Determine the plugin basename.
758 $plugin_basename = $this->get_plugin_basename_from_slug( $addon_information['slug'] );
759
760 if ( isset( $installed_plugins[ $plugin_basename ] ) && ! is_plugin_active( $plugin_basename ) ) {
761
762 // if the plugin exists (installed, visible in the plugin list in WP admin) but not activated.
763
764 return array(
765 'title' => '<h2>We\'re sorry, the <strong>' . esc_html( $addon_information['name'] ) . '</strong> addon does not seem to be available.</h2>',
766 'description' => '<p>It appears you have <strong>' . esc_html( $addon_information['name'] ) . '</strong> installed but it needs to be activated.</p>',
767 'buttons' => '<a class="button-link charitable-not-activated" data-plugin-url="' . esc_attr( $addon_information['slug'] ) . '/' . esc_attr( $addon_information['slug'] ) . '.php" data-name="' . $addon_information['name'] . '" data-slug="' . $addon_information['slug'] . '" data-field-icon="" href="#">' . esc_html__( 'Activate ', 'charitable' ) . esc_html( $addon_information['name'] ) . '</a>',
768 );
769 } elseif ( is_array( $active_plugins ) && ! in_array( 'charitable-' . $slug . '/charitable-' . $slug . '.php', $active_plugins, true ) ) {
770
771 // if pro is active AND the plugin is NOT activated, then that's another CSS class to encourage an install.
772
773 return array(
774 'title' => '<h2>We\'re sorry, the <strong>' . esc_html( $addon_information['name'] ) . '</strong> addon does not seem to be available.</h2>',
775 'description' => '<p>It appears you need <strong>' . esc_html( $addon_information['name'] ) . '</strong> to be installed and activated.</p>',
776 'buttons' => '<a class="button-link charitable-not-installed" data-plugin-url="' . $addon_information['install'] . '" data-name="' . $addon_information['name'] . '" data-slug="" data-field-icon="" href="#">' . esc_html__( 'Install &amp; Activate ', 'charitable' ) . esc_html( $addon_information['name'] ) . '</a>',
777 'install_url' => ! empty( $addon_information['install'] ) ? esc_url( $addon_information['install'] ) : false,
778 );
779 }
780
781 return false;
782 }
783
784
785 /**
786 * Get information about the addon used to display the missing addon message.
787 *
788 * @since 1.8.0.4
789 *
790 * @param string $field_type The field type.
791 *
792 * @return array|bool
793 */
794 public function get_addon_information( $field_type = false ) {
795
796 if ( false === $field_type ) {
797 return false;
798 }
799
800 // Attempt to self-correct. If the $field_type doesn't start with 'charitable-', add it.
801 if ( false === strpos( strtolower( $field_type ), 'charitable-' ) ) {
802 $field_type = 'charitable-' . $field_type;
803 }
804
805 // grab the list of active plugins only once, don't need it in the loop.
806 $active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- 'active_plugins' is a WordPress core hook.
807
808 // get license stuff once.
809 $charitable_addons = get_transient( '_charitable_addons' ); // @codingStandardsIgnoreLine - testing.
810
811 // Get addons data from transient or perform API query if no transient.
812 if ( false === $charitable_addons ) {
813 $charitable_addons = charitable_get_addons_data_from_server();
814 }
815
816 if ( ! is_array( $charitable_addons ) || empty( $charitable_addons ) ) {
817 return;
818 }
819
820 foreach ( $charitable_addons as $charitable_addon ) {
821 if ( ! empty( $charitable_addon['slug'] ) &&
822 ( strtolower( $field_type ) === strtolower( $charitable_addon['slug'] )
823 || strtolower( $field_type ) . 's' === strtolower( $charitable_addon['slug'] ) // covers in case the field type is 'video' when the slug is actually 'charitable-videos' (pural).
824 )
825 ) {
826 return $charitable_addon;
827 }
828 // This is to cover the case where the field type for example is 'charitable-ambassadors-team' but the slug is 'charitable-ambassadors'.
829 if ( substr_count( $field_type, '-' ) > 1 ) {
830 $modified_field_type = substr( $field_type, 0, strrpos( $field_type, '-' ) );
831 if ( ! empty( $charitable_addon['slug'] ) && strtolower( $modified_field_type ) === strtolower( $charitable_addon['slug'] ) ) {
832 return $charitable_addon;
833 }
834 }
835 }
836
837 return false;
838 }
839
840 /**
841 * Retrieve the plugin basename from the plugin slug.
842 *
843 * @since 1.7.0
844 *
845 * @param string $slug The plugin slug.
846 * @return string The plugin basename if found, else the plugin slug.
847 */
848 public function get_plugin_basename_from_slug( $slug ) {
849
850 $keys = array_keys( get_plugins() );
851
852 foreach ( $keys as $key ) {
853 if ( preg_match( '|^' . $slug . '|', $key ) ) {
854 return $key;
855 }
856 }
857
858 return $slug;
859 }
860
861 /**
862 * Get addoon field label.
863 *
864 * @since 1.8.0
865 *
866 * @param string $field_type The field type.
867 *
868 * @return array
869 */
870 public function get_addon_field_label( $field_type = false ) {
871
872 $field_type_labels = $this->get_addon_field_labels();
873
874 if ( array_key_exists( $field_type, $field_type_labels ) ) {
875 return esc_html( $field_type_labels[ $field_type ]['field_label'] );
876 }
877
878 // Try a variation.
879 if ( array_key_exists( 'charitable-' . $field_type, $field_type_labels ) ) {
880 return esc_html( $field_type_labels[ 'charitable-' . $field_type ]['field_label'] );
881 }
882
883 // Try a variation.
884 if ( array_key_exists( 'charitable-' . $field_type . 's', $field_type_labels ) ) {
885 return esc_html( $field_type_labels[ 'charitable-' . $field_type . 's' ]['field_label'] );
886 }
887
888 return false;
889 }
890
891 /**
892 * Get addoon label.
893 *
894 * @since 1.8.0
895 *
896 * @param string $field_type The field type.
897 *
898 * @return array
899 */
900 public function get_addon_label( $field_type = false ) {
901
902 $field_type_labels = $this->get_addon_field_labels();
903
904 if ( array_key_exists( $field_type, $field_type_labels ) ) {
905 return esc_html( $field_type_labels[ $field_type ]['addon_label'] );
906 }
907
908 // Try a variation.
909 if ( array_key_exists( 'charitable-' . $field_type, $field_type_labels ) ) {
910 return esc_html( $field_type_labels[ 'charitable-' . $field_type ]['addon_label'] );
911 }
912
913 // Try a variation.
914 if ( array_key_exists( 'charitable-' . $field_type . 's', $field_type_labels ) ) {
915 return esc_html( $field_type_labels[ 'charitable-' . $field_type . 's' ]['addon_label'] );
916 }
917
918 return false;
919 }
920
921 /**
922 * Get the search term that will show the correct addon if the user clicks on "View addons" in the preview
923 *
924 * @since 1.8.0
925 *
926 * @param string $addon_slug The addon slug.
927 *
928 * @return string
929 */
930 public function get_addon_search_term( $addon_slug = false ) {
931
932 if ( false === $addon_slug ) {
933 return false;
934 }
935
936 $addon_labels = $this->get_addon_field_labels();
937
938 if ( ! empty( $addon_labels[ $addon_slug ] ) ) {
939 return strtolower( $addon_labels[ $addon_slug ]['admin_search'] );
940 }
941
942 // not found? take a guess.
943 $search_term = str_replace( 'charitable-', '', $addon_slug );
944
945 return strtolower( $search_term );
946 }
947
948 /**
949 * Get the placeholder image that appears in preview when the user doesn't have the addon activated.
950 *
951 * @since 1.8.0
952 *
953 * @param string $addon_slug The addon slug.
954 *
955 * @return string
956 */
957 public function get_addon_placeholder_image( $addon_slug = false ) {
958
959 if ( false === $addon_slug ) {
960 return false;
961 }
962
963 $addon_labels = $this->get_addon_field_labels();
964
965 if ( ! empty( $addon_labels[ $addon_slug ] ) ) {
966 return strtolower( $addon_labels[ $addon_slug ]['placeholder_img'] );
967 }
968
969 return false;
970 }
971
972 /**
973 * Get addon field labels.
974 *
975 * @since 1.8.0
976 *
977 * @return array
978 */
979 public function get_addon_field_labels() {
980
981 $this->addon_labels = apply_filters(
982 'charitable_campaign_builder_addon_labels',
983 array(
984 'ambassadors-team' => array(
985 'field_label' => 'Team Field',
986 'addon_label' => 'Ambassadors Addon',
987 'url' => false,
988 'plan' => 'pro',
989 'admin_search' => 'ambassador',
990 'placeholder_img' => charitable()->get_path( 'directory', false ) . 'assets/images/campaign-builder/placeholders/field-team.jpg',
991 ),
992 'video' => array(
993 'field_label' => 'Video Field',
994 'addon_label' => 'Video Addon',
995 'url' => false,
996 'plan' => 'basic',
997 'admin_search' => 'video',
998 'placeholder_img' => charitable()->get_path( 'directory', false ) . 'assets/images/campaign-builder/placeholders/field-video.jpg',
999 ),
1000 'updates-main' => array(
1001 'field_label' => 'Updates Field',
1002 'addon_label' => 'Simple Updates Addon',
1003 'url' => false,
1004 'plan' => 'pro',
1005 'admin_search' => 'updates',
1006 'placeholder_img' => charitable()->get_path( 'directory', false ) . 'assets/images/campaign-builder/placeholders/field-updates.jpg',
1007 ),
1008 'comments-main' => array(
1009 'field_label' => 'Comments Field',
1010 'addon_label' => 'Comments Addon',
1011 'url' => false,
1012 'plan' => 'pro',
1013 'admin_search' => 'comments',
1014 'placeholder_img' => charitable()->get_path( 'directory', false ) . 'assets/images/campaign-builder/placeholders/field-comments.jpg',
1015 ),
1016 )
1017 );
1018
1019 return $this->addon_labels;
1020 }
1021
1022 /**
1023 * Get the preview of a campaign builder template.
1024 *
1025 * @since 1.8.0
1026 *
1027 * @param string|bool $template_id The ID of the template to preview.
1028 * @param string|bool $theme The theme to use for the preview.
1029 * @param array|bool $campaign_data The data to use for the campaign.
1030 * @param bool $exclude_wrappers Whether to exclude the template wrappers or not.
1031 *
1032 * @return string The HTML preview of the template.
1033 */
1034 public function get_template_preview( $template_id = false, $theme = false, $campaign_data = false, $exclude_wrappers = false ) {
1035
1036 if ( false === $template_id ) {
1037 return false;
1038 }
1039
1040 ob_start();
1041
1042 $this->field_id = 0;
1043 $column_counter = 0;
1044 $element_counter = 0;
1045 $section_counter = 0;
1046 $field_id = 0;
1047 $last_field_id = 0;
1048
1049 $layout = isset( $theme['layout'] ) ? $theme['layout'] : false;
1050
1051 // if this is a "default" theme or there's no layout (odd) then display the "no fields preview".
1052 $no_preview_css_class = ( empty( $layout ) || ! is_array( $layout ) || ( ! empty( $theme['meta']['template_type'] ) && is_array( $theme['meta']['template_type'] ) && in_array( 'default', $theme['meta']['template_type'], true ) ) ) ? false : 'charitable-hidden';
1053 $design_wrap_css_class = ( false === $no_preview_css_class ) ? ' no-fields-mode ' : false;
1054
1055 if ( false !== $exclude_wrappers ) {
1056 ?>
1057
1058 <div>
1059 <div>
1060
1061 <?php } else { ?>
1062
1063 <div class="charitable-design-wrap<?php echo esc_attr( $design_wrap_css_class ); ?>" id="charitable-design-wrap">
1064
1065 <?php /* loading screen, sometimes needed for color previewing */ ?>
1066
1067 <div class="charitable-campaign-preview-loader"><i class="charitable-loading-spinner charitable-loading-black charitable-loading-inline"></i></div>
1068
1069 <div class="charitable-campaign-preview charitable-campaign-preview-container">
1070
1071 <?php } ?>
1072
1073 <?php
1074
1075 if ( ! empty( $layout ) && is_array( $layout ) ) {
1076
1077 foreach ( $layout as $row ) {
1078
1079 $additional_css = ! empty( $row['css_class'] ) ? esc_attr( $row['css_class'] ) : '';
1080 $row_fields = ! empty( $row['fields'] ) ? $row['fields'] : false;
1081 $row_bg_image = ! empty( $row['background_image'] ) ? $row['background_image'] : '';
1082 $row_locked = ! empty( $row['locked'] );
1083
1084 echo $this->get_template_element_start( $row['type'], $element_counter, $additional_css, $campaign_data, $row_bg_image, $row_locked ); // phpcs:ignore
1085
1086 $tabs_rendered = false;
1087
1088 foreach ( $row['columns'] as $column ) {
1089
1090 echo '<!-- column START -->';
1091
1092 echo '<div data-column-id="' . intval( $column_counter ) . '" class="column charitable-field-column">';
1093
1094 foreach ( $column as $section ) {
1095
1096 echo '<!-- section START -->';
1097
1098 ob_start();
1099
1100 switch ( $section['type'] ) {
1101 case 'fields':
1102 if ( $tabs_rendered ) {
1103 ++$last_field_id;
1104 }
1105 $last_field_id = (int) $this->render_fields( $section['fields'], $theme, $campaign_data, $last_field_id, $row_locked );
1106 break;
1107 case 'header':
1108 $last_field_id = (int) $this->render_fields( $section['fields'], $theme, $campaign_data, $last_field_id, $row_locked );
1109 break;
1110 case 'tabs':
1111 echo $this->get_template_element_start( 'tabs', null, null, $campaign_data ); // phpcs:ignore
1112 $campaign_tabs = ! empty( $campaign_data['tabs'] ) ? $campaign_data['tabs'] : array();
1113 echo $this->get_template_tab_nav( $section['tabs'], $theme, $campaign_tabs ); // phpcs:ignore
1114 $last_field_id = (int) $this->get_template_tab_content( $section['tabs'], $theme, $campaign_data, $row_fields, $last_field_id );
1115 echo $this->get_template_element_end( 'tabs' ); // phpcs:ignore
1116 $tabs_rendered = true;
1117 break;
1118 default:
1119 do_action( 'charitable_campaign_builder_preview_section_' . $section['type'], $section, $row, $theme, $campaign_data );
1120 break;
1121 }
1122
1123 $field_html = ob_get_clean();
1124
1125 $section_css_class = ( '' === $field_html && ( empty( $row['css_class'] ) || strpos( $row['css_class'], 'no-field-target' ) === false ) ) ? 'charitable-field-target' : '';
1126 $section_css_class .= ( empty( $row['css_class'] ) || strpos( $row['css_class'], 'no-field-wrap' ) === false ) ? ' charitable-field-wrap' : '';
1127
1128 echo '<div data-section-id="' . intval( $section_counter ) . '" data-section-type="' . esc_attr( $section['type'] ) . '" class="section charitable-field-section ' . esc_attr( $section_css_class ) . '">';
1129
1130 echo '<div class="charitable-drag-new-block-here"><p>Drag New Block Here.</p></div>';
1131
1132 echo $field_html; // phpcs:ignore
1133
1134 ++$section_counter;
1135
1136 echo '</div>';
1137
1138 echo '<!-- section END -->';
1139 }
1140
1141 ++$column_counter;
1142
1143 echo '</div>';
1144
1145 echo '<!-- column END -->';
1146 }
1147
1148 echo $this->get_template_element_end( $row['type'], $element_counter ); // phpcs:ignore
1149
1150 ++$element_counter;
1151 }
1152 }
1153
1154 return ob_get_clean();
1155 }
1156
1157 /**
1158 * Returns the default start of a column.
1159 *
1160 * @return string The HTML markup for the start of a column.
1161 */
1162 public function get_default_column_start() {
1163
1164 ob_start();
1165
1166 ?>
1167
1168 <div data-column-id="0" class="column charitable-field-column">
1169
1170 <?php
1171
1172 return ob_get_clean();
1173 }
1174
1175 /**
1176 * Returns the default end of a column.
1177 *
1178 * @return string The HTML markup for the end of a column.
1179 */
1180 public function get_default_column_end() {
1181
1182 ob_start();
1183
1184 ?>
1185
1186 </div>
1187
1188 <?php
1189
1190 return ob_get_clean();
1191 }
1192
1193 /**
1194 * Output the 'fields' or 'blocks' for the template.
1195 *
1196 * @since 1.8.0
1197 *
1198 * @param string $template_id Template slug.
1199 * @param string $theme The current theme.
1200 * @param array $campaign_data The campaign data.
1201 *
1202 * @return string
1203 */
1204 public function get_template_field_options( $template_id = false, $theme = false, $campaign_data = false ) {
1205
1206 ob_start();
1207
1208 $field_id = 0;
1209 $layout = (array) $theme['layout'];
1210
1211 // If there is no description for this campaign, then this (assumingly) must be a new campaign and we should check and see if there's a description that comes with the template.
1212 if ( false !== $template_id && ! isset( $campaign_data['settings']['general']['description'] ) ) {
1213
1214 if ( ! empty( $this->templates_data['templates'][ $template_id ]['settings']['general']['description'] ) ) {
1215 $campaign_data['settings']['general']['description'] = $this->templates_data['templates'][ $template_id ]['settings']['general']['description'];
1216 }
1217 }
1218
1219 foreach ( $layout as $row ) {
1220 foreach ( $row['columns'] as $column ) {
1221 foreach ( $column as $section_key => $section ) {
1222
1223 switch ( $section['type'] ) {
1224 case 'fields':
1225 foreach ( $section['fields'] as $key => $field_type_data ) :
1226
1227 $field_type = is_array( $field_type_data ) && ! empty( $field_type_data['type'] ) ? esc_attr( $field_type_data['type'] ) : esc_attr( $key );
1228
1229 $type = 'Charitable_Field_' . str_replace( ' ', '_', ( ucwords( str_replace( '-', ' ', $field_type ) ) ) );
1230
1231 if ( is_array( $field_type_data ) ) {
1232 $campaign_data['fields'][ $field_id ] = $field_type_data;
1233 }
1234
1235 if ( class_exists( $type ) ) :
1236 $class = new $type();
1237 if ( method_exists( $class, 'settings_display' ) ) {
1238 echo $class->settings_display( $field_id, $campaign_data ); // phpcs:ignore --- second param was $this->campaign_data, but that isn't available.
1239 }
1240 endif;
1241 ++$field_id;
1242 endforeach;
1243 break;
1244 case 'tabs':
1245 foreach ( $section['tabs'] as $tab_index => $tab ) :
1246 foreach ( $tab['fields'] as $key => $field_type_data ) :
1247
1248 $field_type = is_array( $field_type_data ) && ! empty( $field_type_data['type'] ) ? esc_attr( $field_type_data['type'] ) : esc_attr( $key );
1249
1250 // header.
1251 $type = 'Charitable_Field_' . str_replace( ' ', '_', ( ucwords( str_replace( '-', ' ', $field_type ) ) ) );
1252
1253 if ( is_array( $field_type_data ) ) {
1254 $campaign_data['fields'][ $field_id ] = $field_type_data;
1255 }
1256
1257 if ( class_exists( $type ) ) :
1258 $class = new $type();
1259 if ( method_exists( $class, 'settings_display' ) ) {
1260 echo $class->settings_display( $field_id, $campaign_data ); // phpcs:ignore --- second param was $this->campaign_data, but that isn't available.
1261 }
1262 endif;
1263
1264 ++$field_id;
1265
1266 endforeach;
1267 endforeach;
1268 break;
1269 default:
1270 break;
1271 }
1272 }
1273 }
1274 }
1275
1276 return ob_get_clean();
1277 }
1278
1279 /**
1280 * Output tab options for the left sidebar.
1281 *
1282 * @since 1.8.0
1283 *
1284 * @param integer $template_id Tab info.
1285 * @param string $theme The current theme.
1286 * @param array $campaign_data Campaign data.
1287 *
1288 * @return string
1289 */
1290 public function get_template_tab_options( $template_id = false, $theme = false, $campaign_data = false ) { // phpcs:ignore
1291
1292 ob_start();
1293
1294 $max_tab_title_length = apply_filters( 'charitable_builder_design_tab_title_length', 20 );
1295
1296 $layout = (array) $theme['layout'];
1297 $tabs = array();
1298
1299 // Currently only one tab area per template, so we need to locate that tab area (if it exists) in the layout.
1300 foreach ( $layout as $row ) {
1301 foreach ( $row['columns'] as $column ) {
1302 foreach ( $column as $section ) {
1303 if ( $section['type'] === 'tabs' && ! empty( $section['tabs'] ) ) {
1304 $tabs = $section['tabs'];
1305 continue;
1306 }
1307 }
1308 }
1309 }
1310
1311 $number_of_tabs = count( $tabs );
1312 $hide_tab_nav_disabled = $number_of_tabs > 1 ? 'disabled="disabled"' : false;
1313
1314 ?>
1315
1316 <?php $enable_tabs = 'enabled'; ?>
1317
1318 <div data-field-id="" id="charitable-group-row-design-layout-options-tabs-enable" class="charitable-panel-field charitable-panel-field-toggle " data-ajax-label="enable_tabs">
1319 <span class="charitable-toggle-control">
1320 <input type="checkbox" id="charitable-panel-field-settings-charitable-campaign-enable-tabs" name="layout__advanced__enable_tabs" data-advanced-field-id="enable_tabs" value="disabled" <?php checked( $enable_tabs, 'disabled' ); ?> />
1321 <label class="charitable-toggle-control-icon" for="charitable-panel-field-settings-charitable-campaign-enable-tabs"></label>
1322 <label for="charitable-panel-field-settings-charitable-campaign-enable-tabs">Hide tab section. </label>
1323 <?php echo charitable_get_tooltip_html( esc_html__( 'Remove tabs from your public campaign page but not delete any items in the preview.', 'charitable' ), 'tooltipstered' ); // phpcs:ignore ?>
1324 </span>
1325 </div>
1326
1327 <hr />
1328
1329 <!-- group -->
1330 <div class="charitable-group charitable-layout-options-tab-group charitable-closed charitable-new-tab hidden" data-group_id="2">
1331 <div class="charitable-general-layout-heading" data-group="general-layout-tab">
1332 <a href="#" class="charitable-draggable"><i class="fa fa-bars"></i></a>
1333 <span>[ <?php echo esc_html__( 'New Tab', 'charitable' ); ?> ]</span>
1334 <a href="#" class="charitable-toggleable-group"><i class="fa fa-angle-down charitable-angle-down"></i></a>
1335 <a href="#" class="charitable-tab-group-delete" title="Delete Tab"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
1336
1337 </div>
1338 <!-- rows -->
1339 <div class="charitable-group-rows">
1340 <!-- row -->
1341 <div class="charitable-group-row charitable-tab-title-row" id="tabs_xxx_row_title" data-field-id="">
1342 <label for=""><?php echo esc_html__( 'Title', 'charitable' ); ?> <?php echo charitable_get_tooltip_html( false, 'tooltipstered' ); // phpcs:ignore ?>
1343 <input type="text" class="" id="tabs_xxx_title" name="tabs__xxx__title" value="" placeholder="<?php echo esc_html__( 'Tab Title', 'charitable' ); ?> " maxlength="<?php echo intval( $max_tab_title_length ); ?>">
1344 </div>
1345 <div class="charitable-group-row charitable-tab-title-row" id="tabs_xxx_row_visible_nav" data-field-id="">
1346 <span class="charitable-toggle-control">
1347 <input type="checkbox" id="tabs_xxx_visible_nav" class="charitable-settings-tab-visible-nav" name="tabs__xxx__visible_nav" value="invisible" <?php echo esc_attr( $hide_tab_nav_disabled ); ?> />
1348 <label class="charitable-toggle-control-icon" for="tabs_xxx_visible_nav"></label>
1349 <label for="tabs_xxx_visible_nav"><?php echo esc_html__( 'Hide tab navigation.', 'charitable' ); ?> </label>
1350 <?php echo charitable_get_tooltip_html( esc_html__( 'Hide the tab in the navigation bar (only if you have one tab in your design).', 'charitable' ), 'tooltipstered' ); // phpcs:ignore ?>
1351 </span>
1352 </div>
1353 </div>
1354 <!-- end rows -->
1355 </div> <!-- end group -->
1356 <?php
1357
1358 if ( $tabs ) {
1359
1360 $counter = 0;
1361
1362 foreach ( $tabs as $tab_id => $tab_info ) :
1363
1364 $group_id = ( strtolower( $tab_id ) === 'campaign' ) ? 0 : $tab_id;
1365 $group_name_var = ( $tab_id === 'campaign' ) ? $group_id : $group_id;
1366 $group_title = isset( $tab_info['title'] ) && '' !== trim( $tab_info['title'] ) ? $tab_info['title'] : '[ New Tab ]';
1367 $group_header = ( $tab_id === 'campaign' ) ? 'Campaign' : $group_title;
1368 $group_desc = isset( $tab_info['desc'] ) && '' !== trim( $tab_info['desc'] ) ? $tab_info['desc'] : false;
1369 $group_classes = isset( $tab_info['title'] ) && '' !== trim( $tab_info['title'] ) ? false : 'charitable-new-tab';
1370 $data_group = ( $tab_id === 'campaign' ) ? 'general-layout-tab' : 'general-layout-tab'; // general-layout-campaign.
1371 $active = $counter === 0 ? 'active' : false;
1372 $closed = $counter === 0 ? 'charitable-open' : 'charitable-closed';
1373 $arrow_icon = ! $active ? 'fa fa-angle-down charitable-angle-right' : 'fa fa-angle-down charitable-angle-down';
1374 $type = ( isset( $tab_info['type'] ) && '' !== trim( $tab_info['type'] ) ) ? $tab_info['type'] : 'html';
1375 $row_class = 'html' !== $type ? 'hidden' : false;
1376
1377 $visible_nav_checked = ! empty( $tab_info['visible_nav'] ) && 'invisible' === $tab_info['visible_nav'] ? 'checked="checked"' : '';
1378 $visible_nav_checked = ( false !== $hide_tab_nav_disabled ) ? '' : $visible_nav_checked;
1379 $disabled_css = ( false !== $hide_tab_nav_disabled ) ? 'charitable-disabled' : false;
1380
1381 ?>
1382
1383 <!-- group -->
1384 <div class="charitable-group charitable-layout-options-tab-group <?php echo esc_attr( $group_classes ); ?> <?php echo esc_attr( $closed ); ?> <?php echo esc_attr( $active ); ?>" data-group_id="<?php echo esc_attr( $group_id ); ?>">
1385 <div class="charitable-general-layout-heading" data-group="<?php echo esc_attr( $data_group ); ?>">
1386 <a href="#" class="charitable-draggable"><i class="fa fa-bars"></i></a>
1387 <span><?php echo esc_html( $group_header ); ?></span>
1388 <a href="#" class="charitable-toggleable-group"><i class="<?php echo esc_attr( $arrow_icon ); ?>"></i></a>
1389 <a href="#" class="charitable-tab-group-delete" title="<?php esc_attr_e( 'Delete Tab', 'charitable' ); ?>"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
1390
1391 </div>
1392 <!-- rows -->
1393 <div class="charitable-group-rows">
1394 <div class="charitable-group-row charitable-tab-title-row" id="row_tabs__<?php echo esc_attr( $group_name_var ); ?>__title" data-tab-id="<?php echo esc_attr( $group_name_var ); ?>">
1395 <label for=""><?php echo esc_html__( 'Title', 'charitable' ); ?> <?php echo charitable_get_tooltip_html( false, 'tooltipstered' ); // phpcs:ignore ?>
1396 <input type="text" class="" id="" name="tabs__<?php echo esc_attr( $group_name_var ); ?>__title" value="<?php echo esc_attr( $group_title ); ?>" placeholder="" maxlength="<?php echo intval( $max_tab_title_length ); ?>">
1397 </div>
1398 <div class="charitable-group-row charitable-tab-title-row" id="row_tabs__<?php echo esc_attr( $group_name_var ); ?>__visible_nav" data-tab-id="<?php echo esc_attr( $group_name_var ); ?>">
1399 <span class="charitable-toggle-control">
1400 <input type="checkbox" id="charitable-panel-field-settings-charitable-campaign-<?php echo esc_attr( $group_name_var ); ?>__visible_nav" class="charitable-settings-tab-visible-nav" name="tabs__<?php echo esc_attr( $group_name_var ); ?>__visible_nav" value="invisible" <?php echo esc_attr( $visible_nav_checked ); ?> <?php echo esc_attr( $visible_nav_checked ); ?> />
1401 <label class="charitable-toggle-control-icon <?php echo esc_attr( $disabled_css ); ?>" for="charitable-panel-field-settings-charitable-campaign-<?php echo esc_attr( $group_name_var ); ?>__visible_nav"></label>
1402 <label class="<?php echo esc_attr( $disabled_css ); ?>" for="charitable-panel-field-settings-charitable-campaign-<?php echo esc_attr( $group_name_var ); ?>__visible_nav">Hide tab navigation. </label>
1403 <?php echo charitable_get_tooltip_html( esc_html__( 'Hide the tab in the navigation bar (only if you have one tab in your design).', 'charitable' ), 'tooltipstered' ); // phpcs:ignore ?>
1404 </span>
1405 </div>
1406 </div>
1407 <!-- end rows -->
1408 </div> <!-- end group -->
1409
1410 <?php ++$counter; ?>
1411
1412 <?php
1413 endforeach;
1414
1415 } else {
1416 ?>
1417
1418 <!-- group -->
1419 <div class="charitable-group charitable-layout-options-tab-group charitable-layout-options-general-group active" data-group_id="0"> <!-- open or closed -->
1420 <a href="#" class="charitable-general-layout-heading charitable-toggleable-group" data-group="general-layout-campaign">
1421 <span><?php echo esc_html__( 'Campaign', 'charitable' ); ?></span>
1422 <i class="fa fa-angle-down"></i>
1423 </a>
1424 <!-- rows -->
1425 <div class="charitable-group-rows">
1426 <input type="hidden" name="tabs__campaign__type" value="html" />
1427 <!-- row -->
1428 <div class="charitable-group-row" id="" data-field-id="">
1429 <label for="">Title <?php echo charitable_get_tooltip_html( false, 'tooltipstered' ); // phpcs:ignore ?>
1430 <input type="text" class="" id="charitable-field-option-4-name" name="tabs__campaign__title" value="" placeholder="">
1431 </div>
1432 <!-- row -->
1433 <div class="charitable-group-row" id="" data-field-id="">
1434 <label for="">Description <?php echo charitable_get_tooltip_html( false, 'tooltipstered' ); // phpcs:ignore ?>
1435 <textarea class="" id="" name="tabs__campaign__desc" rows="3"></textarea>
1436 </div>
1437 </div>
1438 <!-- end rows -->
1439 </div> <!-- end group -->
1440 <?php } ?>
1441
1442 <!-- button? -->
1443 <button class="charitable-tab-groups-add charitable-btn charitable-btn-sm"><?php echo esc_html__( '+ Add New Tab', 'charitable' ); ?></button>
1444 <!-- end button -->
1445
1446 <?php
1447 return ob_get_clean();
1448 }
1449
1450 /**
1451 * Retreive data of a particular theme.
1452 *
1453 * @since 1.8.0
1454 *
1455 * @param string $template_id Template slug.
1456 * @param array $campaign_data Campaign data.
1457 * @param bool $no_cache Whether to bypass the cache or not. True to bypass the cache. False to use the cache. Default is false.
1458 *
1459 * @return array
1460 */
1461 public function get_template_data( $template_id = false, $campaign_data = array(), $no_cache = false ) {
1462
1463 if ( false === $template_id ) {
1464 $template_id = charitable_campaign_builder_default_template();
1465 }
1466
1467 $templates_data = $this->get_templates_data( $template_id, $campaign_data, $no_cache );
1468 $template_data = ! empty( $templates_data['templates'][ $template_id ] ) ? (array) $templates_data['templates'][ $template_id ] : false;
1469
1470 return $template_data;
1471 }
1472
1473 /**
1474 * Retreive data of all available campaign builder templates from the database (or generate).
1475 *
1476 * @since 1.8.0
1477 * @since 1.8.1.3 Added $no_cache parameter.
1478 *
1479 * @param string $template_id Template slug.
1480 * @param array $campaign_data Campaign data.
1481 * @param bool $no_cache Whether to bypass the cache or not. True to bypass the cache. False to use the cache. Default is false.
1482 *
1483 * @return array
1484 */
1485 public function get_templates_data( $template_id = false, $campaign_data = array(), $no_cache = false ) {
1486
1487 $templates_data = $no_cache || ( charitable_is_debug() ) || ( defined( 'CHARITABLE_BUILDER_NO_CACHE_TEMPLATE' ) && CHARITABLE_BUILDER_NO_CACHE_TEMPLATE ) ? false : get_option( 'charitable_campaign_builder_templates' );
1488
1489 // Invalidate caches written by an older plugin version so newly added templates appear after upgrade.
1490 if ( is_array( $templates_data ) && ( empty( $templates_data['cache_version'] ) || Charitable::VERSION !== $templates_data['cache_version'] ) ) {
1491 $templates_data = false;
1492 }
1493
1494 if ( empty( $templates_data ) ) {
1495
1496 // if we cannot locate, we will create the basic choices and add them into the WordPress option, while returning the data.
1497 $templates_data = $this->get_source_template_data();
1498
1499 foreach ( $templates_data['templates'] as $key => $values ) :
1500
1501 if ( is_admin() ) :
1502 $templates_data['templates'][ $key ]['preview'] = $this->get_template_preview( $key, $templates_data['templates'][ $key ], $campaign_data );
1503 $templates_data['templates'][ $key ]['field_options'] = $this->get_template_field_options( $key, $templates_data['templates'][ $key ], $campaign_data );
1504 $templates_data['templates'][ $key ]['tab_options'] = $this->get_template_tab_options( $key, $templates_data['templates'][ $key ], $campaign_data );
1505 $templates_data['templates'][ $key ]['advanced']['show_field_names'] = charitable_show_field_names_by_default( $key ) ? 'show' : 'hide';
1506 $templates_data['templates'][ $key ]['advanced']['preview_mode'] = charitable_show_preview_mode_by_default( $key ) ? 'normal' : 'minimum';
1507 endif;
1508 endforeach;
1509
1510 $templates_data['cache_version'] = Charitable::VERSION;
1511
1512 if ( ( charitable_is_debug() ) || ( defined( 'CHARITABLE_BUILDER_NO_CACHE_TEMPLATE' ) && CHARITABLE_BUILDER_NO_CACHE_TEMPLATE ) ) {
1513 delete_option( 'charitable_campaign_builder_templates', $templates_data );
1514 } else {
1515 // update the WordPress option.
1516 update_option( 'charitable_campaign_builder_templates', $templates_data );
1517 }
1518 }
1519
1520 return apply_filters( 'charitable_campaign_builder_template_data', $templates_data );
1521 }
1522
1523
1524 /**
1525 * Get the meta field data for a given template.
1526 *
1527 * @param string $field_name The name of the meta field.
1528 * @param int|bool $template_id The ID of the template to get the meta field data for.
1529 * @return mixed The value of the meta field.
1530 */
1531 public function get_template_data_meta_field( $field_name = '', $template_id = false ) {
1532
1533 if ( '' === trim( $field_name ) ) {
1534 return false;
1535 }
1536
1537 if ( false === $template_id ) {
1538 $template_id = charitable_campaign_builder_default_template();
1539 }
1540
1541 $template_data = $this->get_template_data( $template_id );
1542
1543 return ! empty( $template_data['meta'][ $field_name ] ) ? $template_data['meta'][ $field_name ] : false;
1544 }
1545
1546 /**
1547 * Out the HTML that does the display of items and sidebar search/categories of the builder.
1548 *
1549 * @since 1.8.0
1550 * @version 1.8.1.12 Broke off HTML into function to generate sections (blank vs templated).
1551 *
1552 * @return array
1553 */
1554 public function output_templates_panel() {
1555
1556 ob_start();
1557
1558 $current_template_id = false;
1559 $template_items = array(
1560 'single_column' => 0,
1561 'template' => 0,
1562 );
1563
1564 if ( isset( $_GET['campaign_id'] ) && 0 !== intval( $_GET['campaign_id'] ) ) { // phpcs:ignore
1565
1566 $template_data = charitable_get_template_data_from_campaign( abs( $_GET['campaign_id'] ) ); // phpcs:ignore
1567 $current_template_id = isset( $template_data['template_id'] ) ? $template_data['template_id'] : false;
1568
1569 }
1570
1571 $this->prepare_templates_data();
1572
1573 ?>
1574
1575 <div id="charitable-template-container" class="charitable-template-options">
1576
1577 <div class="charitable-setup-templates-sidebar">
1578
1579 <div class="charitable-setup-templates-search-wrap">
1580 <i class="fa fa-search"></i>
1581 <i class="fa fa-close"></i>
1582 <label>
1583 <input type="text" id="charitable-setup-template-search" value="" placeholder="Search Templates">
1584 </label>
1585 </div>
1586
1587 <ul class="charitable-setup-templates-categories">
1588 <?php $this->template_categories(); ?>
1589 </ul>
1590
1591 <div class="charitable-setup-templates-feedback">
1592 <h6><?php echo esc_html__( "Don't See What You're Looking For?", 'charitable' ); ?></h6>
1593 <p>
1594 <?php
1595 printf(
1596 /* translators: 1: Template ID */
1597 '<a href="#" class="send-feedback">%1$s</a> %2$s',
1598 esc_html__( 'Let us know', 'charitable' ),
1599 esc_html__( 'what templates to add in the future.', 'charitable' ) // no comma here to avoid fatal on old PHP versions (for now).
1600 );
1601 ?>
1602 </p>
1603
1604 </div>
1605
1606 </div>
1607
1608 <div id="charitable-template-list" class="charitable-template-list"> <!-- start template list -->
1609
1610 <div class="charitable-template-list-container">
1611
1612 <?php
1613
1614 if ( ! empty( $this->prepared_templates ) ) :
1615
1616 foreach ( $this->prepared_templates as $campaign_template_slug => $campaign_template_data ) :
1617
1618 if ( is_array( $campaign_template_data['meta']['template_type'] ) && array_intersect( array( 'blank' ), $campaign_template_data['meta']['template_type'] ) ) {
1619
1620 ++$template_items['single_column'];
1621
1622 } else {
1623
1624 ++$template_items['template'];
1625
1626 }
1627
1628 endforeach;
1629
1630 if ( $template_items['single_column'] > 0 ) :
1631
1632 echo '<div class="charitable-template-list-section charitable-template-list-section-blank">
1633 <h4>' . esc_html__( 'Start from scratch with a blank template...', 'charitable' ) . '</h4>
1634 </div>';
1635
1636 foreach ( $this->prepared_templates as $campaign_template_slug => $campaign_template_data ) :
1637
1638 $this->output_template_item( $campaign_template_slug, $campaign_template_data, array( 'blank' ), array(), $current_template_id );
1639
1640 endforeach;
1641
1642 endif;
1643
1644 if ( $template_items['template'] > 0 ) :
1645
1646 echo '<div class="charitable-template-list-section charitable-template-list-section-prebuilt">
1647 <h4>' . esc_html__( '...or select from a prebuilt template and be up and running in minutes!', 'charitable' ) . '</h4>
1648 </div>';
1649
1650 foreach ( $this->prepared_templates as $campaign_template_slug => $campaign_template_data ) :
1651
1652 $this->output_template_item( $campaign_template_slug, $campaign_template_data, false, array( 'blank' ), $current_template_id );
1653
1654 endforeach;
1655
1656 endif;
1657
1658 ?>
1659
1660 <?php else : ?>
1661
1662 <div class="charitable-error">
1663
1664 <p><?php echo esc_html__( 'Unable to locate templates for campaigns. Contact support.', 'charitable' ); ?></p>
1665
1666 </div>
1667
1668 <?php endif; ?>
1669
1670 </div>
1671
1672 <div class="charitable-templates-no-results" style="display: none;">
1673 <p><?php echo esc_html__( 'Sorry, we didn\'t find any templates that match your criteria.', 'charitable' ); ?></p>
1674 </div>
1675
1676 </div> <!-- end template list -->
1677
1678 </div>
1679
1680 <?php
1681
1682 return ob_get_clean();
1683 }
1684
1685 /**
1686 * HTML output for a single template item in the list.
1687 *
1688 * @since 1.8.1.12
1689 *
1690 * @param string $campaign_template_slug The template slug.
1691 * @param array $campaign_template_data The template data.
1692 * @param array $filter_include The categories to include.
1693 * @param array $filter_exclude The categories to exclude.
1694 * @param bool $current_template_id The current template ID.
1695 */
1696 private function output_template_item( $campaign_template_slug = '', $campaign_template_data = array(), $filter_include = array(), $filter_exclude = array(), $current_template_id = false ) {
1697
1698 $create_update_term = isset( $_GET['campaign_id'] ) && 0 !== intval( $_GET['campaign_id'] ) ? esc_html__( 'Restart Campaign', 'charitable' ) : esc_html__( 'Create Campaign', 'charitable' ); // phpcs:ignore
1699
1700 if ( '' === trim( $campaign_template_slug ) || empty( $campaign_template_data ) ) {
1701 return false;
1702 }
1703
1704 // If we are filtering for template categories and this template is not in the list ('template_type'), then return false.
1705 if ( ! empty( $filter_include ) && is_array( $campaign_template_data['meta']['template_type'] ) && ! empty( $campaign_template_data['meta']['template_type'] ) ) {
1706 // If none of the elements in the $filter_include array are in the template_type, return.
1707 if ( ! array_intersect( $filter_include, $campaign_template_data['meta']['template_type'] ) ) {
1708 return false;
1709 }
1710 }
1711
1712 // If we are filtering for items to exclude, then we will return false if any of the elements in the $filter_exclude array are in the template_type.
1713 if ( ! empty( $filter_exclude ) && is_array( $campaign_template_data['meta']['template_type'] ) && ! empty( $campaign_template_data['meta']['template_type'] ) ) {
1714 // If any of the elements in the $filter_exclude array are in the template_type, return.
1715 if ( array_intersect( $filter_exclude, $campaign_template_data['meta']['template_type'] ) ) {
1716 return false;
1717 }
1718 }
1719
1720 $primary = isset( $campaign_template_data['meta']['colors']['primary'] ) && ! empty( $campaign_template_data['meta']['colors']['primary'] ) ? esc_attr( $campaign_template_data['meta']['colors']['primary'] ) : false;
1721 $secondary = isset( $campaign_template_data['meta']['colors']['secondary'] ) && ! empty( $campaign_template_data['meta']['colors']['secondary'] ) ? esc_attr( $campaign_template_data['meta']['colors']['secondary'] ) : false;
1722 $tertiary = isset( $campaign_template_data['meta']['colors']['tertiary'] ) && ! empty( $campaign_template_data['meta']['colors']['tertiary'] ) ? esc_attr( $campaign_template_data['meta']['colors']['tertiary'] ) : false;
1723 $button_color = isset( $campaign_template_data['meta']['colors']['button_bg'] ) && ! empty( $campaign_template_data['meta']['colors']['button_bg'] ) ? esc_attr( $campaign_template_data['meta']['colors']['button_bg'] ) : false;
1724
1725 $template_categories = ! empty( $campaign_template_data['meta']['categories'] ) ? implode( ', ', $campaign_template_data['meta']['categories'] ) : false; // todo: santitize.
1726 $template_tags = ! empty( $campaign_template_data['meta']['search_tags'] ) ? implode( ', ', $campaign_template_data['meta']['search_tags'] ) : false; // todo: santitize.
1727 $suffixes = ! empty( $campaign_template_data['meta']['suffixes'] ) ? serialize( $campaign_template_data['meta']['suffixes'] ) : false; // todo: santitize.
1728
1729 $show_only_for_lite = ! empty( $campaign_template_data['dependencies']['is_lite'] ) ? true : false;
1730
1731 if ( charitable_is_pro() && $show_only_for_lite ) {
1732 return;
1733 }
1734
1735 if ( isset( $campaign_template_data['thumbnail_url'] ) && false !== $campaign_template_data['thumbnail_url'] ) {
1736
1737 $template_thumbnail_html = '<div class="charitable-template-thumbnail charitable-template-thumbnail-' . $campaign_template_slug . '" style="background-image: url(' . $campaign_template_data['thumbnail_url'] . ')"></div>';
1738
1739 } elseif ( file_exists( charitable()->get_path( 'assets', true ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/thumbnail.png' ) ) {
1740
1741 $template_thumbnail_html = '<div class="charitable-template-thumbnail charitable-template-thumbnail-' . $campaign_template_slug . '" style="background-image: url(' . charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $campaign_template_slug . '/thumbnail.png)"></div>';
1742
1743 } else {
1744
1745 $template_thumbnail_html = '<div class="charitable-template-thumbnail charitable-template-thumbnail-' . $campaign_template_slug . '" style="background-image: url(' . charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/feedback.png)"></div>';
1746
1747 }
1748
1749 $create_update_css = isset( $_GET['campaign_id'] ) && 0 !== intval( $_GET['campaign_id'] ) ? 'update-campaign' : 'create-campaign'; // phpcs:ignore
1750 $template_code = esc_attr( $campaign_template_slug );
1751 $template_preview_url = charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/' . $template_code . '/preview.png';
1752 $template_description = esc_html( wp_strip_all_tags( $campaign_template_data['meta']['description'] ) );
1753 $template_types = is_array( $campaign_template_data['meta']['template_type'] ) && ! empty( $campaign_template_data['meta']['template_type'] ) ? implode( ' ', $campaign_template_data['meta']['template_type'] ) : false;
1754 $template_types = ( false === $template_types && false !== $campaign_template_data['meta']['template_type'] ) ? esc_attr( $campaign_template_data['meta']['template_type'] ) : $template_types;
1755 ?>
1756
1757 <?php $is_new_class = ! empty( $campaign_template_data['meta']['is_new'] ) ? ' charitable-template-is-new' : ''; ?>
1758 <div class="charitable-template-list-container-item charitable-template-<?php echo esc_attr( $campaign_template_slug ); ?> <?php echo esc_attr( $template_types ); ?><?php echo esc_attr( $is_new_class ); ?>">
1759
1760 <div class="charitable-template
1761 <?php
1762 if ( $current_template_id && $campaign_template_slug === $current_template_id ) :
1763 ?>
1764 active<?php endif; ?>" data-template-code="<?php echo esc_attr( $template_code ); ?>" data-template-preview-url="<?php echo esc_url( $template_preview_url ); ?>" data-template-description="<?php echo esc_attr( $template_description ); ?>" data-template-label="<?php echo esc_html( $campaign_template_data['meta']['label'] ); ?>" data-template-type="<?php echo esc_attr( $template_types ); ?>" data-template-categories="<?php echo esc_attr( $template_categories ); ?>" data-template-tags="<?php echo esc_attr( $template_tags ); ?>" data-template-primary="<?php echo esc_attr( $primary ); ?>" data-template-secondary="<?php echo esc_attr( $secondary ); ?>" data-template-tertiary="<?php echo esc_attr( $tertiary ); ?>" data-template-button="<?php echo esc_attr( $button_color ); ?>" data-template-suffixes="<?php echo htmlspecialchars( $suffixes ); // phpcs:ignore ?>">
1765
1766 <div class="charitable-banner-container
1767 <?php
1768 if ( ! $current_template_id || $campaign_template_slug !== $current_template_id ) :
1769 ?>
1770 charitable-hidden<?php endif; ?>">
1771 <div class="charitable-banner"><?php echo esc_html__( 'Active', 'charitable' ); ?></div>
1772 </div>
1773
1774 <div class="template-thumbnail placeholder">
1775 <?php if ( 'lite-to-pro' !== $campaign_template_slug ) { ?>
1776 <div class="template-buttons
1777 <?php
1778 if ( $current_template_id && $campaign_template_slug === $current_template_id ) :
1779 ?>
1780 charitable-hidden<?php endif; ?>">
1781 <a href="#" class="button <?php echo esc_attr( $create_update_css ); ?>"><?php echo esc_html( $create_update_term ); ?></a>
1782 <?php if ( ! isset( $campaign_template_data['meta']['preview'] ) || false !== $campaign_template_data['meta']['preview'] ) : ?>
1783 <a href="#" class="button preview-campaign"><?php echo esc_html__( 'Preview', 'charitable' ); ?></a>
1784 <?php endif; ?>
1785 </div>
1786 <?php } else { ?>
1787 <div class="template-buttons">
1788 <a href="#" class="button send-feedback"><?php echo esc_html__( 'Send Feedback', 'charitable' ); ?></a>
1789 </div>
1790 <?php } ?>
1791
1792 <?php echo $template_thumbnail_html; // phpcs:ignore ?>
1793
1794 </div>
1795 <?php
1796 $template_label_html = esc_html( $campaign_template_data['meta']['label'] );
1797 if ( ! empty( $campaign_template_data['meta']['is_new'] ) ) {
1798 $template_label_html .= ' <span class="charitable-template-label-new">' . esc_html__( 'NEW', 'charitable' ) . '</span>';
1799 }
1800 printf( '<h4>%s</h4>', $template_label_html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1801 ?>
1802 <?php printf( '<p>%s</p>', $campaign_template_data['meta']['description'] ); // phpcs:ignore ?>
1803
1804 </div>
1805
1806 </div>
1807
1808 <?php
1809 }
1810
1811 /**
1812 * Prepare templates data for output.
1813 *
1814 * @since 1.8.0
1815 * @version 1.8.1.12
1816 */
1817 private function prepare_templates_data() {
1818
1819 $templates = $this->get_templates_data();
1820 $single_templates = array();
1821
1822 if ( empty( $templates ) || empty( $templates['templates'] ) ) {
1823 return;
1824 }
1825
1826 ksort( $templates['templates'] );
1827
1828 // move any "simple" template to the front.
1829 foreach ( $templates['templates'] as $template_name => $template_info ) :
1830 if ( ! empty( $template_info['meta']['template_type'] ) && is_array( $template_info['meta']['template_type'] ) && in_array( 'simple', $template_info['meta']['template_type'] ) ) {
1831 $single_templates['templates'][ $template_name ] = $template_info;
1832 unset( $templates['templates'][ $template_name ] );
1833 }
1834 endforeach;
1835
1836 ksort( $single_templates['templates'] );
1837
1838 // Pin the Beacon family right after Simple in a deterministic order
1839 // (2 Columns first, 1 Column second, then any future Beacon variants alphabetically).
1840 $beacon_priority = array( 'beacon-split', 'beacon-single' );
1841 $beacon_pinned = array();
1842 foreach ( $beacon_priority as $beacon_slug ) {
1843 if ( isset( $templates['templates'][ $beacon_slug ] ) ) {
1844 $beacon_pinned[ $beacon_slug ] = $templates['templates'][ $beacon_slug ];
1845 unset( $templates['templates'][ $beacon_slug ] );
1846 }
1847 }
1848 foreach ( $templates['templates'] as $template_name => $template_info ) {
1849 if ( 0 === strpos( $template_name, 'beacon-' ) ) {
1850 $beacon_pinned[ $template_name ] = $template_info;
1851 unset( $templates['templates'][ $template_name ] );
1852 }
1853 }
1854
1855 $templates['templates'] = array_merge( $single_templates['templates'], $beacon_pinned, $templates['templates'] );
1856
1857 // Loop through each available template.
1858 foreach ( $templates['templates'] as $id => $template ) {
1859
1860 $this->prepared_templates[ $id ] = $this->prepare_template_render_arguments( $template );
1861
1862 }
1863 }
1864
1865 /**
1866 * Prepare arguments for rendering template item.
1867 *
1868 * @since 1.8.0
1869 *
1870 * @param array $template Template data.
1871 *
1872 * @return array Arguments.
1873 */
1874 private function prepare_template_render_arguments( $template ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
1875
1876 return $template;
1877 }
1878
1879 /**
1880 * Generate and display categories menu.
1881 *
1882 * @since 1.8.0
1883 */
1884 public function template_categories() {
1885
1886 $templates_count = $this->get_count_in_categories();
1887
1888 $generic_categories = array(
1889 'all' => esc_html__( 'All Templates', 'charitable' ),
1890 );
1891
1892 if ( isset( $templates_count['all'], $templates_count['available'] ) && $templates_count['all'] !== $templates_count['available'] ) {
1893 $generic_categories['available'] = esc_html__( 'Available Templates', 'charitable' );
1894 }
1895
1896 $generic_categories['favorites'] = esc_html__( 'Favorite Templates', 'charitable' );
1897 $generic_categories['new'] = esc_html__( 'New Templates', 'charitable' );
1898 $generic_categories['single'] = esc_html__( 'Single', 'charitable' );
1899 $generic_categories['peer-to-peer'] = esc_html__( 'Peer-to-Peer', 'charitable' );
1900
1901 $generic_categories = apply_filters( 'charitable_campaign_builder_template_generic_categories', $generic_categories );
1902
1903 $this->output_categories( $generic_categories, $templates_count );
1904
1905 printf( '<li class="divider"></li>' );
1906
1907 $common_categories = array();
1908
1909 if ( $this->is_custom_templates_available ) {
1910 $common_categories['custom'] = esc_html__( 'Custom Templates', 'charitable' );
1911 }
1912
1913 if ( $this->is_addon_templates_available ) {
1914 $common_categories['addons'] = esc_html__( 'Addon Templates', 'charitable' );
1915 }
1916
1917 $categories = array_merge(
1918 $common_categories,
1919 $this->get_categories()
1920 );
1921
1922 $this->output_categories( $categories, $templates_count );
1923 }
1924
1925 /**
1926 * Output categories list.
1927 *
1928 * @since 1.8.0
1929 *
1930 * @param array $categories Categories list.
1931 * @param array $templates_count Templates count by categories.
1932 *
1933 * @return void
1934 */
1935 private function output_categories( $categories, $templates_count ) {
1936
1937 // Sidebar slugs that should display a small "NEW" pill next to the label.
1938 $new_category_slugs = apply_filters(
1939 'charitable_campaign_builder_template_new_category_slugs',
1940 array( 'structured-layouts' )
1941 );
1942
1943 foreach ( $categories as $slug => $name ) {
1944
1945 $class = '';
1946
1947 if ( $slug === 'all' ) {
1948 $class = ' class="active"';
1949 } elseif ( empty( $templates_count[ $slug ] ) ) {
1950 $class = ' class="charitable-hidden"';
1951 }
1952
1953 $count = isset( $templates_count[ $slug ] ) ? $templates_count[ $slug ] : '0';
1954
1955 $badge_html = in_array( $slug, $new_category_slugs, true )
1956 ? '<span class="charitable-template-category-new">' . esc_html__( 'NEW', 'charitable' ) . '</span>'
1957 : '';
1958
1959 printf(
1960 '<li data-category="%1$s"%2$s>%5$s%3$s<span>%4$s</span></li>',
1961 esc_attr( $slug ),
1962 $class, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1963 esc_html( $name ),
1964 esc_html( $count ),
1965 $badge_html // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1966 );
1967 }
1968 }
1969
1970 /**
1971 * Get categories data.
1972 *
1973 * @since 1.8.0
1974 *
1975 * @return array
1976 */
1977 public function get_categories() {
1978
1979 $this->categories = apply_filters(
1980 'charitable_campaign_builder_template_categories',
1981 array(
1982 'structured-layouts' => esc_html__( 'Structured Layouts', 'charitable' ),
1983 'animal-pets' => esc_html__( 'Animal / Pets', 'charitable' ),
1984 'club-organizations' => esc_html__( 'Club / Organizations', 'charitable' ),
1985 'environmental' => esc_html__( 'Environmental', 'charitable' ),
1986 'events' => esc_html__( 'Events', 'charitable' ),
1987 'family' => esc_html__( 'Family', 'charitable' ),
1988 'legal' => esc_html__( 'Legal', 'charitable' ),
1989 'medical' => esc_html__( 'Medical', 'charitable' ),
1990 'political' => esc_html__( 'Political', 'charitable' ),
1991 'special-occasions' => esc_html__( 'Special Occasions', 'charitable' ),
1992 'sports' => esc_html__( 'Sports', 'charitable' ),
1993 'youth' => esc_html__( 'Youth', 'charitable' ),
1994 )
1995 );
1996
1997 return $this->categories;
1998 }
1999
2000 /**
2001 * Get categories templates count.
2002 *
2003 * @since 1.8.0
2004 *
2005 * @return array
2006 */
2007 private function get_count_in_categories() {
2008
2009 $all_categories = array();
2010 $available_templates_count = 0;
2011 $favorites_templates_count = 0;
2012
2013 foreach ( $this->prepared_templates as $template_id => $template_data ) {
2014
2015 $template_meta = $template_data['meta'];
2016 $categories = $template_meta['categories'];
2017
2018 if ( $template_meta['has_access'] ) {
2019 ++$available_templates_count;
2020 }
2021
2022 if ( $template_meta['favorite'] ) {
2023 ++$favorites_templates_count;
2024 }
2025
2026 if ( is_array( $categories ) ) {
2027 // loop through $categories and add to $all_categories.
2028 foreach ( $categories as $category ) {
2029 $all_categories[] = $category;
2030 }
2031 continue;
2032 }
2033
2034 $all_categories[] = $categories;
2035 }
2036
2037 $categories_count = array_count_values( $all_categories );
2038 $categories_count['all'] = count( $this->prepared_templates );
2039 $categories_count['available'] = $available_templates_count;
2040 $categories_count['favorites'] = $favorites_templates_count;
2041
2042 return $categories_count;
2043 }
2044
2045 /**
2046 * Retrieves the source template data for the campaign builder, which hopefully ends up cached.
2047 *
2048 * @access private
2049 * @return array The source template data.
2050 */
2051 public function get_source_template_data() {
2052
2053 return array(
2054 'version' => '1.0.0',
2055 'date_added' => time(),
2056 'templates' => array(
2057 'simple-1-col' => array(
2058 'meta' => array(
2059 'label' => 'Simple 1 Column',
2060 'slug' => 'simple-1-column',
2061 'description' => 'A clean single-column starting point with the essentials. Drop in your story, photos, and donation form, then customize to match your cause.',
2062 'template_type' => array( 'single', 'simple', 'blank' ),
2063 'search_tags' => array(
2064 'default',
2065 'simple',
2066 'blank',
2067 'clean',
2068 ),
2069 'categories' => array(
2070 'default',
2071 ),
2072 'created' => '2023-10-05', // Y-m-d.
2073 'version' => '1.0',
2074 'author' => 'David Bisset',
2075 'dependencies' => false, // not used yet.
2076 'has_access' => true, // not used yet, but false if the user can't select it.
2077 'favorite' => false,
2078 'preview' => false, // show a "preview" button, default would be true.
2079 'colors' => array(
2080 'primary' => '#000000',
2081 'secondary' => '#2B66D1',
2082 'tertiary' => '#F99E36',
2083 'accent' => false,
2084 'button_bg' => '#5AA152',
2085 ),
2086 'parent_theme' => false, // not used yet.
2087 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2088 ),
2089 'layout' => array(
2090 array( // row.
2091 'type' => 'row',
2092 'columns' => array(
2093 array( // column.
2094 array( // section.
2095 'type' => 'fields',
2096 'fields' => array(),
2097 ),
2098 ),
2099 ),
2100 ),
2101 array( // row.
2102 'type' => 'row',
2103 'css_class' => 'no-padding no-field-target no-field-wrap',
2104 'columns' => array(
2105 array( // column.
2106 array( // section.
2107 'type' => 'tabs',
2108 'tabs' => array(
2109 array(
2110 'title' => 'Overview',
2111 'type' => '',
2112 'slug' => 'default',
2113 'fields' => array(),
2114 ),
2115 ),
2116 ),
2117 ),
2118 ),
2119 ),
2120 ),
2121 'advanced' => array(
2122 'tab_style' => 'boxed',
2123 'tab_size' => 'medium',
2124 ),
2125 'settings' => array(
2126 'general' => array(
2127 'description' => '<p><strong>This is a description of your campaign.</strong></p><p>Write one or two short paragraphs about what you want to accomplish and perhaps brief of origin of this campaign, past campaigns, and feature key people and events that will enspire people to give to your cause.</p>',
2128 ),
2129 ),
2130 ),
2131 'simple-2-col' => array(
2132 'meta' => array(
2133 'label' => 'Simple 2 Column',
2134 'slug' => 'simple-2-column',
2135 'description' => 'Two-column blank layout for storytelling on one side and donation form or media on the other. Ideal when you want side-by-side content without committing to a specific style.',
2136 'template_type' => array( 'single', 'simple', 'blank' ),
2137 'search_tags' => array(
2138 'default',
2139 'simple',
2140 'blank',
2141 'clean',
2142 ),
2143 'categories' => array(
2144 'default',
2145 ),
2146 'created' => '2023-10-05', // Y-m-d.
2147 'version' => '1.0',
2148 'author' => 'David Bisset',
2149 'dependencies' => false, // not used yet.
2150 'has_access' => true, // not used yet, but false if the user can't select it.
2151 'favorite' => false,
2152 'preview' => false, // show a "preview" button, default would be true.
2153 'colors' => array(
2154 'primary' => '#000000',
2155 'secondary' => '#2B66D1',
2156 'tertiary' => '#F99E36',
2157 'accent' => false,
2158 'button_bg' => '#5AA152',
2159 ),
2160 'parent_theme' => false, // not used yet.
2161 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2162 ),
2163 'layout' => array(
2164 array( // row.
2165 'type' => 'row',
2166 'columns' => array(
2167 array( // column.
2168 array( // section.
2169 'type' => 'fields',
2170 'fields' => array(),
2171 ),
2172 ),
2173 array( // column.
2174 array( // section.
2175 'type' => 'fields',
2176 'fields' => array(),
2177 ),
2178 ),
2179 ),
2180 ),
2181 array( // row.
2182 'type' => 'row',
2183 'css_class' => 'no-padding no-field-target no-field-wrap',
2184 'columns' => array(
2185 array( // column.
2186 array( // section.
2187 'type' => 'tabs',
2188 'tabs' => array(
2189 array(
2190 'title' => 'Overview',
2191 'type' => '',
2192 'slug' => 'default',
2193 'fields' => array(),
2194 ),
2195 ),
2196 ),
2197 ),
2198 ),
2199 ),
2200 ),
2201 'advanced' => array(
2202 'tab_style' => 'boxed',
2203 'tab_size' => 'medium',
2204 ),
2205 'settings' => array(
2206 'general' => array(
2207 'description' => '<p><strong>This is a description of your campaign.</strong></p><p>Write one or two short paragraphs about what you want to accomplish and perhaps brief of origin of this campaign, past campaigns, and feature key people and events that will enspire people to give to your cause.</p>',
2208 ),
2209 ),
2210 ),
2211 'simple-2-col-header' => array(
2212 'meta' => array(
2213 'label' => 'Simple 2 Column w/ header',
2214 'slug' => 'simple-2-column-header',
2215 'description' => 'Two-column layout with a banner header row up top for title and branding. Below, balance your campaign story alongside donation amounts, photos, or a donor wall.',
2216 'template_type' => array( 'single', 'simple', 'blank' ),
2217 'search_tags' => array(
2218 'default',
2219 'simple',
2220 'blank',
2221 'clean',
2222 ),
2223 'categories' => array(
2224 'default',
2225 ),
2226 'created' => '2023-10-05', // Y-m-d.
2227 'version' => '1.0',
2228 'author' => 'David Bisset',
2229 'dependencies' => false, // not used yet.
2230 'has_access' => true, // not used yet, but false if the user can't select it.
2231 'favorite' => false,
2232 'preview' => false, // show a "preview" button, default would be true.
2233 'colors' => array(
2234 'primary' => '#000000',
2235 'secondary' => '#2B66D1',
2236 'tertiary' => '#F99E36',
2237 'accent' => false,
2238 'button_bg' => '#5AA152',
2239 ),
2240 'parent_theme' => false, // not used yet.
2241 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2242 ),
2243 'layout' => array(
2244 array( // row.
2245 'type' => 'header',
2246 'css_class' => 'charitable-header',
2247 'columns' => array(
2248 array( // column.
2249 array( // section.
2250 'type' => 'fields',
2251 'fields' => array(),
2252 ),
2253 ),
2254 ),
2255 ),
2256 array( // row.
2257 'type' => 'row',
2258 'columns' => array(
2259 array( // column.
2260 array( // section.
2261 'type' => 'fields',
2262 'fields' => array(),
2263 ),
2264 ),
2265 array( // column.
2266 array( // section.
2267 'type' => 'fields',
2268 'fields' => array(),
2269 ),
2270 ),
2271 ),
2272 ),
2273 array( // row.
2274 'type' => 'row',
2275 'css_class' => 'no-padding no-field-target no-field-wrap',
2276 'columns' => array(
2277 array( // column.
2278 array( // section.
2279 'type' => 'tabs',
2280 'tabs' => array(
2281 array(
2282 'title' => 'Overview',
2283 'type' => '',
2284 'slug' => 'default',
2285 'fields' => array(),
2286 ),
2287 ),
2288 ),
2289 ),
2290 ),
2291 ),
2292 ),
2293 'advanced' => array(
2294 'tab_style' => 'boxed',
2295 'tab_size' => 'medium',
2296 ),
2297 'settings' => array(
2298 'general' => array(
2299 'description' => '<p><strong>This is a description of your campaign.</strong></p><p>Write one or two short paragraphs about what you want to accomplish and perhaps brief of origin of this campaign, past campaigns, and feature key people and events that will enspire people to give to your cause.</p>',
2300 ),
2301 ),
2302 ),
2303 'youth-sports' => array(
2304 'meta' => array(
2305 'label' => 'Youth Sports',
2306 'slug' => 'youth-sports',
2307 'description' => 'Accept donations for a local school\'s sports or athletics team. Designed for spirit-driven fundraisers covering uniforms, equipment, travel costs, and seasonal program support.',
2308 'template_type' => array( 'single', 'prebuilt' ),
2309 'search_tags' => array(
2310 'youth',
2311 'sports',
2312 'blue',
2313 ),
2314 'categories' => array(
2315 'sports',
2316 'youth',
2317 'special-occasions',
2318 ),
2319 'created' => '2023-10-05', // Y-m-d.
2320 'version' => '1.0',
2321 'author' => 'David Bisset',
2322 'dependencies' => false, // not used yet.
2323 'has_access' => true, // not used yet, but false if the user can't select it.
2324 'favorite' => false,
2325 'colors' => array(
2326 'primary' => '#102963',
2327 'secondary' => '#0D0D0D',
2328 'tertiary' => '#989898',
2329 'accent' => false,
2330 'button_bg' => '#ec5f25',
2331 ),
2332 'parent_theme' => false, // not used yet.
2333 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2334 ),
2335 'layout' => array(
2336 array( // row.
2337 'type' => 'row',
2338 'columns' => array(
2339 array( // column.
2340 array( // section.
2341 'type' => 'fields',
2342 'fields' => array(
2343 array(
2344 'type' => 'photo',
2345 'default' => 'photo.jpg',
2346 ),
2347 array(
2348 'type' => 'campaign-summary',
2349 ),
2350 ),
2351 ),
2352 ),
2353 array(
2354 array( // section.
2355 'type' => 'fields',
2356 'fields' => array(
2357 array(
2358 'type' => 'campaign-title',
2359 ),
2360 array(
2361 'type' => 'progress-bar',
2362 'show_hide' => array(
2363 'show_donated' => true,
2364 'show_goal' => true,
2365 ),
2366 'css_class' => 'sample-css-class',
2367 'label_donate' => ' ',
2368 'label_goal' => 'Goal USD: ',
2369 'meta_position' => 'top',
2370 ),
2371 array(
2372 'type' => 'donate-amount',
2373 'headline' => 'Select An Amount',
2374 ),
2375 array(
2376 'type' => 'donate-button',
2377 'button_label' => 'Donate Now',
2378 ),
2379 ),
2380 ),
2381 ),
2382 ),
2383 ),
2384 array( // row.
2385 'type' => 'row',
2386 'css_class' => 'no-padding no-field-target no-field-wrap',
2387 'columns' => array(
2388 array( // column.
2389 array( // section.
2390 'type' => 'tabs',
2391 'tabs' => array(
2392 array(
2393 'title' => 'Overview',
2394 'type' => '',
2395 'slug' => 'default',
2396 'fields' => array(
2397 array(
2398 'type' => 'campaign-description',
2399 'headline' => 'Make every minute on the field count ⚾',
2400 'content' => '<p>We are announcing a campaign to bolster our local youth sports team, comprised of enthusiastic youngsters aged 8-12. Your support will enable us to update their uniforms, provide essential supplies, ensure a safe playing environment, and offer security measures on the field. Additionally, your contributions will go towards accommodations for parents and teachers, fostering a supportive community atmosphere where these young athletes can thrive. Join us in nurturing the next generation of athletes, empowering them with the resources and encouragement they need to excel both on and off the field.</p><p>Together, let\'s create a positive and inspiring environment for our youth, fostering a love for sports and teamwork that will last a lifetime.😄</p>',
2401 ),
2402 array(
2403 'type' => 'ambassadors-team',
2404 'headline' => 'Top Team Members',
2405 'columns' => 6,
2406 'upgrade-img-bg' => 'field-team.jpg',
2407 ),
2408 ),
2409 ),
2410 array(
2411 'title' => 'Updates',
2412 'type' => '',
2413 'slug' => 'updates',
2414 'fields' => array(
2415 'updates-main' => array(
2416 'headline' => 'Top Team Members',
2417 'columns' => 6,
2418 ),
2419 ),
2420 ),
2421 array(
2422 'title' => 'Comments',
2423 'type' => '',
2424 'slug' => 'comments',
2425 'fields' => array(
2426 'comments-main' => array(
2427 'headline' => 'Top Team Members',
2428 'columns' => 6,
2429 ),
2430 ),
2431 ),
2432 ),
2433 ),
2434 ),
2435 ),
2436 ),
2437 ),
2438 'advanced' => array(
2439 'tab_style' => 'minimum',
2440 'tab_size' => 'small',
2441 ),
2442 'settings' => array(
2443 'general' => array(
2444 'description' => '<p>We are announcing a campaign to bolster our local youth sports team, comprised of enthusiastic youngsters aged 8-12. Your support will enable us to update their uniforms, provide essential supplies, ensure a safe playing environment, and offer security measures on the field. Additionally, your contributions will go towards accommodations for parents and teachers, fostering a supportive community atmosphere where these young athletes can thrive. Join us in nurturing the next generation of athletes, empowering them with the resources and encouragement they need to excel both on and off the field.</p><p>Together, let\'s create a positive and inspiring environment for our youth, fostering a love for sports and teamwork that will last a lifetime.😄</p>',
2445 ),
2446 ),
2447 ),
2448 'school-trip' => array(
2449 'meta' => array(
2450 'label' => 'School Trip',
2451 'slug' => 'school-trip',
2452 'description' => 'Raise funds for a school trip or class fundraiser. Tells the story behind the trip, breaks down costs, and lets parents and community members chip in toward the goal.',
2453 'template_type' => array( 'single', 'prebuilt' ),
2454 'search_tags' => array(
2455 'youth',
2456 'sports',
2457 'green',
2458 'brown',
2459 ),
2460 'categories' => array(
2461 'sports',
2462 'youth',
2463 'events',
2464 ),
2465 'created' => '2023-10-05', // Y-m-d.
2466 'version' => '1.0',
2467 'author' => 'David Bisset',
2468 'dependencies' => false, // not used yet.
2469 'has_access' => true, // not used yet, but false if the user can't select it.
2470 'favorite' => false,
2471 'colors' => array(
2472 'primary' => '#7A8347',
2473 'secondary' => '#000000',
2474 'tertiary' => '#5F5F5F',
2475 'accent' => false,
2476 'button_bg' => '#7A8347',
2477 ),
2478 'parent_theme' => false, // not used yet.
2479 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2480 'suffixes' => array(
2481 'social-links' => '-white',
2482 'social-sharing' => '-white',
2483 ),
2484 ),
2485 'layout' => array(
2486 array( // row.
2487 'type' => 'header',
2488 'css_class' => 'charitable-header',
2489 'columns' => array(
2490 array( // column.
2491 array( // section.
2492 'type' => 'fields',
2493 'fields' => array(
2494 array(
2495 'type' => 'text',
2496 'headline' => 'Help the youth learn with an adventure',
2497 'content' => 'Join us in giving our students the opportunity to explore, learn, and grow on this incredible journey!',
2498 'align' => 'center',
2499 ),
2500 ),
2501 ),
2502 ),
2503 ),
2504 ),
2505 array( // row.
2506 'type' => 'row',
2507 'columns' => array(
2508 array( // column.
2509 array( // section.
2510 'type' => 'fields',
2511 'fields' => array(
2512 array(
2513 'type' => 'campaign-description',
2514 'headline' => 'Raise fund for our school trip!',
2515 'content' => '<p>We are announcing a campaign to make the dream of a once-in-a-lifetime field trip come true for our graduating 5th graders: an unforgettable adventure in California! Your support will enable us to purchase necessary supplies, ensure safe travel, and provide accommodations for both parents and teachers accompanying the students. With your contribution, we can create lasting memories and valuable learning experiences, fostering a sense of camaraderie and curiosity that will stay with these young minds for a lifetime.</p>',
2516 ),
2517 array(
2518 'type' => 'photo',
2519 'default' => 'trip-1.jpg',
2520 ),
2521 ),
2522 ),
2523 ),
2524 array( // column.
2525 array( // section.
2526 'type' => 'fields',
2527 'fields' => array(
2528 array(
2529 'type' => 'photo',
2530 'default' => 'trip-2.jpg',
2531 ),
2532 array(
2533 'type' => 'campaign-summary',
2534 ),
2535 array(
2536 'type' => 'text',
2537 'headline' => 'Together, we can inspire these young minds and equip them with the tools they need to make a positive impact on the world.',
2538 'content' => 'Your generosity will not only provide the means for this adventure but will also contribute to the overall educational enrichment of our students. This trip isn\'t just a mere excursion; it\'s a chance for our 5th graders to expand their horizons, learn about different cultures, and engage with historical and natural wonders that will enhance their academic understanding. ',
2539 ),
2540 array(
2541 'type' => 'progress-bar',
2542 'show_hide' => array(
2543 'show_donated' => true,
2544 'show_goal' => true,
2545 ),
2546 'css_class' => 'sample-css-class',
2547 'label_donate' => 'Funded: ',
2548 'label_goal' => 'Goal: ',
2549 'meta_position' => 'top',
2550 ),
2551 array(
2552 'type' => 'social-links',
2553 'headline' => 'Follow on',
2554 'facebook_url' => 'https://facebook.com/',
2555 'twitter_url' => 'https://twitter.com/',
2556 ),
2557 ),
2558 ),
2559 ),
2560 ),
2561 ),
2562 array( // row.
2563 'type' => 'row',
2564 'css_class' => 'no-padding no-field-target no-field-wrap',
2565 'columns' => array(
2566 array( // column.
2567 array( // section.
2568 'type' => 'tabs',
2569 'tabs' => array(
2570 array(
2571 'title' => 'Donate Now',
2572 'type' => '',
2573 'slug' => 'default',
2574 'fields' => array(
2575 array(
2576 'type' => 'donation-form',
2577 ),
2578 ),
2579 ),
2580 ),
2581 ),
2582 ),
2583 ),
2584 ),
2585 ),
2586 'advanced' => array(
2587 'tab_style' => 'boxed',
2588 'tab_size' => 'medium',
2589 ),
2590 'settings' => array(
2591 'general' => array(
2592 'description' => '<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqui.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqui.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod</p>',
2593 ),
2594 ),
2595 ),
2596 'beacon-split' => array(
2597 'meta' => array(
2598 'label' => 'Beacon (2 Columns)',
2599 'slug' => 'beacon-split',
2600 'description' => 'A bold, structured layout for grassroots campaigns. Locked hero banner with donation widget overlay, plus a two-column story area below.',
2601 'template_type' => array( 'single', 'prebuilt' ),
2602 'is_new' => true,
2603 'search_tags' => array(
2604 'beacon',
2605 'structured',
2606 'two-columns',
2607 'two-column',
2608 'hero-banner',
2609 'donation-widget',
2610 'modern',
2611 'overlay',
2612 'environmental',
2613 ),
2614 'categories' => array(
2615 'structured-layouts',
2616 'environmental',
2617 ),
2618 'created' => '2026-06-06', // Y-m-d.
2619 'version' => '1.0',
2620 'author' => 'David Bisset',
2621 'dependencies' => false,
2622 'has_access' => true,
2623 'favorite' => false,
2624 'colors' => array(
2625 'primary' => '#1d3a8a',
2626 'secondary' => '#0D0D0D',
2627 'tertiary' => '#5F5F5F',
2628 'accent' => false,
2629 'button_bg' => '#1d3a8a',
2630 ),
2631 'parent_theme' => false,
2632 'thumbnail_url' => false,
2633 ),
2634 'layout' => array(
2635 array( // ROW 1 — locked hero.
2636 'type' => 'header',
2637 'css_class' => 'beacon-hero-row',
2638 'locked' => true,
2639 'background_image' => '', // Hero image lives inside the campaign-hero block; row bg unused here.
2640 'columns' => array(
2641 array(
2642 array(
2643 'type' => 'fields',
2644 'fields' => array(
2645 array(
2646 'type' => 'campaign-hero',
2647 'background_image' => charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/beacon-split/hero.jpg',
2648 'avatar_image' => charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/beacon-split/avatar.jpg',
2649 'title_override' => 'Save Maple Grove Park',
2650 'accent_color' => '#1d3a8a',
2651 'show_raised' => true,
2652 'enable_recurring' => false,
2653 'onetime_amounts' => '50,150,500,1000',
2654 'onetime_default' => '50',
2655 'onetime_show_other'=> true,
2656 'cta_label' => 'Donate Now',
2657 ),
2658 ),
2659 ),
2660 ),
2661 ),
2662 ),
2663 array( // ROW 2 — editable body, two columns.
2664 'type' => 'row',
2665 'css_class' => 'beacon-body-row',
2666 'columns' => array(
2667 array( // Column 1 — story.
2668 array(
2669 'type' => 'fields',
2670 'fields' => array(
2671 array(
2672 'type' => 'social-sharing',
2673 'headline' => '',
2674 ),
2675 array(
2676 'type' => 'campaign-description',
2677 'headline' => 'Support Hometown Heroes for Maple Grove',
2678 'content' => '<p>For generations, Maple Grove Park has been the beating heart of our community. It is where our children take their first steps, where local musicians find their muse under the shade of ancient trees, and where neighbors gather to escape the hustle of daily life. This park is not just a patch of green space on a map; it is a living sanctuary of shared memories, vibrant wildlife, and irreplaceable local history.</p><p>But right now, this beloved community treasure is facing a quiet but devastating crisis. <strong>Commercial development plans</strong> threaten to bulldoze our history and replace our vibrant canopy with concrete. If we stay silent, the ecosystem that supports local wildlife will be destroyed, and the public space that belongs to every single one of us will be lost forever. We cannot afford to stand by and watch Maple Grove vanish.</p><p>That is why we are launching the <strong>Save Maple Grove Park</strong> initiative. Our mission is simple but powerful: to legally protect this land, restore its natural habitats, and ensure it remains a free, beautiful public park for generations to come. We are working alongside local environmental experts, city planners, and passionate residents to present an airtight preservation alternative to the city council. But an expert plan means nothing without the roaring voice of the community behind it.</p>',
2679 ),
2680 array(
2681 'type' => 'text',
2682 'headline' => 'How your donation makes a difference',
2683 'content' => '<p>Every contribution moves us closer to permanent protection for Maple Grove Park. Here is exactly where your dollars go:</p><ul><li><strong>$50</strong> funds an hour of expert legal counsel reviewing the city development proposal.</li><li><strong>$150</strong> pays for native plant restoration across a quarter-acre of degraded meadow.</li><li><strong>$500</strong> covers an environmental impact study for one section of the park.</li><li><strong>$1,000</strong> sponsors a community meeting - venue, materials, and outreach to mobilize hundreds of neighbors.</li></ul><p>No donation is too small. Every signature, every dollar, and every shared story strengthens our case before the city council on July 14.</p>',
2684 ),
2685 array(
2686 'type' => 'text',
2687 'headline' => 'A community-led plan, ready to act',
2688 'content' => '<p>This is not a protest. It is a plan. Over the past six months, our coalition of biologists, urban planners, attorneys, and lifelong residents has assembled a complete preservation proposal - one that protects the park\'s ecosystem, maintains public access, and even strengthens the city\'s tax base through eco-tourism and community programming. We will present this proposal at the July 14 council session. With your support, we will arrive with the resources, the research, and the unified voice we need to win.</p>',
2689 ),
2690 array(
2691 'type' => 'donation-wall',
2692 'headline' => 'Donor Wall',
2693 ),
2694 ),
2695 ),
2696 ),
2697 array( // Column 2 — media.
2698 array(
2699 'type' => 'fields',
2700 'fields' => array(
2701 array(
2702 'type' => 'photo',
2703 'default' => 'photo-1.jpg',
2704 ),
2705 array(
2706 'type' => 'photo',
2707 'default' => 'photo-2.jpg',
2708 ),
2709 array(
2710 'type' => 'photo',
2711 'default' => 'photo-3.jpg',
2712 ),
2713 ),
2714 ),
2715 ),
2716 ),
2717 ),
2718 ),
2719 'advanced' => array(
2720 'tab_style' => 'minimum',
2721 'tab_size' => 'small',
2722 ),
2723 'settings' => array(
2724 'general' => array(
2725 'description' => '<p>Help us protect Maple Grove Park from commercial development and ensure it remains a free, beautiful public park for generations to come.</p>',
2726 ),
2727 ),
2728 ),
2729 'beacon-single' => array(
2730 'meta' => array(
2731 'label' => 'Beacon (1 Column)',
2732 'slug' => 'beacon-single',
2733 'description' => 'A bold, structured layout for grassroots campaigns. Locked hero banner with donation widget overlay, plus a single full-width column for long-form storytelling.',
2734 'template_type' => array( 'single', 'prebuilt' ),
2735 'is_new' => true,
2736 'search_tags' => array(
2737 'beacon',
2738 'structured',
2739 'one-column',
2740 'single-column',
2741 'long-form',
2742 'hero-banner',
2743 'donation-widget',
2744 'modern',
2745 'overlay',
2746 'environmental',
2747 ),
2748 'categories' => array(
2749 'structured-layouts',
2750 'environmental',
2751 ),
2752 'created' => '2026-06-06', // Y-m-d.
2753 'version' => '1.0',
2754 'author' => 'David Bisset',
2755 'dependencies' => false,
2756 'has_access' => true,
2757 'favorite' => false,
2758 'colors' => array(
2759 'primary' => '#1d3a8a',
2760 'secondary' => '#0D0D0D',
2761 'tertiary' => '#5F5F5F',
2762 'accent' => false,
2763 'button_bg' => '#1d3a8a',
2764 ),
2765 'parent_theme' => false,
2766 'thumbnail_url' => false,
2767 ),
2768 'layout' => array(
2769 array( // ROW 1 — locked hero.
2770 'type' => 'header',
2771 'css_class' => 'beacon-hero-row',
2772 'locked' => true,
2773 'background_image' => '', // Hero image lives inside the campaign-hero block; row bg unused here.
2774 'columns' => array(
2775 array(
2776 array(
2777 'type' => 'fields',
2778 'fields' => array(
2779 array(
2780 'type' => 'campaign-hero',
2781 'background_image' => charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/beacon-single/hero.jpg',
2782 'avatar_image' => charitable()->get_path( 'assets', false ) . 'images/campaign-builder/templates/beacon-single/avatar.jpg',
2783 'title_override' => 'Save Maple Grove Park',
2784 'accent_color' => '#1d3a8a',
2785 'show_raised' => true,
2786 'enable_recurring' => false,
2787 'onetime_amounts' => '50,150,500,1000',
2788 'onetime_default' => '50',
2789 'onetime_show_other'=> true,
2790 'cta_label' => 'Donate Now',
2791 ),
2792 ),
2793 ),
2794 ),
2795 ),
2796 ),
2797 array( // ROW 2 — editable body, single full-width column.
2798 'type' => 'row',
2799 'css_class' => 'beacon-body-row',
2800 'columns' => array(
2801 array( // Column 1 — single body column.
2802 array(
2803 'type' => 'fields',
2804 'fields' => array(
2805 array(
2806 'type' => 'social-sharing',
2807 'headline' => '',
2808 ),
2809 array(
2810 'type' => 'campaign-description',
2811 'headline' => 'Support Hometown Heroes for Maple Grove',
2812 'content' => '<p>For generations, Maple Grove Park has been the beating heart of our community. It is where our children take their first steps, where local musicians find their muse under the shade of ancient trees, and where neighbors gather to escape the hustle of daily life. This park is not just a patch of green space on a map; it is a living sanctuary of shared memories, vibrant wildlife, and irreplaceable local history.</p><p>But right now, this beloved community treasure is facing a quiet but devastating crisis. <strong>Commercial development plans</strong> threaten to bulldoze our history and replace our vibrant canopy with concrete. If we stay silent, the ecosystem that supports local wildlife will be destroyed, and the public space that belongs to every single one of us will be lost forever. We cannot afford to stand by and watch Maple Grove vanish.</p><p>That is why we are launching the <strong>Save Maple Grove Park</strong> initiative. Our mission is simple but powerful: to legally protect this land, restore its natural habitats, and ensure it remains a free, beautiful public park for generations to come. We are working alongside local environmental experts, city planners, and passionate residents to present an airtight preservation alternative to the city council. But an expert plan means nothing without the roaring voice of the community behind it.</p>',
2813 ),
2814 array(
2815 'type' => 'text',
2816 'headline' => 'How your donation makes a difference',
2817 'content' => '<p>Every contribution moves us closer to permanent protection for Maple Grove Park. Here is exactly where your dollars go:</p><ul><li><strong>$50</strong> funds an hour of expert legal counsel reviewing the city development proposal.</li><li><strong>$150</strong> pays for native plant restoration across a quarter-acre of degraded meadow.</li><li><strong>$500</strong> covers an environmental impact study for one section of the park.</li><li><strong>$1,000</strong> sponsors a community meeting - venue, materials, and outreach to mobilize hundreds of neighbors.</li></ul><p>No donation is too small. Every signature, every dollar, and every shared story strengthens our case before the city council on July 14.</p>',
2818 ),
2819 array(
2820 'type' => 'photo',
2821 'default' => 'photo-1.jpg',
2822 ),
2823 array(
2824 'type' => 'text',
2825 'headline' => 'A community-led plan, ready to act',
2826 'content' => '<p>This is not a protest. It is a plan. Over the past six months, our coalition of biologists, urban planners, attorneys, and lifelong residents has assembled a complete preservation proposal - one that protects the park\'s ecosystem, maintains public access, and even strengthens the city\'s tax base through eco-tourism and community programming. We will present this proposal at the July 14 council session. With your support, we will arrive with the resources, the research, and the unified voice we need to win.</p>',
2827 ),
2828 array(
2829 'type' => 'photo',
2830 'default' => 'photo-2.jpg',
2831 ),
2832 array(
2833 'type' => 'donation-wall',
2834 'headline' => 'Donor Wall',
2835 ),
2836 ),
2837 ),
2838 ),
2839 ),
2840 ),
2841 ),
2842 'advanced' => array(
2843 'tab_style' => 'minimum',
2844 'tab_size' => 'small',
2845 ),
2846 'settings' => array(
2847 'general' => array(
2848 'description' => '<p>Help us protect Maple Grove Park from commercial development and ensure it remains a free, beautiful public park for generations to come.</p>',
2849 ),
2850 ),
2851 ),
2852 'animal-sanctuary' => array(
2853 'meta' => array(
2854 'label' => 'Animal Sanctuary',
2855 'slug' => 'animal-sanctuary',
2856 'description' => 'Get funds for your mission to rescue, rehabilitate, or rehome animals in need. Showcase your team, share success stories, and connect donors to the cause that matters most.',
2857 'template_type' => array( 'single', 'prebuilt' ),
2858 'search_tags' => array(
2859 'animal-pets',
2860 'environmental',
2861 'blue',
2862 ),
2863 'categories' => array(
2864 'animal-pets',
2865 'environmental',
2866 ),
2867 'created' => '2023-10-05', // Y-m-d.
2868 'version' => '1.0',
2869 'author' => 'David Bisset',
2870 'dependencies' => false, // not used yet.
2871 'has_access' => true, // not used yet, but false if the user can't select it.
2872 'favorite' => false,
2873 'colors' => array(
2874 'primary' => '#805F93',
2875 'secondary' => '#1D1C1C',
2876 'tertiary' => '#808080',
2877 'accent' => false,
2878 'button_bg' => '#805F93',
2879 ),
2880 'parent_theme' => false, // not used yet.
2881 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
2882 'suffixes' => array(
2883 'social-sharing' => '-dark',
2884 'social-links' => '-dark',
2885 ),
2886 ),
2887 'layout' => array(
2888 array( // row.
2889 'type' => 'row',
2890 'columns' => array(
2891 array( // column.
2892 array( // section.
2893 'type' => 'fields',
2894 'fields' => array(
2895 array(
2896 'type' => 'photo',
2897 'default' => 'photo-1.jpg',
2898 ),
2899 array(
2900 'type' => 'campaign-description',
2901 'headline' => 'Donate today to support our mission to rescue, rehabilitate, and rehome',
2902 'content' => '<p>I\'m thrilled to launch our new campaign aimed at supporting an incredible cause: an animal sanctuary dedicated to rescuing abandoned and lost animals and finding them loving homes. With your help, we aim to raise funds to maintain this sanctuary, providing a safe haven for these adorable pets and ensuring they receive the care they deserve. Your contributions will not only help us sustain the facility but also enable us to actively seek new, caring families for these animals, giving them a chance at a brighter and happier future. Together, we can make a real difference in the lives of these innocent creatures.</p>',
2903 ),
2904 array(
2905 'type' => 'campaign-summary',
2906 ),
2907 array(
2908 'type' => 'donate-button',
2909 'button_label' => 'Donate Now',
2910 ),
2911 ),
2912 ),
2913 ),
2914 array( // column.
2915 array( // section.
2916 'type' => 'fields',
2917 'fields' => array(
2918 array(
2919 'type' => 'organizer',
2920 'role_or_title' => 'Organizer',
2921 'content' => '',
2922 ),
2923 array(
2924 'type' => 'social-sharing',
2925 'headline' => 'Share Now',
2926 'facebook_url' => 'https://facebook.com/',
2927 'twitter_url' => 'https://twitter.com/',
2928 ),
2929 array(
2930 'type' => 'social-links',
2931 'headline' => 'Follow Now',
2932 'facebook_url' => 'https://facebook.com/',
2933 'twitter_url' => 'https://twitter.com/',
2934 'instagram_url' => 'https://instagram.com/',
2935 ),
2936 ),
2937 ),
2938 ),
2939 ),
2940 ),
2941 array( // row.
2942 'type' => 'row',
2943 'css_class' => 'no-padding no-field-target no-field-wrap',
2944 'columns' => array(
2945 array( // column.
2946 array( // section.
2947 'type' => 'tabs',
2948 'tabs' => array(
2949 array(
2950 'title' => 'Overview',
2951 'type' => '',
2952 'slug' => 'default',
2953 'fields' => array(
2954 array(
2955 'type' => 'text',
2956 'headline' => 'About Us',
2957 'content' => 'We are an animal santuary established in 2007.',
2958 ),
2959 ),
2960 ),
2961 ),
2962 ),
2963 ),
2964 ),
2965 ),
2966 ),
2967 'advanced' => array(
2968 'tab_style' => 'boxed',
2969 'tab_size' => 'medium',
2970 ),
2971 'settings' => array(
2972 'general' => array(
2973 'description' => '<p>I\'m thrilled to launch our new campaign aimed at supporting an incredible cause: an animal sanctuary dedicated to rescuing abandoned and lost animals and finding them loving homes. With your help, we aim to raise funds to maintain this sanctuary, providing a safe haven for these adorable pets and ensuring they receive the care they deserve. Your contributions will not only help us sustain the facility but also enable us to actively seek new, caring families for these animals, giving them a chance at a brighter and happier future. Together, we can make a real difference in the lives of these innocent creatures.</p>',
2974 ),
2975 ),
2976 ),
2977 'save-the-museum' => array(
2978 'meta' => array(
2979 'label' => 'Save The Museum',
2980 'slug' => 'save-the-museum',
2981 'description' => 'Save a historical building, residence, or important landmark. Use compelling imagery and timeline content to rally community support around preserving what matters.',
2982 'template_type' => array( 'single', 'prebuilt' ),
2983 'search_tags' => array(
2984 'legal',
2985 'special-occasions',
2986 'blue',
2987 ),
2988 'categories' => array(
2989 'legal',
2990 'club-organizations',
2991 'special-occasions',
2992 ),
2993 'created' => '2023-10-05', // Y-m-d.
2994 'version' => '1.0',
2995 'author' => 'David Bisset',
2996 'dependencies' => false, // not used yet.
2997 'has_access' => true, // not used yet, but false if the user can't select it.
2998 'favorite' => false,
2999 'colors' => array(
3000 'primary' => '#F58A07',
3001 'secondary' => '#1D3444',
3002 'tertiary' => '#5B5B5B',
3003 'accent' => false,
3004 'button_bg' => '#FFFFFF',
3005 ),
3006 'parent_theme' => false, // not used yet.
3007 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3008 'suffixes' => array(
3009 'social-sharing' => '-dark',
3010 'social-links' => '-dark',
3011 ),
3012 ),
3013 'layout' => array(
3014 array( // row.
3015 'type' => 'row',
3016 'columns' => array(
3017 array( // column.
3018 array( // section.
3019 'type' => 'fields',
3020 'fields' => array(
3021 array(
3022 'type' => 'photo',
3023 'default' => 'photo-1.jpg',
3024 ),
3025 array(
3026 'type' => 'campaign-description',
3027 'headline' => 'About This Project',
3028 'content' => '<p>In 1918 the top floor of the Lyric Theatre was converted into a hospital to care for the sick and dying during the Spanish Flu Epidemic. A nurse, Mrs. Lapp, was forced to care for all of families upstairs. Today nearly 100 years later the top floor of the Lyric remains unused and it’s sole occupant is the spirit of Nurse Lapp. We are trying to save this abandoned building with the help of you all.</p>',
3029 ),
3030 ),
3031 ),
3032 ),
3033 array( // column.
3034 array( // section.
3035 'type' => 'fields',
3036 'fields' => array(
3037 array(
3038 'type' => 'campaign-title',
3039 ),
3040 array(
3041 'type' => 'progress-bar',
3042 'show_hide' => array(
3043 'show_donated' => true,
3044 'show_goal' => false,
3045 ),
3046 'css_class' => 'sample-css-class',
3047 'label_donate' => 'Funded ',
3048 'label_goal' => 'Goal USD: ',
3049 'meta_position' => 'top',
3050 ),
3051 array(
3052 'type' => 'campaign-summary',
3053 ),
3054 array(
3055 'type' => 'donate-button',
3056 'button_label' => 'Donate Now',
3057 ),
3058 array(
3059 'type' => 'social-links',
3060 'headline' => 'Follow on',
3061 'facebook_url' => 'https://facebook.com/',
3062 'twitter_url' => 'https://twitter.com/',
3063 ),
3064 ),
3065 ),
3066 ),
3067 ),
3068 ),
3069 array( // row.
3070 'type' => 'row',
3071 'css_class' => 'no-padding no-field-target no-field-wrap',
3072 'columns' => array(
3073 array( // column.
3074 array( // section.
3075 'type' => 'tabs',
3076 'tabs' => array(
3077 array(
3078 'title' => 'Overview',
3079 'type' => '',
3080 'slug' => 'default',
3081 'fields' => array(
3082 array(
3083 'type' => 'text',
3084 'headline' => 'Save the Lyrique Building',
3085 'content' => '<p>Mrs. Lapp\'s spirit is said to linger in the hushed corners of the Lyric Theatre, her presence felt by those who dare to venture to the top floor. Visitors claim to have heard soft whispers and felt a comforting presence, as if she\'s still watching over the space she once tended to with such dedication. The abandoned building stands as a testament to the resilience of the human spirit during times of crisis, and it has become a symbol of hope and survival. Efforts to preserve the Lyric Theatre are fueled by the desire to honor Mrs. Lapp\'s legacy and the countless lives she touched.</p><p>The community has rallied together, sharing stories of her bravery and compassion, inspiring a new generation to join the cause. With each donation and volunteer effort, the echoes of the past grow stronger, uniting people across time in a shared mission to save this historical landmark and the memory of Nurse Lapp, reminding us all of the power of compassion and community in the face of adversity.</p>',
3086 ),
3087 array(
3088 'type' => 'photo',
3089 'default' => 'photo-2.jpg',
3090 ),
3091 ),
3092 ),
3093 array(
3094 'title' => 'Updates',
3095 'type' => '',
3096 'slug' => 'updates',
3097 'fields' => array(
3098 'updates-main' => array(
3099 'headline' => 'Top Team Members',
3100 'columns' => 6,
3101 ),
3102 ),
3103 ),
3104 array(
3105 'title' => 'Comments',
3106 'type' => '',
3107 'slug' => 'comments',
3108 'fields' => array(
3109 'comments-main' => array(
3110 'headline' => 'Top Team Members',
3111 'columns' => 6,
3112 ),
3113 ),
3114 ),
3115 ),
3116 ),
3117 ),
3118 ),
3119 ),
3120 ),
3121 'advanced' => array(
3122 'tab_style' => 'boxed',
3123 'tab_size' => 'medium',
3124 ),
3125 'settings' => array(
3126 'general' => array(
3127 'description' => '<p>In 1918 the top floor of the Lyric Theatre was converted into a hospital to care for the sick and dying during the Spanish Flu Epidemic. A nurse, Mrs. Lapp, was forced to care for all of families upstairs. Today nearly 100 years later the top floor of the Lyric remains unused and it’s sole occupant is the spirit of Nurse Lapp. We are trying to save this abandoned building with the help of you all.</p>',
3128 ),
3129 ),
3130 ),
3131 'environmental' => array(
3132 'meta' => array(
3133 'label' => 'Environmental',
3134 'slug' => 'environmental',
3135 'description' => 'Preserve nature and the earth for future generations. Highlight conservation work, scientific impact, and the local change donors are funding - ideal for habitat, land trust, and climate causes.',
3136 'template_type' => array( 'single', 'prebuilt' ),
3137 'search_tags' => array(
3138 'environmental',
3139 'animal-pets',
3140 'blue',
3141 ),
3142 'categories' => array(
3143 'environmental',
3144 'animal-pets',
3145 ),
3146 'created' => '2023-10-05', // Y-m-d.
3147 'version' => '1.0',
3148 'author' => 'David Bisset',
3149 'dependencies' => false, // not used yet.
3150 'has_access' => true, // not used yet, but false if the user can't select it.
3151 'favorite' => false,
3152 'colors' => array(
3153 'primary' => '#F9F6EE',
3154 'secondary' => '#24231E',
3155 'tertiary' => '#61AA4F',
3156 'accent' => false,
3157 'button_bg' => '#61AA4F',
3158 ),
3159 'parent_theme' => false, // not used yet.
3160 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3161 'suffixes' => array(
3162 'social-sharing' => '-dark',
3163 'social-links' => '-dark',
3164 ),
3165 ),
3166 'layout' => array(
3167 array( // row.
3168 'type' => 'row',
3169 'columns' => array(
3170 array( // column.
3171 array( // section.
3172 'type' => 'fields',
3173 'fields' => array(
3174 array(
3175 'type' => 'photo',
3176 'default' => 'photo-1.jpg',
3177 ),
3178 ),
3179 ),
3180 ),
3181 array( // column.
3182 array( // section.
3183 'type' => 'fields',
3184 'fields' => array(
3185 array(
3186 'type' => 'text',
3187 'headline' => 'Save Earth',
3188 'content' => 'Together, let\'s reclaim the serenity of our shorelines and ensure a cleaner, greener future for generations to come.',
3189 'width_percentage' => 100,
3190 'align' => 'left',
3191 ),
3192 array(
3193 'type' => 'campaign-description',
3194 'headline' => 'Help Preserving The Earth For Future Generations',
3195 'content' => '<p>We are announcing a campaign dedicated to restoring the natural beauty of our local beach, which has sadly fallen victim to years of neglect and pollution. Our non-profit is on a mission to raise funds for cleaning supplies, vehicles, and essential costs to orchestrate a massive cleanup effort. By contributing, you\'re not just supporting a cleaner beach but also promoting environmental health and community pride. </p>',
3196 'width_percentage' => 100,
3197 'align' => 'left',
3198 ),
3199 array(
3200 'type' => 'campaign-summary',
3201 'width_percentage' => 100,
3202 'align' => 'left',
3203 ),
3204 array(
3205 'type' => 'donate-button',
3206 'button_label' => 'Donate Now',
3207 'width_percentage' => 100,
3208 'align' => 'left',
3209 ),
3210 array(
3211 'type' => 'social-links',
3212 'headline' => ' ',
3213 'facebook_url' => 'https://facebook.com/',
3214 'twitter_url' => 'https://twitter.com/',
3215 'align' => 'left',
3216 ),
3217 ),
3218 ),
3219 ),
3220 ),
3221 ),
3222 array( // row.
3223 'type' => 'row',
3224 'css_class' => 'no-padding no-field-target no-field-wrap',
3225 'columns' => array(
3226 array( // column.
3227 array( // section.
3228 'type' => 'tabs',
3229 'tabs' => array(
3230 array(
3231 'title' => 'Overview',
3232 'type' => '',
3233 'slug' => 'default',
3234 'fields' => array(
3235 array(
3236 'type' => 'text',
3237 'headline' => 'Keep the scene green by taking the lead',
3238 'content' => 'Your support will make a significant impact, enabling us to organize community clean-up events where volunteers, equipped with the necessary resources, can work together to remove garbage and restore the beach to its natural state. Additionally, your contributions will empower us to implement educational programs aimed at raising awareness about the importance of environmental conservation. We believe that through collective action, we can not only clean up our beloved beach but also inspire a lasting change in our community\'s attitudes towards environmental responsibility. Every donation, no matter how big or small, brings us one step closer to a cleaner, healthier environment for everyone. Join us in this vital endeavor, and let\'s create a positive ripple effect that benefits both our local ecosystem and the people who call this community home.',
3239 ),
3240 array(
3241 'type' => 'photo',
3242 'default' => 'photo-2.jpg',
3243 'align' => 'center',
3244 ),
3245 ),
3246 ),
3247 ),
3248 ),
3249 ),
3250 ),
3251 ),
3252 ),
3253 'advanced' => array(
3254 'tab_style' => 'boxed',
3255 'tab_size' => 'medium',
3256 ),
3257 'settings' => array(
3258 'general' => array(
3259 'description' => '<p>We are announcing a campaign dedicated to restoring the natural beauty of our local beach, which has sadly fallen victim to years of neglect and pollution. Our non-profit is on a mission to raise funds for cleaning supplies, vehicles, and essential costs to orchestrate a massive cleanup effort. By contributing, you\'re not just supporting a cleaner beach but also promoting environmental health and community pride.</p>',
3260 ),
3261 ),
3262 ),
3263 'disaster-relief' => array(
3264 'meta' => array(
3265 'label' => 'Disaster Relief',
3266 'slug' => 'disaster-relief',
3267 'description' => 'Help those affected by natural disasters and emergency events. Move quickly with a focused story, a clear goal, and transparent allocation so urgent donations turn into immediate aid.',
3268 'template_type' => array( 'single', 'prebuilt' ),
3269 'search_tags' => array(
3270 'environmental',
3271 'medical',
3272 'family',
3273 'blue',
3274 ),
3275 'categories' => array(
3276 'environmental',
3277 'medical',
3278 'family',
3279 ),
3280 'created' => '2023-10-05', // Y-m-d.
3281 'version' => '1.0',
3282 'author' => 'David Bisset',
3283 'dependencies' => false, // not used yet.
3284 'has_access' => true, // not used yet, but false if the user can't select it.
3285 'favorite' => false,
3286 'colors' => array(
3287 'primary' => '#9F190E',
3288 'secondary' => '#202020',
3289 'tertiary' => '#FFFFFF',
3290 'accent' => false,
3291 'button_bg' => '#9F190E',
3292 ),
3293 'parent_theme' => false, // not used yet.
3294 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3295 'suffixes' => array(),
3296 ),
3297 'layout' => array(
3298 array( // row.
3299 'type' => 'row',
3300 'columns' => array(
3301 array( // column.
3302 array( // section.
3303 'type' => 'fields',
3304 'fields' => array(
3305 array(
3306 'type' => 'photo',
3307 'default' => 'photo-1.jpg',
3308 ),
3309 array(
3310 'type' => 'progress-bar',
3311 'show_hide' => array(
3312 'show_donated' => true,
3313 'show_goal' => true,
3314 ),
3315 'css_class' => 'sample-css-class',
3316 'label_donate' => ' ',
3317 'label_goal' => 'Goal USD: ',
3318 'meta_position' => 'top',
3319 ),
3320 ),
3321 ),
3322 ),
3323 array( // column.
3324 array( // section.
3325 'type' => 'fields',
3326 'fields' => array(
3327 array(
3328 'type' => 'text',
3329 'headline' => 'Donate Today',
3330 'content' => 'This project will only be funded if at least $270,000 is raised by December 22, 2025.',
3331 ),
3332 array(
3333 'type' => 'campaign-summary',
3334 ),
3335 array(
3336 'type' => 'donate-button',
3337 'button_label' => 'Donate Now',
3338 'width_percentage' => 100,
3339 ),
3340 array(
3341 'type' => 'social-sharing',
3342 'headline' => 'Share',
3343 'facebook_url' => 'https://facebook.com/',
3344 'twitter_url' => 'https://twitter.com/',
3345 'align' => 'left',
3346 ),
3347 array(
3348 'type' => 'social-links',
3349 'headline' => 'Follow on',
3350 'facebook_url' => 'https://facebook.com/',
3351 'twitter_url' => 'https://twitter.com/',
3352 'align' => 'left',
3353 ),
3354 ),
3355 ),
3356 ),
3357 ),
3358 ),
3359 array( // row.
3360 'type' => 'row',
3361 'css_class' => 'no-padding no-field-target no-field-wrap',
3362 'columns' => array(
3363 array( // column.
3364 array( // section.
3365 'type' => 'tabs',
3366 'tabs' => array(
3367 array(
3368 'title' => 'Overview',
3369 'type' => '',
3370 'slug' => 'default',
3371 'fields' => array(
3372 array(
3373 'type' => 'campaign-description',
3374 'headline' => 'About This Campaign',
3375 'content' => '<p>We are announcing a new campaign aimed at providing crucial support to the victims of a recent devastating natural disaster. Together, we are rallying behind our fellow community members, raising funds through this non-profit initiative to help them rebuild homes, restore essential services, and reclaim their lives. Join us in making a difference, as every contribution brings us one step closer to bringing hope and stability back to those in need..</p>',
3376 'align' => 'left',
3377 ),
3378 array(
3379 'type' => 'photo',
3380 'default' => 'photo-2.jpg',
3381 ),
3382 ),
3383 ),
3384 ),
3385 ),
3386 ),
3387 ),
3388 ),
3389 ),
3390 'advanced' => array(
3391 'tab_style' => 'boxed',
3392 'tab_size' => 'medium',
3393 ),
3394 'settings' => array(
3395 'general' => array(
3396 'description' => '<p>We are announcing a new campaign aimed at providing crucial support to the victims of a recent devastating natural disaster. Together, we are rallying behind our fellow community members, raising funds through this non-profit initiative to help them rebuild homes, restore essential services, and reclaim their lives. Join us in making a difference, as every contribution brings us one step closer to bringing hope and stability back to those in need.</p>',
3397 ),
3398 ),
3399 ),
3400 'club-organization' => array(
3401 'meta' => array(
3402 'label' => 'Club / Organization',
3403 'slug' => 'club-organization',
3404 'description' => 'Fund programs or grow operating support for a worthy organization or club. Pair member impact stories with sponsor tiers and donor recognition to keep contributors engaged.',
3405 'template_type' => array( 'single', 'prebuilt' ),
3406 'search_tags' => array(
3407 'club-organizations',
3408 'school',
3409 'blue',
3410 ),
3411 'categories' => array(
3412 'club-organizations',
3413 'school',
3414 'sports',
3415 ),
3416 'created' => '2023-10-05', // Y-m-d.
3417 'version' => '1.0',
3418 'author' => 'David Bisset',
3419 'dependencies' => false, // not used yet.
3420 'has_access' => true, // not used yet, but false if the user can't select it.
3421 'favorite' => false,
3422 'colors' => array(
3423 'primary' => '#474735',
3424 'secondary' => '#B1A17C',
3425 'tertiary' => '#F4F0EE',
3426 'accent' => false,
3427 'button_bg' => '#B49A5F',
3428 ),
3429 'parent_theme' => false, // not used yet.
3430 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3431 'suffixes' => array(),
3432 ),
3433 'layout' => array(
3434 array( // row.
3435 'type' => 'row',
3436 'columns' => array(
3437 array( // column.
3438 array( // section.
3439 'type' => 'fields',
3440 'fields' => array(
3441 array(
3442 'type' => 'photo',
3443 'default' => 'photo-1.jpg',
3444 ),
3445 ),
3446 ),
3447 ),
3448 array( // column.
3449 array( // section.
3450 'type' => 'fields',
3451 'fields' => array(
3452 array(
3453 'type' => 'campaign-description',
3454 'headline' => 'About This Club',
3455 'content' => '<p>We are announcing a campaign to support our cherished local country club, a haven for our community, especially our elderly residents. Our non-profit is passionately rallying for funds to ensure the maintenance of this vital space and to sustain the heartwarming annual events that bring together generations. By contributing, you\'re preserving a beloved institution that unites the young and the old, fostering a sense of belonging and community spirit. Join us in safeguarding this haven for all ages!</p>',
3456 'width_percentage' => 100,
3457 'align' => 'left',
3458 ),
3459 array(
3460 'type' => 'progress-bar',
3461 'show_hide' => array(
3462 'show_donated' => true,
3463 'show_goal' => true,
3464 ),
3465 'css_class' => 'sample-css-class',
3466 'label_donate' => ' ',
3467 'label_goal' => 'Goal USD: ',
3468 'meta_position' => 'top',
3469 'width_percentage' => 100,
3470 'align' => 'left',
3471 ),
3472 array(
3473 'type' => 'campaign-summary',
3474 'width_percentage' => 100,
3475 'align' => 'left',
3476 ),
3477 array(
3478 'type' => 'donate-button',
3479 'button_label' => 'Donate Now',
3480 'width_percentage' => 40,
3481 'align' => 'center',
3482 ),
3483 ),
3484 ),
3485 ),
3486 ),
3487 ),
3488 array( // row.
3489 'type' => 'row',
3490 'css_class' => 'no-padding no-field-target no-field-wrap',
3491 'columns' => array(
3492 array( // column.
3493 array( // section.
3494 'type' => 'tabs',
3495 'tabs' => array(
3496 array(
3497 'title' => 'Overview',
3498 'type' => '',
3499 'slug' => 'default',
3500 'fields' => array(
3501 array(
3502 'type' => 'text',
3503 'headline' => 'With 100% of donations going towards grants and programs',
3504 'content' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqui.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
3505 ),
3506 array(
3507 'type' => 'photo',
3508 'default' => 'photo-2.jpg',
3509 ),
3510 ),
3511 ),
3512 array(
3513 'title' => 'Updates',
3514 'type' => '',
3515 'slug' => 'updates',
3516 'fields' => array(
3517 array(
3518 'type' => 'updates-main',
3519 'headline' => 'Top Team Members',
3520 'columns' => 6,
3521 ),
3522 ),
3523 ),
3524 array(
3525 'title' => 'Comments',
3526 'type' => '',
3527 'slug' => 'comments',
3528 'fields' => array(
3529 array(
3530 'type' => 'comments-main',
3531 'headline' => 'Top Team Members',
3532 'columns' => 6,
3533 ),
3534 ),
3535 ),
3536 ),
3537 ),
3538 ),
3539 ),
3540 ),
3541 ),
3542 'advanced' => array(
3543 'tab_style' => 'boxed',
3544 'tab_size' => 'medium',
3545 ),
3546 'settings' => array(
3547 'general' => array(
3548 'description' => '<p>We are announcing a campaign to support our cherished local country club, a haven for our community, especially our elderly residents. Our non-profit is passionately rallying for funds to ensure the maintenance of this vital space and to sustain the heartwarming annual events that bring together generations. By contributing, you\'re preserving a beloved institution that unites the young and the old, fostering a sense of belonging and community spirit. Join us in safeguarding this haven for all ages!</p>',
3549 ),
3550 ),
3551 ),
3552 'medical-causes' => array(
3553 'meta' => array(
3554 'label' => 'Medical Causes',
3555 'slug' => 'medical-causes',
3556 'description' => 'Have a goal for more research, better treatment, or a cure. Built for foundations, advocacy groups, and patient communities raising funds toward a specific medical milestone.',
3557 'template_type' => array( 'single', 'prebuilt' ),
3558 'search_tags' => array(
3559 'medical',
3560 'family',
3561 'blue',
3562 ),
3563 'categories' => array(
3564 'medical',
3565 'family',
3566 ),
3567 'created' => '2023-10-05', // Y-m-d.
3568 'version' => '1.0',
3569 'author' => 'David Bisset',
3570 'dependencies' => false, // not used yet.
3571 'has_access' => true, // not used yet, but false if the user can't select it.
3572 'favorite' => false,
3573 'colors' => array(
3574 'primary' => '#192E45',
3575 'secondary' => '#215DB7',
3576 'tertiary' => '#48A9F5',
3577 'accent' => false,
3578 'button_bg' => '#48A9F5',
3579 ),
3580 'parent_theme' => false, // not used yet.
3581 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3582 'suffixes' => array(),
3583 ),
3584 'layout' => array(
3585 array( // row.
3586 'type' => 'row',
3587 'columns' => array(
3588 array( // column.
3589 array( // section.
3590 'type' => 'fields',
3591 'fields' => array(
3592 array(
3593 'type' => 'video',
3594 'headline' => '',
3595 'upgrade-img-bg' => 'field-charitable-videos.jpg',
3596 ),
3597 ),
3598 ),
3599 ),
3600 array( // column.
3601 array( // section.
3602 'type' => 'fields',
3603 'fields' => array(
3604 array(
3605 'type' => 'campaign-title',
3606 ),
3607 array(
3608 'type' => 'text',
3609 'headline' => 'Cancer Research Program',
3610 'content' => 'By donating to this cause, you can improve the lives of a variety of different people in your community and feel good about doing it.',
3611 ),
3612 array(
3613 'type' => 'progress-bar',
3614 'show_hide' => array(
3615 'show_donated' => true,
3616 'show_goal' => true,
3617 ),
3618 'css_class' => 'sample-css-class',
3619 'label_donate' => ' ',
3620 'label_goal' => 'Goal USD: ',
3621 'meta_position' => 'top',
3622 ),
3623 array(
3624 'type' => 'campaign-summary',
3625 ),
3626 array(
3627 'type' => 'donate-button',
3628 'button_label' => 'Donate Now',
3629 'align' => 'left',
3630 'width_percentage' => 50,
3631 ),
3632 ),
3633 ),
3634 ),
3635 ),
3636 ),
3637 array( // row.
3638 'type' => 'row',
3639 'css_class' => 'no-padding no-field-target no-field-wrap',
3640 'columns' => array(
3641 array( // column.
3642 array( // section.
3643 'type' => 'tabs',
3644 'tabs' => array(
3645 array(
3646 'title' => 'Overview',
3647 'type' => '',
3648 'slug' => 'overview',
3649 'fields' => array(
3650 array(
3651 'type' => 'campaign-description',
3652 'headline' => 'About Our Organization',
3653 'content' => '<p>We are announcing a campaign dedicated to supporting our local hospital\'s crucial cancer research initiatives, focusing on children under 18, women, and minority communities. Our non-profit is passionately rallying for funds to maintain this specialized facility and sustain popular fundraisers accessible to our community. By contributing, you\'re directly investing in cutting-edge research, ensuring that young lives are given a fighting chance, and addressing healthcare disparities. Join us in this noble cause, as together, we strive to create a healthier future for our youth, women, and minority members.</p>',
3654 'width_percentage' => 100,
3655 'align' => 'left',
3656 ),
3657 array(
3658 'type' => 'photo',
3659 'default' => 'photo-1.jpg',
3660 ),
3661 array(
3662 'type' => 'text',
3663 'headline' => 'Thinking differently is a proven strategy for changing the world',
3664 'content' => '<p>Your support will not only fund vital research but also provide essential resources for specialized treatments, counseling, and support services tailored to the unique needs of young patients, women, and minorities battling cancer. By contributing to this campaign, you\'re helping us bridge the gaps in healthcare access, ensuring that everyone, regardless of age, gender, or ethnicity, receives the best possible care and support during their challenging journey.</p><p>Together, we can make a significant difference in the lives of those affected by cancer, offering them hope, strength, and the promise of a brighter, healthier tomorrow. Join us in our fight against cancer, and let\'s create a community where every individual receives the care and compassion they deserve.</p>',
3665 ),
3666 array(
3667 'type' => 'photo',
3668 'default' => 'photo-2.jpg',
3669 ),
3670 ),
3671 ),
3672 array(
3673 'title' => 'Comments',
3674 'type' => '',
3675 'slug' => 'comments',
3676 'fields' => array(),
3677 ),
3678 array(
3679 'title' => 'Reviews',
3680 'type' => '',
3681 'slug' => 'reviews',
3682 'fields' => array(),
3683 ),
3684 ),
3685 ),
3686 ),
3687 ),
3688 ),
3689 ),
3690 'advanced' => array(
3691 'tab_style' => 'boxed',
3692 'tab_size' => 'medium',
3693 ),
3694 'settings' => array(
3695 'general' => array(
3696 'description' => '<p>We are announcing a campaign dedicated to supporting our local hospital\'s crucial cancer research initiatives, focusing on children under 18, women, and minority communities. Our non-profit is passionately rallying for funds to maintain this specialized facility and sustain popular fundraisers accessible to our community. By contributing, you\'re directly investing in cutting-edge research, ensuring that young lives are given a fighting chance, and addressing healthcare disparities. Join us in this noble cause, as together, we strive to create a healthier future for our youth, women, and minority members.</p>',
3697 ),
3698 ),
3699 ),
3700 'medical-bills' => array(
3701 'meta' => array(
3702 'label' => 'Medical Bills',
3703 'slug' => 'medical-bills',
3704 'description' => 'Raise funds to support the medical treatment of a friend or family member. Personal layout with space for the patient\'s story, treatment plan, and updates as donations come in.',
3705 'template_type' => array( 'single', 'prebuilt' ),
3706 'search_tags' => array(
3707 'medical',
3708 'family',
3709 'blue',
3710 ),
3711 'categories' => array(
3712 'medical',
3713 'family',
3714 ),
3715 'created' => '2023-10-05', // Y-m-d.
3716 'version' => '1.0',
3717 'author' => 'David Bisset',
3718 'dependencies' => false, // not used yet.
3719 'has_access' => true, // not used yet, but false if the user can't select it.
3720 'favorite' => false,
3721 'colors' => array(
3722 'primary' => '#5C8AF3',
3723 'secondary' => '#21458F',
3724 'tertiary' => '#F7F7F7',
3725 'accent' => false,
3726 'button_bg' => '#5C8AF3',
3727 ),
3728 'parent_theme' => false, // not used yet.
3729 'thumbnail_url' => false, // overrides default location to look for thumbnails in template page.
3730 ),
3731 'layout' => array(
3732 array( // row.
3733 'type' => 'header',
3734 'columns' => array(
3735 array( // column.
3736 array( // section.
3737 'type' => 'fields',
3738 'fields' => array(
3739 array(
3740 'type' => 'campaign-title',
3741 'align' => 'center',
3742 ),
3743 ),
3744 ),
3745 ),
3746 ),
3747 ),
3748 array( // row.
3749 'type' => 'row',
3750 'css_class' => '', // 'css_class' => 'no-padding no-field-target no-field-wrap',
3751 'columns' => array(
3752 array( // column.
3753 array( // section.
3754 'type' => 'tabs',
3755 'tabs' => array(
3756 array(
3757 'title' => 'Overview',
3758 'type' => '',
3759 'slug' => 'default',
3760 'fields' => array(
3761 array(
3762 'type' => 'campaign-description',
3763 'headline' => '',
3764 'content' => '<p>On June 12, 2023 Brook’s wife Maleena was admitted to hospital while 20 weeks pregnant, where she was diagnosed with Stage 4 B-cell Lymphoma. At just 22 years old, she has been confronted with a life-altering diagnosis and is now facing a grueling fight. After experiencing inflammation and pain in her legs in March, Brooke spoke with her GP and sought treatment from chiropractors and physios.</p>',
3765 'width_percentage' => 100,
3766 'align' => 'left',
3767 ),
3768 array(
3769 'type' => 'photo',
3770 'default' => 'photo-2.jpg',
3771 ),
3772 array(
3773 'type' => 'text',
3774 'headline' => ' ',
3775 'content' => 'Devastated by Maleena\'s diagnosis, Brooke found himself navigating a whirlwind of emotions and responsibilities. He became Maleena\'s unwavering pillar of support, attending every doctor\'s appointment and chemotherapy session with her, his heart aching at the sight of his young wife enduring such pain. The couple, once planning nursery decorations and baby names, now found themselves discussing treatment options and potential outcomes. Despite the overwhelming fear and uncertainty, they clung to each other, drawing strength from their love and the life growing inside Maleena. Friends and family rallied around them, offering words of encouragement and practical help, reminding them that they were not alone in this battle. In the face of adversity, Brooke and Maleena vowed to fight together, holding onto hope and cherishing every moment they had together, knowing that their love would be the guiding light in the darkest of times.',
3776 ),
3777 array(
3778 'type' => 'photo',
3779 'default' => 'photo-1.jpg',
3780 ),
3781 ),
3782 ),
3783 array(
3784 'title' => 'Updates',
3785 'type' => '',
3786 'slug' => 'updates',
3787 'fields' => array(
3788 array(
3789 'type' => 'text',
3790 'headline' => ' ',
3791 'content' => 'This is on second tab.',
3792 ),
3793 ),
3794 ),
3795 array(
3796 'title' => 'Comments',
3797 'type' => '',
3798 'slug' => 'comments',
3799 'fields' => array(
3800 array(
3801 'type' => 'text',
3802 'headline' => ' ',
3803 'content' => 'This is on third tab.',
3804 ),
3805 ),
3806 ),
3807 ),
3808 ),
3809 ),
3810 array( // column.
3811 array( // section.
3812 'type' => 'fields',
3813 'fields' => array(
3814 array(
3815 'type' => 'campaign-summary',
3816 ),
3817 array(
3818 'type' => 'progress-bar',
3819 'show_hide' => array(
3820 'show_donated' => true,
3821 'show_goal' => true,
3822 ),
3823 'css_class' => 'sample-css-class',
3824 'label_donate' => ' ',
3825 'label_goal' => 'Goal USD: ',
3826 'meta_position' => 'top',
3827 ),
3828 array(
3829 'type' => 'text',
3830 'headline' => ' ',
3831 'content' => 'This project will only be funded if at least $270,000 is raised by December 22, 2025.',
3832 ),
3833 array(
3834 'type' => 'donate-button',
3835 'button_label' => 'Donate Now',
3836 'width_percentage' => 75,
3837 'align' => 'center',
3838 ),
3839 array(
3840 'type' => 'organizer',
3841 'role_or_title' => 'Organizer',
3842 'content' => '',
3843 ),
3844 ),
3845 ),
3846 ),
3847 ),
3848 ),
3849 ),
3850 'advanced' => array(
3851 'tab_style' => 'boxed',
3852 'tab_size' => 'medium',
3853 ),
3854 'settings' => array(
3855 'general' => array(
3856 'description' => '<p>On June 12, 2023 Brook’s wife Maleena was admitted to hospital while 20 weeks pregnant, where she was diagnosed with Stage 4 B-cell Lymphoma. At just 22 years old, she has been confronted with a life-altering diagnosis and is now facing a grueling fight. After experiencing inflammation and pain in her legs in March, Brooke spoke with her GP and sought treatment from chiropractors and physios.</p>',
3857 ),
3858 ),
3859 ),
3860
3861 ),
3862 );
3863 }
3864
3865 /**
3866 * Template for upgrade banner.
3867 *
3868 * @since 1.8.0
3869 */
3870 public function upgrade_banner_template() {
3871
3872 if ( charitable_is_pro() ) {
3873 return;
3874 }
3875
3876 $medium = esc_html__( 'Campaign Builder Templates', 'charitable' );
3877
3878 ?>
3879 <script type="text/html" id="tmpl-charitable-templates-upgrade-banner">
3880 <div class="charitable-template-upgrade-banner">
3881 <div class="charitable-template-content">
3882 <h3>
3883 <?php
3884 printf( esc_html__( 'Super Charge Your Campaigns And Donations With Pro Addons!', 'charitable' ) );
3885 ?>
3886 </h3>
3887
3888 <p>
3889 <?php
3890 // translators: %s is the amount saved.
3891 printf( esc_html__( 'With Charitable addons you can enable crowdfunding, provide additional payment methods, add videos, offer recurring donations, and more. As thanks to you for being a lite user you can unlock access and save up to $%s, automatically applied at checkout! ', 'charitable' ), '<strong>300.00</strong>' );
3892 ?>
3893 </p>
3894 </div>
3895 <div class="charitable-template-upgrade-button">
3896 <a href="<?php echo esc_url( charitable_pro_upgrade_url( $medium ) ); ?>" class="charitable-btn charitable-btn-orange charitable-btn-md" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Upgrade to PRO', 'charitable' ); ?></a>
3897 </div>
3898 </div>
3899 </script>
3900 <?php
3901 }
3902
3903 /**
3904 * Tab Empty Notice.
3905 *
3906 * @since 1.8.0
3907 */
3908 public function tab_empty_notice() {
3909
3910 return sprintf(
3911 /* translators: 1: Template ID */
3912 '<p class="empty-tab-notice">' . __( 'This tab is empty. Drag a block from the left into this area or ', 'charitable' ) . '<br/><strong><a href="%1$s" class="charitable-configure-tab-settings">' . __( 'configure tab settings', 'charitable' ) . '</a></strong>.</p>',
3913 '#'
3914 );
3915 }
3916
3917 /**
3918 * No Empty Notice.
3919 *
3920 * @since 1.8.0
3921 */
3922 public function no_tab_empty_notice() {
3923
3924 return sprintf(
3925 /* translators: 1: Template ID */
3926 __( 'There are no tabs yet for this template. You can ', 'charitable' ) . '<br/><strong><a href="%1$s" class="charitable-configure-tab-settings">' . __( 'configure tab settings', 'charitable' ) . '</a> to add a new tab</strong>.',
3927 '#'
3928 );
3929 }
3930 }
3931
3932 endif;
3933
3934 new Charitable_Campaign_Builder_Templates();
3935