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 +358 -35 2.2.1 → 2.4.1 View file →
@@ -34,34 +34,268 @@
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 299 'headline' => 'Campaign Description',
66 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,
@@ -66,38 +300,70 @@
66 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' ),
@@ -199,13 +465,19 @@
199 465 'label' => __( 'Round Amounts', 'better-payment' ),
200 466 'type' => 'toggle',
201 467 'defaultValue' => false,
202 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.
203 474 [
204 475 'key' => 'donate_label',
205 476 'label' => __( 'Donate Label:', 'better-payment' ),
206 477 'type' => 'text',
207 478 'defaultValue' => 'Donated:',
479 + 'info' => __( 'Shown before the progress percentage — the percentage is added automatically. Do not type a number here.', 'better-payment' ),
208 480 ],
209 481 [
210 482 'key' => 'goal_label',
211 483 'label' => __( 'Goal Label:', 'better-payment' ),
@@ -210,8 +482,9 @@
210 482 'key' => 'goal_label',
211 483 'label' => __( 'Goal Label:', 'better-payment' ),
212 484 'type' => 'text',
213 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' ),
214 487 ],
215 488 [
216 489 'key' => 'width',
217 490 'label' => __( 'Width', 'better-payment' ),
@@ -317,8 +590,14 @@
317 590 'placeholder' => __( 'Donate Now', 'better-payment' ),
318 591 'defaultValue' => 'Donate Now',
319 592 ],
320 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 + [
321 600 'key' => 'url',
322 601 'label' => __( 'Payment Form Page URL', 'better-payment' ),
323 602 'type' => 'url',
324 603 'placeholder' => 'https://',
@@ -337,8 +616,9 @@
337 616 'type' => 'range',
338 617 'min' => 10,
339 618 'max' => 100,
340 619 'step' => 1,
620 + 'unit' => '%',
341 621 'defaultValue' => 100,
342 622 ],
343 623 [
344 624 'key' => 'align',
@@ -396,8 +676,9 @@
396 676 'type' => 'range',
397 677 'min' => 10,
398 678 'max' => 100,
399 679 'step' => 1,
680 + 'unit' => '%',
400 681 'defaultValue' => 100,
401 682 'info' => __( 'Width of the organizer block as a percentage.', 'better-payment' ),
402 683 ],
403 684 [
@@ -413,8 +694,10 @@
413 694 'label' => __( 'Donate Amount', 'better-payment' ),
414 695 'icon' => 'money-alt',
415 696 'defaultSettings' => [
416 697 'headline' => 'Donate Amount',
698 + 'width' => 100,
699 + 'align' => 'left',
417 700 ],
418 701 'settingsSchema' => [
419 702 [
420 703 'key' => 'headline',
@@ -443,8 +726,25 @@
443 726 'type' => 'switch',
444 727 'metaKey' => 'bpc_allow_custom_amount',
445 728 'defaultValue' => true,
446 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 + ],
447 747 ],
448 748 ] );
449 749
450 750 ElementRegistry::register( 'social_sharing', [
@@ -629,6 +929,29 @@
629 929 'type' => 'align',
630 930 ],
631 931 ],
632 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 + }
633 956 }
634 957 }