| 1 |
<?php |
| 2 |
|
| 3 |
namespace Better_Payment\Lite\Campaign\Elements; |
| 4 |
|
| 5 |
if ( ! defined( 'ABSPATH' ) ) { |
| 6 |
exit; |
| 7 |
} |
| 8 |
|
| 9 |
/** |
| 10 |
* Registers all built-in campaign element types into the ElementRegistry. |
| 11 |
* |
| 12 |
* Mirrors Fluent Forms' DefaultElements.php pattern: a structured PHP |
| 13 |
* definition drives both the PHP renderer and the JS builder's settings panel. |
| 14 |
* |
| 15 |
* Each element schema shape: |
| 16 |
* [ |
| 17 |
* 'type' => string, // unique key |
| 18 |
* 'label' => string, // display name in builder |
| 19 |
* 'icon' => string, // dashicons suffix (without 'dashicons-') |
| 20 |
* 'defaultSettings' => array, // settings applied when element is dropped |
| 21 |
* 'settingsSchema' => array, // controls shown in the right panel |
| 22 |
* ] |
| 23 |
* |
| 24 |
* settingsSchema control shape: |
| 25 |
* [ |
| 26 |
* 'key' => string, // settings key |
| 27 |
* 'label' => string, // control label |
| 28 |
* 'type' => string, // text | number | color | toggle | select | textarea |
| 29 |
* 'placeholder' => string, // optional |
| 30 |
* 'min' => int, // for number type |
| 31 |
* 'max' => int, // for number type |
| 32 |
* 'rows' => int, // for textarea type |
| 33 |
* 'options' => array, // for select type: [['value'=>..., 'label'=>...], ...] |
| 34 |
* ] |
| 35 |
*/ |
| 36 |
class CampaignElements { |
| 37 |
|
| 38 |
/** |
| 39 |
* Curated web-safe font-family options shared by typography-enabled elements. |
| 40 |
* Value is the full CSS font stack; empty value inherits the theme font. |
| 41 |
* |
| 42 |
* @return array<int, array{value: string, label: string}> |
| 43 |
*/ |
| 44 |
public static function font_family_options(): array { |
| 45 |
$families = [ |
| 46 |
'' => __( 'Default', 'better-payment' ), |
| 47 |
'Arial, Helvetica, sans-serif' => 'Arial', |
| 48 |
"'Arial Black', Gadget, sans-serif" => 'Arial Black', |
| 49 |
"'Comic Sans MS', cursive, sans-serif" => 'Comic Sans MS', |
| 50 |
"'Courier New', Courier, monospace" => 'Courier New', |
| 51 |
'Georgia, serif' => 'Georgia', |
| 52 |
"'Helvetica Neue', Helvetica, Arial, sans-serif" => 'Helvetica', |
| 53 |
'Impact, Charcoal, sans-serif' => 'Impact', |
| 54 |
"'Lucida Sans Unicode', 'Lucida Grande', sans-serif" => 'Lucida Sans', |
| 55 |
"'Palatino Linotype', 'Book Antiqua', Palatino, serif" => 'Palatino', |
| 56 |
'Tahoma, Geneva, sans-serif' => 'Tahoma', |
| 57 |
"'Times New Roman', Times, serif" => 'Times New Roman', |
| 58 |
"'Trebuchet MS', Helvetica, sans-serif" => 'Trebuchet MS', |
| 59 |
'Verdana, Geneva, sans-serif' => 'Verdana', |
| 60 |
]; |
| 61 |
|
| 62 |
$options = []; |
| 63 |
foreach ( $families as $value => $label ) { |
| 64 |
$options[] = [ 'value' => $value, 'label' => $label ]; |
| 65 |
} |
| 66 |
|
| 67 |
return $options; |
| 68 |
} |
| 69 |
|
| 70 |
/** |
| 71 |
* Font-style options shared by typography-enabled elements. |
| 72 |
* |
| 73 |
* @return array<int, array{value: string, label: string}> |
| 74 |
*/ |
| 75 |
public static function font_style_options(): array { |
| 76 |
return [ |
| 77 |
[ 'value' => '', 'label' => __( 'Default', 'better-payment' ) ], |
| 78 |
[ 'value' => 'normal', 'label' => __( 'Normal', 'better-payment' ) ], |
| 79 |
[ 'value' => 'italic', 'label' => __( 'Italic', 'better-payment' ) ], |
| 80 |
[ 'value' => 'oblique', 'label' => __( 'Oblique', 'better-payment' ) ], |
| 81 |
]; |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* Font-weight options shared by typography-enabled elements. |
| 86 |
* |
| 87 |
* @return array<int, array{value: string, label: string}> |
| 88 |
*/ |
| 89 |
public static function font_weight_options(): array { |
| 90 |
return [ |
| 91 |
[ 'value' => '', 'label' => __( 'Default', 'better-payment' ) ], |
| 92 |
[ 'value' => '100', 'label' => __( '100 (Thin)', 'better-payment' ) ], |
| 93 |
[ 'value' => '200', 'label' => __( '200 (Extra Light)', 'better-payment' ) ], |
| 94 |
[ 'value' => '300', 'label' => __( '300 (Light)', 'better-payment' ) ], |
| 95 |
[ 'value' => '400', 'label' => __( '400 (Normal)', 'better-payment' ) ], |
| 96 |
[ 'value' => '500', 'label' => __( '500 (Medium)', 'better-payment' ) ], |
| 97 |
[ 'value' => '600', 'label' => __( '600 (Semi Bold)', 'better-payment' ) ], |
| 98 |
[ 'value' => '700', 'label' => __( '700 (Bold)', 'better-payment' ) ], |
| 99 |
[ 'value' => '800', 'label' => __( '800 (Extra Bold)', 'better-payment' ) ], |
| 100 |
[ 'value' => '900', 'label' => __( '900 (Black)', 'better-payment' ) ], |
| 101 |
]; |
| 102 |
} |
| 103 |
|
| 104 |
/** |
| 105 |
* Text-transform options shared by typography-enabled elements. |
| 106 |
* |
| 107 |
* @return array<int, array{value: string, label: string}> |
| 108 |
*/ |
| 109 |
public static function text_transform_options(): array { |
| 110 |
return [ |
| 111 |
[ 'value' => '', 'label' => __( 'Default', 'better-payment' ) ], |
| 112 |
[ 'value' => 'uppercase', 'label' => __( 'Uppercase', 'better-payment' ) ], |
| 113 |
[ 'value' => 'lowercase', 'label' => __( 'Lowercase', 'better-payment' ) ], |
| 114 |
[ 'value' => 'capitalize', 'label' => __( 'Capitalize', 'better-payment' ) ], |
| 115 |
[ 'value' => 'none', 'label' => __( 'Normal', 'better-payment' ) ], |
| 116 |
]; |
| 117 |
} |
| 118 |
|
| 119 |
/** |
| 120 |
* Text-decoration options shared by typography-enabled elements. |
| 121 |
* |
| 122 |
* @return array<int, array{value: string, label: string}> |
| 123 |
*/ |
| 124 |
public static function text_decoration_options(): array { |
| 125 |
return [ |
| 126 |
[ 'value' => '', 'label' => __( 'Default', 'better-payment' ) ], |
| 127 |
[ 'value' => 'underline', 'label' => __( 'Underline', 'better-payment' ) ], |
| 128 |
[ 'value' => 'overline', 'label' => __( 'Overline', 'better-payment' ) ], |
| 129 |
[ 'value' => 'line-through', 'label' => __( 'Line Through', 'better-payment' ) ], |
| 130 |
[ 'value' => 'none', 'label' => __( 'None', 'better-payment' ) ], |
| 131 |
]; |
| 132 |
} |
| 133 |
|
| 134 |
/** |
| 135 |
* Full typography control group shared by typography-enabled elements, |
| 136 |
* wrapped in a single collapsible "Typography" section. Mirrors Elementor's |
| 137 |
* Typography group (Family, Size, Weight, Transform, Style, Decoration, Line |
| 138 |
* Height, Letter Spacing, Word Spacing, Color). |
| 139 |
* |
| 140 |
* @param array $overrides Per-control overrides keyed by control key |
| 141 |
* (e.g. [ 'font_size' => [ 'defaultValue' => 32 ] ]). |
| 142 |
* Values are merged into that control descriptor. |
| 143 |
* @return array<int, array<string, mixed>> A single-element array holding the |
| 144 |
* collapsible section (type 'section') whose `children` |
| 145 |
* are the individual typography controls. |
| 146 |
*/ |
| 147 |
private static function typography_schema( array $overrides = [], string $prefix = '', string $section_label = '', string $section_key = '_typography' ): array { |
| 148 |
if ( '' === $section_label ) { |
| 149 |
$section_label = __( 'Typography', 'better-payment' ); |
| 150 |
} |
| 151 |
$children = [ |
| 152 |
[ |
| 153 |
'key' => 'font_family', |
| 154 |
'label' => __( 'Font Family', 'better-payment' ), |
| 155 |
'type' => 'select', |
| 156 |
'defaultValue' => '', |
| 157 |
'options' => self::font_family_options(), |
| 158 |
], |
| 159 |
[ |
| 160 |
'key' => 'font_size', |
| 161 |
'label' => __( 'Font Size (px)', 'better-payment' ), |
| 162 |
'type' => 'number', |
| 163 |
'min' => 8, |
| 164 |
'max' => 200, |
| 165 |
], |
| 166 |
[ |
| 167 |
'key' => 'font_weight', |
| 168 |
'label' => __( 'Font Weight', 'better-payment' ), |
| 169 |
'type' => 'select', |
| 170 |
'defaultValue' => '', |
| 171 |
'options' => self::font_weight_options(), |
| 172 |
], |
| 173 |
[ |
| 174 |
'key' => 'text_transform', |
| 175 |
'label' => __( 'Text Transform', 'better-payment' ), |
| 176 |
'type' => 'select', |
| 177 |
'defaultValue' => '', |
| 178 |
'options' => self::text_transform_options(), |
| 179 |
], |
| 180 |
[ |
| 181 |
'key' => 'font_style', |
| 182 |
'label' => __( 'Font Style', 'better-payment' ), |
| 183 |
'type' => 'select', |
| 184 |
'defaultValue' => '', |
| 185 |
'options' => self::font_style_options(), |
| 186 |
], |
| 187 |
[ |
| 188 |
'key' => 'text_decoration', |
| 189 |
'label' => __( 'Text Decoration', 'better-payment' ), |
| 190 |
'type' => 'select', |
| 191 |
'defaultValue' => '', |
| 192 |
'options' => self::text_decoration_options(), |
| 193 |
], |
| 194 |
[ |
| 195 |
'key' => 'line_height', |
| 196 |
'label' => __( 'Line Height (px)', 'better-payment' ), |
| 197 |
'type' => 'number', |
| 198 |
'min' => 0, |
| 199 |
'max' => 400, |
| 200 |
], |
| 201 |
[ |
| 202 |
'key' => 'letter_spacing', |
| 203 |
'label' => __( 'Letter Spacing (px)', 'better-payment' ), |
| 204 |
'type' => 'number', |
| 205 |
'min' => -20, |
| 206 |
'max' => 100, |
| 207 |
], |
| 208 |
[ |
| 209 |
'key' => 'word_spacing', |
| 210 |
'label' => __( 'Word Spacing (px)', 'better-payment' ), |
| 211 |
'type' => 'number', |
| 212 |
'min' => -20, |
| 213 |
'max' => 100, |
| 214 |
], |
| 215 |
[ |
| 216 |
'key' => 'color', |
| 217 |
'label' => __( 'Text Color', 'better-payment' ), |
| 218 |
'type' => 'color', |
| 219 |
], |
| 220 |
]; |
| 221 |
|
| 222 |
// Overrides are keyed by the BASE control key (e.g. 'font_size'), applied |
| 223 |
// before the prefix so callers don't need to know the prefix. |
| 224 |
if ( ! empty( $overrides ) ) { |
| 225 |
foreach ( $children as $i => $control ) { |
| 226 |
if ( isset( $overrides[ $control['key'] ] ) ) { |
| 227 |
$children[ $i ] = array_merge( $control, $overrides[ $control['key'] ] ); |
| 228 |
} |
| 229 |
} |
| 230 |
} |
| 231 |
|
| 232 |
// Prefix the control keys so a single element can carry more than one |
| 233 |
// independent typography set. The Description widget uses this to style |
| 234 |
// its title and body separately ('title_font_family' vs 'font_family'); |
| 235 |
// the renderer strips the prefix before applying the CSS. |
| 236 |
if ( '' !== $prefix ) { |
| 237 |
foreach ( $children as $i => $control ) { |
| 238 |
$children[ $i ]['key'] = $prefix . $control['key']; |
| 239 |
} |
| 240 |
} |
| 241 |
|
| 242 |
// Return the controls wrapped in a single collapsible section (type => |
| 243 |
// 'section' with children) instead of a flat section_label + siblings. |
| 244 |
// The builder renders this as one expandable accordion (see |
| 245 |
// RightSidebar.js CollapsibleSection). |
| 246 |
return [ |
| 247 |
[ |
| 248 |
'key' => $section_key, |
| 249 |
'label' => $section_label, |
| 250 |
'type' => 'section', |
| 251 |
'collapsible' => true, |
| 252 |
'collapsed' => true, |
| 253 |
'children' => $children, |
| 254 |
], |
| 255 |
]; |
| 256 |
} |
| 257 |
|
| 258 |
public static function register_all(): void { |
| 259 |
|
| 260 |
ElementRegistry::register( 'campaign_title', [ |
| 261 |
'label' => __( 'Campaign Title', 'better-payment' ), |
| 262 |
'icon' => 'heading', |
| 263 |
// Typography keys are intentionally NOT seeded here — their absence is |
| 264 |
// how the renderer distinguishes a user override (emitted with |
| 265 |
// !important to beat template styles) from the template default. |
| 266 |
'defaultSettings' => [ |
| 267 |
'align' => 'left', |
| 268 |
], |
| 269 |
'settingsSchema' => array_merge( |
| 270 |
[ |
| 271 |
[ |
| 272 |
'key' => 'title', |
| 273 |
'label' => __( 'Campaign Title', 'better-payment' ), |
| 274 |
'type' => 'text', |
| 275 |
'placeholder' => __( 'Campaign name', 'better-payment' ), |
| 276 |
], |
| 277 |
], |
| 278 |
self::typography_schema( [ |
| 279 |
'font_size' => [ 'defaultValue' => 32 ], |
| 280 |
'color' => [ 'defaultValue' => '#1a1a2e' ], |
| 281 |
] ), |
| 282 |
[ |
| 283 |
[ |
| 284 |
'key' => 'align', |
| 285 |
'label' => __( 'Align', 'better-payment' ), |
| 286 |
'type' => 'align', |
| 287 |
], |
| 288 |
] |
| 289 |
), |
| 290 |
] ); |
| 291 |
|
| 292 |
ElementRegistry::register( 'campaign_description', [ |
| 293 |
'label' => __( 'Campaign Description', 'better-payment' ), |
| 294 |
'icon' => 'editor-paragraph', |
| 295 |
// Typography keys are intentionally NOT seeded — their absence marks a |
| 296 |
// user override, which the renderer emits with !important so it wins |
| 297 |
// over template styles. |
| 298 |
'defaultSettings' => [ |
| 299 |
'headline' => 'Campaign Description', |
| 300 |
'content' => "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).", |
| 301 |
'width' => 100, |
| 302 |
'align' => 'left', |
| 303 |
], |
| 304 |
'settingsSchema' => array_merge( |
| 305 |
[ |
| 306 |
[ |
| 307 |
'key' => 'headline', |
| 308 |
'label' => __( 'Headline', 'better-payment' ), |
| 309 |
'type' => 'text', |
| 310 |
'placeholder' => __( 'Headline', 'better-payment' ), |
| 311 |
], |
| 312 |
], |
| 313 |
// Title Typography — styles the headline (h3) only. Uses `title_` |
| 314 |
// prefixed keys so it stays independent of the body typography. |
| 315 |
self::typography_schema( |
| 316 |
[ |
| 317 |
'font_size' => [ |
| 318 |
'info' => __( 'Leave empty to use the heading default size.', 'better-payment' ), |
| 319 |
], |
| 320 |
], |
| 321 |
'title_', |
| 322 |
__( 'Title Typography', 'better-payment' ), |
| 323 |
'_title_typography' |
| 324 |
), |
| 325 |
[ |
| 326 |
[ |
| 327 |
'key' => 'content', |
| 328 |
'label' => __( 'Campaign Description', 'better-payment' ), |
| 329 |
'type' => 'rich_text', |
| 330 |
'info' => __( 'Supports bold, italic, underline, links, and lists.', 'better-payment' ), |
| 331 |
], |
| 332 |
], |
| 333 |
// Description Typography — styles the body content. Keeps the base |
| 334 |
// (unprefixed) keys so previously-saved description typography |
| 335 |
// continues to apply to the content. |
| 336 |
self::typography_schema( |
| 337 |
[ |
| 338 |
'font_size' => [ |
| 339 |
'placeholder' => '16', |
| 340 |
'info' => __( 'Leave empty to use the template default size.', 'better-payment' ), |
| 341 |
], |
| 342 |
], |
| 343 |
'', |
| 344 |
__( 'Description Typography', 'better-payment' ), |
| 345 |
'_content_typography' |
| 346 |
), |
| 347 |
[ |
| 348 |
[ |
| 349 |
'key' => 'width', |
| 350 |
'label' => __( 'Width', 'better-payment' ), |
| 351 |
'type' => 'range', |
| 352 |
'min' => 10, |
| 353 |
'max' => 100, |
| 354 |
'step' => 1, |
| 355 |
'unit' => '%', |
| 356 |
'defaultValue' => 100, |
| 357 |
'info' => __( 'Content width as a percentage of its container.', 'better-payment' ), |
| 358 |
], |
| 359 |
[ |
| 360 |
'key' => 'align', |
| 361 |
'label' => __( 'Align', 'better-payment' ), |
| 362 |
'type' => 'align', |
| 363 |
], |
| 364 |
] |
| 365 |
), |
| 366 |
] ); |
| 367 |
|
| 368 |
ElementRegistry::register( 'photo', [ |
| 369 |
'label' => __( 'Photo', 'better-payment' ), |
| 370 |
'icon' => 'format-image', |
| 371 |
'defaultSettings' => [ |
| 372 |
'src' => '', |
| 373 |
'src_id' => 0, |
| 374 |
'src_sizes' => [], |
| 375 |
'alt' => '', |
| 376 |
'size' => 'full', |
| 377 |
'width' => 100, |
| 378 |
'align' => 'center', |
| 379 |
], |
| 380 |
'settingsSchema' => [ |
| 381 |
[ |
| 382 |
'key' => 'src', |
| 383 |
'label' => __( 'Choose Image', 'better-payment' ), |
| 384 |
'type' => 'image_upload', |
| 385 |
'info' => __( 'Paste an external image URL or select from the media library.', 'better-payment' ), |
| 386 |
], |
| 387 |
[ |
| 388 |
'key' => 'alt', |
| 389 |
'label' => __( 'ALT Text', 'better-payment' ), |
| 390 |
'type' => 'text', |
| 391 |
'placeholder' => __( 'Describe the image…', 'better-payment' ), |
| 392 |
'info' => __( 'Describes the image for screen readers and when the image fails to load.', 'better-payment' ), |
| 393 |
], |
| 394 |
[ |
| 395 |
'key' => 'size', |
| 396 |
'label' => __( 'Image Resolution', 'better-payment' ), |
| 397 |
'type' => 'select', |
| 398 |
'defaultValue' => 'full', |
| 399 |
'options' => [ |
| 400 |
[ 'value' => 'thumbnail', 'label' => __( 'Thumbnail (150×150)', 'better-payment' ) ], |
| 401 |
[ 'value' => 'medium', 'label' => __( 'Medium (300×300)', 'better-payment' ) ], |
| 402 |
[ 'value' => 'medium_large', 'label' => __( 'Medium Large (768×auto)', 'better-payment' ) ], |
| 403 |
[ 'value' => 'large', 'label' => __( 'Large (1024×1024)', 'better-payment' ) ], |
| 404 |
[ 'value' => 'full', 'label' => __( 'Full (Original)', 'better-payment' ) ], |
| 405 |
], |
| 406 |
], |
| 407 |
[ |
| 408 |
'key' => 'width', |
| 409 |
'label' => __( 'Width', 'better-payment' ), |
| 410 |
'type' => 'range', |
| 411 |
'min' => 10, |
| 412 |
'max' => 100, |
| 413 |
'step' => 1, |
| 414 |
'unit' => '%', |
| 415 |
'defaultValue' => 100, |
| 416 |
'info' => __( 'Image width as a percentage of its container.', 'better-payment' ), |
| 417 |
], |
| 418 |
[ |
| 419 |
'key' => 'align', |
| 420 |
'label' => __( 'Align', 'better-payment' ), |
| 421 |
'type' => 'align', |
| 422 |
], |
| 423 |
], |
| 424 |
] ); |
| 425 |
|
| 426 |
ElementRegistry::register( 'progress_bar', [ |
| 427 |
'label' => __( 'Progress Bar', 'better-payment' ), |
| 428 |
'icon' => 'chart-bar', |
| 429 |
'defaultSettings' => [ |
| 430 |
'headline' => 'Campaign Progress', |
| 431 |
'show_donated' => true, |
| 432 |
'show_goal' => true, |
| 433 |
'round_amounts' => false, |
| 434 |
'donate_label' => 'Donated:', |
| 435 |
'goal_label' => 'Goal:', |
| 436 |
'width' => 100, |
| 437 |
'align' => 'left', |
| 438 |
], |
| 439 |
'settingsSchema' => [ |
| 440 |
[ |
| 441 |
'key' => 'headline', |
| 442 |
'label' => __( 'Headline', 'better-payment' ), |
| 443 |
'type' => 'text', |
| 444 |
'defaultValue' => 'Campaign Progress', |
| 445 |
], |
| 446 |
[ |
| 447 |
'key' => '_campaign_info', |
| 448 |
'label' => __( 'Campaign Information', 'better-payment' ), |
| 449 |
'type' => 'section_label', |
| 450 |
], |
| 451 |
[ |
| 452 |
'key' => 'show_donated', |
| 453 |
'label' => __( 'Show Donated', 'better-payment' ), |
| 454 |
'type' => 'switch', |
| 455 |
'defaultValue' => true, |
| 456 |
], |
| 457 |
[ |
| 458 |
'key' => 'show_goal', |
| 459 |
'label' => __( 'Show Goal', 'better-payment' ), |
| 460 |
'type' => 'switch', |
| 461 |
'defaultValue' => true, |
| 462 |
], |
| 463 |
[ |
| 464 |
'key' => 'round_amounts', |
| 465 |
'label' => __( 'Round Amounts', 'better-payment' ), |
| 466 |
'type' => 'toggle', |
| 467 |
'defaultValue' => false, |
| 468 |
], |
| 469 |
// Both of these are PREFIXES — RendererService prints the live |
| 470 |
// value straight after them ("Donated: 40%", "Goal: €5,000"). The |
| 471 |
// `info` text says so because the field name alone does not, and |
| 472 |
// an author (or the AI, which reads this schema) who types the |
| 473 |
// whole phrase gets the value rendered twice. |
| 474 |
[ |
| 475 |
'key' => 'donate_label', |
| 476 |
'label' => __( 'Donate Label:', 'better-payment' ), |
| 477 |
'type' => 'text', |
| 478 |
'defaultValue' => 'Donated:', |
| 479 |
'info' => __( 'Shown before the progress percentage — the percentage is added automatically. Do not type a number here.', 'better-payment' ), |
| 480 |
], |
| 481 |
[ |
| 482 |
'key' => 'goal_label', |
| 483 |
'label' => __( 'Goal Label:', 'better-payment' ), |
| 484 |
'type' => 'text', |
| 485 |
'defaultValue' => 'Goal:', |
| 486 |
'info' => __( 'Shown before the goal amount — the amount and currency are added automatically. Do not type an amount here.', 'better-payment' ), |
| 487 |
], |
| 488 |
[ |
| 489 |
'key' => 'width', |
| 490 |
'label' => __( 'Width', 'better-payment' ), |
| 491 |
'type' => 'range', |
| 492 |
'min' => 10, |
| 493 |
'max' => 100, |
| 494 |
'step' => 1, |
| 495 |
'unit' => '%', |
| 496 |
'defaultValue' => 100, |
| 497 |
'info' => __( 'Controls the width of the progress bar relative to its container.', 'better-payment' ), |
| 498 |
], |
| 499 |
[ |
| 500 |
'key' => 'align', |
| 501 |
'label' => __( 'Align', 'better-payment' ), |
| 502 |
'type' => 'align', |
| 503 |
'defaultValue' => 'left', |
| 504 |
], |
| 505 |
], |
| 506 |
] ); |
| 507 |
|
| 508 |
ElementRegistry::register( 'campaign_summary', [ |
| 509 |
'label' => __( 'Campaign Summary', 'better-payment' ), |
| 510 |
'icon' => 'info', |
| 511 |
'defaultSettings' => [ |
| 512 |
'headline' => 'Campaign Summary', |
| 513 |
'show_raised' => true, |
| 514 |
'show_donors' => true, |
| 515 |
'show_percent' => true, |
| 516 |
'show_days' => true, |
| 517 |
'width' => 100, |
| 518 |
'align' => 'left', |
| 519 |
], |
| 520 |
'settingsSchema' => [ |
| 521 |
[ |
| 522 |
'key' => 'headline', |
| 523 |
'label' => __( 'Headline', 'better-payment' ), |
| 524 |
'type' => 'text', |
| 525 |
'placeholder' => '', |
| 526 |
'defaultValue' => 'Campaign Summary', |
| 527 |
], |
| 528 |
[ |
| 529 |
'key' => 'choose_label', |
| 530 |
'label' => __( 'Choose what information to show:', 'better-payment' ), |
| 531 |
'type' => 'section_label', |
| 532 |
], |
| 533 |
[ |
| 534 |
'key' => 'summary_note', |
| 535 |
'label' => __( 'Note: All enabled items are always shown. Amount Donated and Number of Donors update as donations come in. Percent Raised shows 0% until a campaign goal is set, and Time Remaining shows 0 days left until an end date is set. Save and refresh the builder to see updated values.', 'better-payment' ), |
| 536 |
'type' => 'note', |
| 537 |
], |
| 538 |
[ |
| 539 |
'key' => 'show_raised', |
| 540 |
'label' => __( 'Amount Donated', 'better-payment' ), |
| 541 |
'type' => 'toggle', |
| 542 |
], |
| 543 |
[ |
| 544 |
'key' => 'show_donors', |
| 545 |
'label' => __( 'Number of Donors', 'better-payment' ), |
| 546 |
'type' => 'toggle', |
| 547 |
], |
| 548 |
[ |
| 549 |
'key' => 'show_percent', |
| 550 |
'label' => __( 'Percent Raised', 'better-payment' ), |
| 551 |
'type' => 'toggle', |
| 552 |
], |
| 553 |
[ |
| 554 |
'key' => 'show_days', |
| 555 |
'label' => __( 'Time Remaining', 'better-payment' ), |
| 556 |
'type' => 'toggle', |
| 557 |
], |
| 558 |
[ |
| 559 |
'key' => 'width', |
| 560 |
'label' => __( 'Width', 'better-payment' ), |
| 561 |
'type' => 'range', |
| 562 |
'min' => 10, |
| 563 |
'max' => 100, |
| 564 |
'unit' => '%', |
| 565 |
], |
| 566 |
[ |
| 567 |
'key' => 'align', |
| 568 |
'label' => __( 'Align', 'better-payment' ), |
| 569 |
'type' => 'align', |
| 570 |
], |
| 571 |
], |
| 572 |
] ); |
| 573 |
|
| 574 |
ElementRegistry::register( 'donation_form', [ |
| 575 |
'label' => __( 'Donate Button', 'better-payment' ), |
| 576 |
'icon' => 'heart', |
| 577 |
'defaultSettings' => [ |
| 578 |
'button_label' => 'Donate Now', |
| 579 |
'button_color' => '', |
| 580 |
'url' => '#', |
| 581 |
'open_new_tab' => false, |
| 582 |
'width' => 100, |
| 583 |
'align' => 'center', |
| 584 |
], |
| 585 |
'settingsSchema' => [ |
| 586 |
[ |
| 587 |
'key' => 'button_label', |
| 588 |
'label' => __( 'Button Label', 'better-payment' ), |
| 589 |
'type' => 'text', |
| 590 |
'placeholder' => __( 'Donate Now', 'better-payment' ), |
| 591 |
'defaultValue' => 'Donate Now', |
| 592 |
], |
| 593 |
[ |
| 594 |
'key' => 'button_color', |
| 595 |
'label' => __( 'Button Color', 'better-payment' ), |
| 596 |
'type' => 'color', |
| 597 |
'info' => __( 'Leave empty to use the campaign primary color.', 'better-payment' ), |
| 598 |
], |
| 599 |
[ |
| 600 |
'key' => 'url', |
| 601 |
'label' => __( 'Payment Form Page URL', 'better-payment' ), |
| 602 |
'type' => 'url', |
| 603 |
'placeholder' => 'https://', |
| 604 |
'defaultValue' => '#', |
| 605 |
'info' => __( 'Link to the page containing your donation form. Overrides the Donation Page set in General Settings.', 'better-payment' ), |
| 606 |
], |
| 607 |
[ |
| 608 |
'key' => 'open_new_tab', |
| 609 |
'label' => __( 'Open Links In New Tab', 'better-payment' ), |
| 610 |
'type' => 'switch', |
| 611 |
'defaultValue' => false, |
| 612 |
], |
| 613 |
[ |
| 614 |
'key' => 'width', |
| 615 |
'label' => __( 'Width', 'better-payment' ), |
| 616 |
'type' => 'range', |
| 617 |
'min' => 10, |
| 618 |
'max' => 100, |
| 619 |
'step' => 1, |
| 620 |
'unit' => '%', |
| 621 |
'defaultValue' => 100, |
| 622 |
], |
| 623 |
[ |
| 624 |
'key' => 'align', |
| 625 |
'label' => __( 'Align', 'better-payment' ), |
| 626 |
'type' => 'align', |
| 627 |
'defaultValue' => 'center', |
| 628 |
], |
| 629 |
], |
| 630 |
] ); |
| 631 |
|
| 632 |
// Build WP users list for the Campaign Creator select. |
| 633 |
$wp_users = get_users( [ 'fields' => [ 'ID', 'display_name', 'user_email' ] ] ); |
| 634 |
$creator_options = array_values( array_map( function ( $u ) { |
| 635 |
return [ |
| 636 |
'value' => (int) $u->ID, |
| 637 |
'label' => $u->display_name, |
| 638 |
'avatar_url' => get_avatar_url( $u->user_email, [ 'size' => 48, 'default' => 'mysteryman' ] ), |
| 639 |
]; |
| 640 |
}, $wp_users ) ); |
| 641 |
|
| 642 |
ElementRegistry::register( 'organizer', [ |
| 643 |
'label' => __( 'Organizer', 'better-payment' ), |
| 644 |
'icon' => 'admin-users', |
| 645 |
'defaultSettings' => [ |
| 646 |
'creator_user_id' => get_current_user_id(), |
| 647 |
'role_title' => 'Organizer', |
| 648 |
'description' => '', |
| 649 |
'width' => 100, |
| 650 |
'align' => 'left', |
| 651 |
], |
| 652 |
'settingsSchema' => [ |
| 653 |
[ |
| 654 |
'key' => 'role_title', |
| 655 |
'label' => __( 'Role or Title', 'better-payment' ), |
| 656 |
'type' => 'text', |
| 657 |
'placeholder' => 'Organizer', |
| 658 |
'defaultValue' => 'Organizer', |
| 659 |
'info' => __( 'Shown beneath the creator\'s name (e.g. Organizer, Founder).', 'better-payment' ), |
| 660 |
], |
| 661 |
[ |
| 662 |
'key' => 'creator_user_id', |
| 663 |
'label' => __( 'Campaign Creator', 'better-payment' ), |
| 664 |
'type' => 'select', |
| 665 |
'options' => $creator_options, |
| 666 |
], |
| 667 |
[ |
| 668 |
'key' => 'description', |
| 669 |
'label' => __( 'Organizer Description', 'better-payment' ), |
| 670 |
'type' => 'rich_text', |
| 671 |
'info' => __( 'Brief bio or message shown beneath the creator\'s name.', 'better-payment' ), |
| 672 |
], |
| 673 |
[ |
| 674 |
'key' => 'width', |
| 675 |
'label' => __( 'Width', 'better-payment' ), |
| 676 |
'type' => 'range', |
| 677 |
'min' => 10, |
| 678 |
'max' => 100, |
| 679 |
'step' => 1, |
| 680 |
'unit' => '%', |
| 681 |
'defaultValue' => 100, |
| 682 |
'info' => __( 'Width of the organizer block as a percentage.', 'better-payment' ), |
| 683 |
], |
| 684 |
[ |
| 685 |
'key' => 'align', |
| 686 |
'label' => __( 'Align', 'better-payment' ), |
| 687 |
'type' => 'align', |
| 688 |
'defaultValue' => 'left', |
| 689 |
], |
| 690 |
], |
| 691 |
] ); |
| 692 |
|
| 693 |
ElementRegistry::register( 'donate_amount', [ |
| 694 |
'label' => __( 'Donate Amount', 'better-payment' ), |
| 695 |
'icon' => 'money-alt', |
| 696 |
'defaultSettings' => [ |
| 697 |
'headline' => 'Donate Amount', |
| 698 |
'width' => 100, |
| 699 |
'align' => 'left', |
| 700 |
], |
| 701 |
'settingsSchema' => [ |
| 702 |
[ |
| 703 |
'key' => 'headline', |
| 704 |
'label' => __( 'Headline', 'better-payment' ), |
| 705 |
'type' => 'text', |
| 706 |
'placeholder' => __( 'Headline', 'better-payment' ), |
| 707 |
'defaultValue' => 'Donate Amount', |
| 708 |
'info' => __( 'Optional heading shown above the donation amounts.', 'better-payment' ), |
| 709 |
], |
| 710 |
[ |
| 711 |
'key' => 'bpc_minimum_amount', |
| 712 |
'label' => __( 'Minimum Donation Amount', 'better-payment' ), |
| 713 |
'type' => 'currency', |
| 714 |
'metaKey' => 'bpc_minimum_amount', |
| 715 |
'info' => __( 'Leave empty to allow no restrictions on how small the donation can be.', 'better-payment' ), |
| 716 |
], |
| 717 |
[ |
| 718 |
'key' => 'bpc_suggested_amounts', |
| 719 |
'label' => __( 'Suggested Donation Amounts', 'better-payment' ), |
| 720 |
'type' => 'suggested_amounts', |
| 721 |
'metaKey' => 'bpc_suggested_amounts', |
| 722 |
], |
| 723 |
[ |
| 724 |
'key' => 'bpc_allow_custom_amount', |
| 725 |
'label' => __( 'Allow Custom Amount', 'better-payment' ), |
| 726 |
'type' => 'switch', |
| 727 |
'metaKey' => 'bpc_allow_custom_amount', |
| 728 |
'defaultValue' => true, |
| 729 |
], |
| 730 |
[ |
| 731 |
'key' => 'width', |
| 732 |
'label' => __( 'Width', 'better-payment' ), |
| 733 |
'type' => 'range', |
| 734 |
'min' => 10, |
| 735 |
'max' => 100, |
| 736 |
'step' => 1, |
| 737 |
'unit' => '%', |
| 738 |
'defaultValue' => 100, |
| 739 |
'info' => __( 'Controls the width of the donation amounts relative to its container.', 'better-payment' ), |
| 740 |
], |
| 741 |
[ |
| 742 |
'key' => 'align', |
| 743 |
'label' => __( 'Align', 'better-payment' ), |
| 744 |
'type' => 'align', |
| 745 |
'defaultValue' => 'left', |
| 746 |
], |
| 747 |
], |
| 748 |
] ); |
| 749 |
|
| 750 |
ElementRegistry::register( 'social_sharing', [ |
| 751 |
'label' => __( 'Social Sharing', 'better-payment' ), |
| 752 |
'icon' => 'share', |
| 753 |
'defaultSettings' => [ |
| 754 |
'headline' => 'Share Now', |
| 755 |
'twitter' => true, |
| 756 |
'facebook' => true, |
| 757 |
'linkedin' => true, |
| 758 |
'pinterest' => true, |
| 759 |
'mastodon' => true, |
| 760 |
'threads' => true, |
| 761 |
'bluesky' => true, |
| 762 |
'open_new_tab' => true, |
| 763 |
'align' => 'left', |
| 764 |
], |
| 765 |
'settingsSchema' => [ |
| 766 |
[ |
| 767 |
'key' => 'headline', |
| 768 |
'label' => __( 'Headline', 'better-payment' ), |
| 769 |
'type' => 'text', |
| 770 |
'placeholder' => __( 'Headline', 'better-payment' ), |
| 771 |
'defaultValue' => 'Share Now', |
| 772 |
], |
| 773 |
[ |
| 774 |
'key' => '_networks', |
| 775 |
'label' => __( 'Choose your social network(s):', 'better-payment' ), |
| 776 |
'type' => 'section_label', |
| 777 |
], |
| 778 |
[ |
| 779 |
'key' => 'twitter', |
| 780 |
'label' => __( 'Twitter / X', 'better-payment' ), |
| 781 |
'type' => 'toggle', |
| 782 |
'defaultValue' => true, |
| 783 |
], |
| 784 |
[ |
| 785 |
'key' => 'facebook', |
| 786 |
'label' => __( 'Facebook', 'better-payment' ), |
| 787 |
'type' => 'toggle', |
| 788 |
'defaultValue' => true, |
| 789 |
], |
| 790 |
[ |
| 791 |
'key' => 'linkedin', |
| 792 |
'label' => __( 'LinkedIn', 'better-payment' ), |
| 793 |
'type' => 'toggle', |
| 794 |
'defaultValue' => true, |
| 795 |
], |
| 796 |
[ |
| 797 |
'key' => 'pinterest', |
| 798 |
'label' => __( 'Pinterest', 'better-payment' ), |
| 799 |
'type' => 'toggle', |
| 800 |
'defaultValue' => true, |
| 801 |
], |
| 802 |
[ |
| 803 |
'key' => 'mastodon', |
| 804 |
'label' => __( 'Mastodon', 'better-payment' ), |
| 805 |
'type' => 'toggle', |
| 806 |
'defaultValue' => true, |
| 807 |
], |
| 808 |
[ |
| 809 |
'key' => 'threads', |
| 810 |
'label' => __( 'Threads', 'better-payment' ), |
| 811 |
'type' => 'toggle', |
| 812 |
'defaultValue' => true, |
| 813 |
], |
| 814 |
[ |
| 815 |
'key' => 'bluesky', |
| 816 |
'label' => __( 'Bluesky', 'better-payment' ), |
| 817 |
'type' => 'toggle', |
| 818 |
'defaultValue' => true, |
| 819 |
], |
| 820 |
[ |
| 821 |
'key' => 'open_new_tab', |
| 822 |
'label' => __( 'Open Links In New Tab', 'better-payment' ), |
| 823 |
'type' => 'switch', |
| 824 |
'defaultValue' => true, |
| 825 |
], |
| 826 |
[ |
| 827 |
'key' => 'align', |
| 828 |
'label' => __( 'Align', 'better-payment' ), |
| 829 |
'type' => 'align', |
| 830 |
], |
| 831 |
], |
| 832 |
] ); |
| 833 |
|
| 834 |
ElementRegistry::register( 'social_links', [ |
| 835 |
'label' => __( 'Social Links', 'better-payment' ), |
| 836 |
'icon' => 'admin-links', |
| 837 |
'defaultSettings' => [ |
| 838 |
'headline' => 'Follow Now', |
| 839 |
'twitter' => 'https://twitter.com/', |
| 840 |
'facebook' => 'https://facebook.com/', |
| 841 |
'linkedin' => 'https://linkedin.com/', |
| 842 |
'instagram' => '', |
| 843 |
'tiktok' => '', |
| 844 |
'pinterest' => '', |
| 845 |
'youtube' => '', |
| 846 |
'threads' => '', |
| 847 |
'bluesky' => '', |
| 848 |
'mastodon' => '', |
| 849 |
'open_new_tab' => true, |
| 850 |
'align' => 'left', |
| 851 |
], |
| 852 |
'settingsSchema' => [ |
| 853 |
[ |
| 854 |
'key' => 'headline', |
| 855 |
'label' => __( 'Headline', 'better-payment' ), |
| 856 |
'type' => 'text', |
| 857 |
'placeholder' => __( 'Headline', 'better-payment' ), |
| 858 |
'defaultValue' => 'Follow Now', |
| 859 |
], |
| 860 |
[ |
| 861 |
'key' => 'twitter', |
| 862 |
'label' => __( 'Twitter / X URL', 'better-payment' ), |
| 863 |
'type' => 'url', |
| 864 |
'placeholder' => 'https://', |
| 865 |
], |
| 866 |
[ |
| 867 |
'key' => 'facebook', |
| 868 |
'label' => __( 'Facebook URL', 'better-payment' ), |
| 869 |
'type' => 'url', |
| 870 |
'placeholder' => 'https://', |
| 871 |
], |
| 872 |
[ |
| 873 |
'key' => 'linkedin', |
| 874 |
'label' => __( 'LinkedIn URL', 'better-payment' ), |
| 875 |
'type' => 'url', |
| 876 |
'placeholder' => 'https://', |
| 877 |
], |
| 878 |
[ |
| 879 |
'key' => 'instagram', |
| 880 |
'label' => __( 'Instagram URL', 'better-payment' ), |
| 881 |
'type' => 'url', |
| 882 |
'placeholder' => 'https://', |
| 883 |
], |
| 884 |
[ |
| 885 |
'key' => 'tiktok', |
| 886 |
'label' => __( 'TikTok URL', 'better-payment' ), |
| 887 |
'type' => 'url', |
| 888 |
'placeholder' => 'https://', |
| 889 |
], |
| 890 |
[ |
| 891 |
'key' => 'pinterest', |
| 892 |
'label' => __( 'Pinterest URL', 'better-payment' ), |
| 893 |
'type' => 'url', |
| 894 |
'placeholder' => 'https://', |
| 895 |
], |
| 896 |
[ |
| 897 |
'key' => 'youtube', |
| 898 |
'label' => __( 'YouTube URL', 'better-payment' ), |
| 899 |
'type' => 'url', |
| 900 |
'placeholder' => 'https://', |
| 901 |
], |
| 902 |
[ |
| 903 |
'key' => 'threads', |
| 904 |
'label' => __( 'Threads URL', 'better-payment' ), |
| 905 |
'type' => 'url', |
| 906 |
'placeholder' => 'https://', |
| 907 |
], |
| 908 |
[ |
| 909 |
'key' => 'bluesky', |
| 910 |
'label' => __( 'Bluesky URL', 'better-payment' ), |
| 911 |
'type' => 'url', |
| 912 |
'placeholder' => 'https://', |
| 913 |
], |
| 914 |
[ |
| 915 |
'key' => 'mastodon', |
| 916 |
'label' => __( 'Mastodon URL', 'better-payment' ), |
| 917 |
'type' => 'url', |
| 918 |
'placeholder' => 'https://', |
| 919 |
], |
| 920 |
[ |
| 921 |
'key' => 'open_new_tab', |
| 922 |
'label' => __( 'Open Links In New Tab', 'better-payment' ), |
| 923 |
'type' => 'switch', |
| 924 |
'defaultValue' => true, |
| 925 |
], |
| 926 |
[ |
| 927 |
'key' => 'align', |
| 928 |
'label' => __( 'Align', 'better-payment' ), |
| 929 |
'type' => 'align', |
| 930 |
], |
| 931 |
], |
| 932 |
] ); |
| 933 |
|
| 934 |
self::register_pro_elements(); |
| 935 |
} |
| 936 |
|
| 937 |
/** |
| 938 |
* Register the three Pro-only elements (Donors Wall, FAQ, Video) with their |
| 939 |
* FULL control schemas, flagged `pro => true`. |
| 940 |
* |
| 941 |
* These used to be label-and-icon stubs with an empty `settingsSchema`, which |
| 942 |
* left a free user staring at a bare "upgrade" panel — no way to see what the |
| 943 |
* element actually does or what it can be configured to do. Lite now ships the |
| 944 |
* complete schema (see {@see ProElementSchemas}) so the builder can render |
| 945 |
* every control, disabled, behind a locked banner. |
| 946 |
* |
| 947 |
* The `pro` flag stays on the schema whether or not Pro is installed. Nothing |
| 948 |
* reads it as an entitlement — locking is decided everywhere by the live |
| 949 |
* `better_payment/pro_enabled` filter, evaluated per request, so a campaign |
| 950 |
* saved while Pro was active gets no special treatment once Pro is gone. |
| 951 |
* |
| 952 |
* Pro no longer overrides these entries. It only attaches renderers on |
| 953 |
* `better_payment/campaign/render_element_{type}`; that filter having a |
| 954 |
* listener IS the unlock. |
| 955 |
*/ |
| 956 |
private static function register_pro_elements(): void { |
| 957 |
foreach ( ProElementSchemas::get_all() as $type => $schema ) { |
| 958 |
ElementRegistry::register( $type, array_merge( $schema, [ 'pro' => true ] ) ); |
| 959 |
} |
| 960 |
} |
| 961 |
} |
| 962 |
|