PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.4.1
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.4.1
2.4.1 2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 All 68 releases
← All changes | includes/Campaign/Elements/CampaignElements.php +393 -59 2.2.0 → 2.4.1 View file →
@@ -34,70 +34,336 @@
34 34 * ]
35 35 */
36 36 class CampaignElements {
37 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 +
38 258 public static function register_all(): void {
39 259
40 260 ElementRegistry::register( 'campaign_title', [
41 261 'label' => __( 'Campaign Title', 'better-payment' ),
42 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.
43 266 'defaultSettings' => [
44 267 'align' => 'left',
45 268 ],
46 - 'settingsSchema' => [
269 + 'settingsSchema' => array_merge(
47 270 [
48 - 'key' => 'title',
49 - 'label' => __( 'Campaign Title', 'better-payment' ),
50 - 'type' => 'text',
51 - 'placeholder' => __( 'Campaign name', 'better-payment' ),
271 + [
272 + 'key' => 'title',
273 + 'label' => __( 'Campaign Title', 'better-payment' ),
274 + 'type' => 'text',
275 + 'placeholder' => __( 'Campaign name', 'better-payment' ),
276 + ],
52 277 ],
278 + self::typography_schema( [
279 + 'font_size' => [ 'defaultValue' => 32 ],
280 + 'color' => [ 'defaultValue' => '#1a1a2e' ],
281 + ] ),
53 282 [
54 - 'key' => 'align',
55 - 'label' => __( 'Align', 'better-payment' ),
56 - 'type' => 'align',
57 - ],
58 - ],
283 + [
284 + 'key' => 'align',
285 + 'label' => __( 'Align', 'better-payment' ),
286 + 'type' => 'align',
287 + ],
288 + ]
289 + ),
59 290 ] );
60 291
61 292 ElementRegistry::register( 'campaign_description', [
62 293 'label' => __( 'Campaign Description', 'better-payment' ),
63 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.
64 298 'defaultSettings' => [
65 - 'headline' => '',
66 - 'content' => '',
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).",
67 301 'width' => 100,
68 302 'align' => 'left',
69 303 ],
70 - 'settingsSchema' => [
304 + 'settingsSchema' => array_merge(
71 305 [
72 - 'key' => 'headline',
73 - 'label' => __( 'Headline', 'better-payment' ),
74 - 'type' => 'text',
75 - 'placeholder' => __( 'Headline', 'better-payment' ),
306 + [
307 + 'key' => 'headline',
308 + 'label' => __( 'Headline', 'better-payment' ),
309 + 'type' => 'text',
310 + 'placeholder' => __( 'Headline', 'better-payment' ),
311 + ],
76 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 + ),
77 325 [
78 - 'key' => 'content',
79 - 'label' => __( 'Campaign Description', 'better-payment' ),
80 - 'type' => 'rich_text',
81 - 'info' => __( 'Supports bold, italic, underline, links, and lists.', 'better-payment' ),
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 + ],
82 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 + ),
83 347 [
84 - 'key' => 'width',
85 - 'label' => __( 'Width', 'better-payment' ),
86 - 'type' => 'range',
87 - 'min' => 10,
88 - 'max' => 100,
89 - 'step' => 1,
90 - 'unit' => '%',
91 - 'defaultValue' => 100,
92 - 'info' => __( 'Content width as a percentage of its container.', 'better-payment' ),
93 - ],
94 - [
95 - 'key' => 'align',
96 - 'label' => __( 'Align', 'better-payment' ),
97 - 'type' => 'align',
98 - ],
99 - ],
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 + ),
100 366 ] );
101 367
102 368 ElementRegistry::register( 'photo', [
103 369 'label' => __( 'Photo', 'better-payment' ),
@@ -160,9 +426,9 @@
160 426 ElementRegistry::register( 'progress_bar', [
161 427 'label' => __( 'Progress Bar', 'better-payment' ),
162 428 'icon' => 'chart-bar',
163 429 'defaultSettings' => [
164 - 'headline' => '',
430 + 'headline' => 'Campaign Progress',
165 431 'show_donated' => true,
166 432 'show_goal' => true,
167 433 'round_amounts' => false,
168 434 'donate_label' => 'Donated:',
@@ -171,11 +437,12 @@
171 437 'align' => 'left',
172 438 ],
173 439 'settingsSchema' => [
174 440 [
175 - 'key' => 'headline',
176 - 'label' => __( 'Headline', 'better-payment' ),
177 - 'type' => 'text',
441 + 'key' => 'headline',
442 + 'label' => __( 'Headline', 'better-payment' ),
443 + 'type' => 'text',
444 + 'defaultValue' => 'Campaign Progress',
178 445 ],
179 446 [
180 447 'key' => '_campaign_info',
181 448 'label' => __( 'Campaign Information', 'better-payment' ),
@@ -198,13 +465,19 @@
198 465 'label' => __( 'Round Amounts', 'better-payment' ),
199 466 'type' => 'toggle',
200 467 'defaultValue' => false,
201 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.
202 474 [
203 475 'key' => 'donate_label',
204 476 'label' => __( 'Donate Label:', 'better-payment' ),
205 477 'type' => 'text',
206 478 'defaultValue' => 'Donated:',
479 + 'info' => __( 'Shown before the progress percentage — the percentage is added automatically. Do not type a number here.', 'better-payment' ),
207 480 ],
208 481 [
209 482 'key' => 'goal_label',
210 483 'label' => __( 'Goal Label:', 'better-payment' ),
@@ -209,8 +482,9 @@
209 482 'key' => 'goal_label',
210 483 'label' => __( 'Goal Label:', 'better-payment' ),
211 484 'type' => 'text',
212 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' ),
213 487 ],
214 488 [
215 489 'key' => 'width',
216 490 'label' => __( 'Width', 'better-payment' ),
@@ -234,9 +508,9 @@
234 508 ElementRegistry::register( 'campaign_summary', [
235 509 'label' => __( 'Campaign Summary', 'better-payment' ),
236 510 'icon' => 'info',
237 511 'defaultSettings' => [
238 - 'headline' => '',
512 + 'headline' => 'Campaign Summary',
239 513 'show_raised' => true,
240 514 'show_donors' => true,
241 515 'show_percent' => true,
242 516 'show_days' => true,
@@ -244,12 +518,13 @@
244 518 'align' => 'left',
245 519 ],
246 520 'settingsSchema' => [
247 521 [
248 - 'key' => 'headline',
249 - 'label' => __( 'Headline', 'better-payment' ),
250 - 'type' => 'text',
251 - 'placeholder' => '',
522 + 'key' => 'headline',
523 + 'label' => __( 'Headline', 'better-payment' ),
524 + 'type' => 'text',
525 + 'placeholder' => '',
526 + 'defaultValue' => 'Campaign Summary',
252 527 ],
253 528 [
254 529 'key' => 'choose_label',
255 530 'label' => __( 'Choose what information to show:', 'better-payment' ),
@@ -256,9 +531,9 @@
256 531 'type' => 'section_label',
257 532 ],
258 533 [
259 534 'key' => 'summary_note',
260 - 'label' => __( 'Note: Some items might not be visible or enabled depending on campaign goal and date settings. Save and refresh the builder to see updated values.', 'better-payment' ),
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' ),
261 536 'type' => 'note',
262 537 ],
263 538 [
264 539 'key' => 'show_raised',
@@ -301,9 +576,9 @@
301 576 'icon' => 'heart',
302 577 'defaultSettings' => [
303 578 'button_label' => 'Donate Now',
304 579 'button_color' => '',
305 - 'url' => '',
580 + 'url' => '#',
306 581 'open_new_tab' => false,
307 582 'width' => 100,
308 583 'align' => 'center',
309 584 ],
@@ -315,12 +590,19 @@
315 590 'placeholder' => __( 'Donate Now', 'better-payment' ),
316 591 'defaultValue' => 'Donate Now',
317 592 ],
318 593 [
319 - 'key' => 'url',
320 - 'label' => __( 'Payment Form Page URL', 'better-payment' ),
321 - 'type' => 'url',
322 - 'placeholder' => 'https://',
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' => '#',
323 605 'info' => __( 'Link to the page containing your donation form. Overrides the Donation Page set in General Settings.', 'better-payment' ),
324 606 ],
325 607 [
326 608 'key' => 'open_new_tab',
@@ -334,8 +616,9 @@
334 616 'type' => 'range',
335 617 'min' => 10,
336 618 'max' => 100,
337 619 'step' => 1,
620 + 'unit' => '%',
338 621 'defaultValue' => 100,
339 622 ],
340 623 [
341 624 'key' => 'align',
@@ -393,8 +676,9 @@
393 676 'type' => 'range',
394 677 'min' => 10,
395 678 'max' => 100,
396 679 'step' => 1,
680 + 'unit' => '%',
397 681 'defaultValue' => 100,
398 682 'info' => __( 'Width of the organizer block as a percentage.', 'better-payment' ),
399 683 ],
400 684 [
@@ -409,19 +693,29 @@
409 693 ElementRegistry::register( 'donate_amount', [
410 694 'label' => __( 'Donate Amount', 'better-payment' ),
411 695 'icon' => 'money-alt',
412 696 'defaultSettings' => [
413 - 'headline' => '',
697 + 'headline' => 'Donate Amount',
698 + 'width' => 100,
699 + 'align' => 'left',
414 700 ],
415 701 'settingsSchema' => [
416 702 [
417 - 'key' => 'headline',
418 - 'label' => __( 'Headline', 'better-payment' ),
419 - 'type' => 'text',
420 - 'placeholder' => __( 'Headline', 'better-payment' ),
421 - 'info' => __( 'Optional heading shown above the donation amounts.', 'better-payment' ),
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' ),
422 709 ],
423 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 + [
424 718 'key' => 'bpc_suggested_amounts',
425 719 'label' => __( 'Suggested Donation Amounts', 'better-payment' ),
426 720 'type' => 'suggested_amounts',
427 721 'metaKey' => 'bpc_suggested_amounts',
@@ -427,13 +721,30 @@
427 721 'metaKey' => 'bpc_suggested_amounts',
428 722 ],
429 723 [
430 724 'key' => 'bpc_allow_custom_amount',
431 - 'label' => __( 'Allow Custom Donations', 'better-payment' ),
725 + 'label' => __( 'Allow Custom Amount', 'better-payment' ),
432 726 'type' => 'switch',
433 727 'metaKey' => 'bpc_allow_custom_amount',
434 728 'defaultValue' => true,
435 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 + ],
436 747 ],
437 748 ] );
438 749
439 750 ElementRegistry::register( 'social_sharing', [
@@ -618,6 +929,29 @@
618 929 'type' => 'align',
619 930 ],
620 931 ],
621 932 ] );
933 +
934 + self::register_pro_elements();
935 + }
936 +
937 + /**
938 + * Register the three Pro-only elements (Donors Wall, FAQ, Video) as lock stubs.
939 + *
940 + * Pro owns their control schemas and default settings. Lite registers only
941 + * {@see ProElementStubs} — label, icon, the `pro` flag and an upgrade feature
942 + * list — so a free user can drop, select, move and delete the element, see its
943 + * mock preview, and read what Pro adds. Better Payment Pro 1.4.4+ replaces the
944 + * entries with its full definitions on `better_payment/campaign_elements`
945 + * (default priority) and attaches the renderers on
946 + * `better_payment/campaign/render_element_{type}`.
947 + *
948 + * The `pro` flag stays on every definition whether or not Pro is installed.
949 + * Nothing reads it as an entitlement — locking is decided by the live
950 + * `better_payment/pro_enabled` filter, per request.
951 + */
952 + private static function register_pro_elements(): void {
953 + foreach ( ProElementStubs::get_all() as $type => $stub ) {
954 + ElementRegistry::register( $type, $stub );
955 + }
622 956 }
623 957 }