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

class-charitable-campaign-meta-boxes.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/admin/campaigns/class-charitable-campaign-meta-boxes.php

540 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class that defines how campaigns are managed on the admin side.
4 *
5 * @package Charitable/Admin/Charitable_Campaign_Meta_Boxes
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.5.0
10 * @version 1.6.45
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Campaign_Meta_Boxes' ) ) :
18
19 /**
20 * Charitable_Campaign_Meta_Boxes class.
21 *
22 * @final
23 * @since 1.0.0
24 */
25 final class Charitable_Campaign_Meta_Boxes {
26
27 /**
28 * The single instance of this class.
29 *
30 * @var Charitable_Campaign_Meta_Boxes|null
31 */
32 private static $instance = null;
33
34 /**
35 * Meta Box Helper class instance.
36 *
37 * @var Charitable_Meta_Box_Helper
38 */
39 private $meta_box_helper;
40
41 /**
42 * Create object instance.
43 *
44 * @since 1.0.0
45 */
46 private function __construct() {
47 $this->meta_box_helper = new Charitable_Meta_Box_Helper( 'charitable-campaign' );
48
49 add_filter( 'default_title', array( $this, 'default_campaign_name' ) );
50 }
51
52
53 /**
54 * Set default campaign name from URL parameters.
55 *
56 * @since 1.0.0
57 * @version 1.8.9.1
58 *
59 * @param string $title The default title.
60 * @return string
61 */
62 public function default_campaign_name( $title ) {
63 // phpcs:disable WordPress.Security.NonceVerification.Recommended
64 if ( ! isset( $_GET['post_type'] ) || ! isset( $_GET['campaign_name'] ) ) {
65 return $title;
66 }
67
68 $post_type = sanitize_key( wp_unslash( $_GET['post_type'] ) );
69 $campaign_name = sanitize_text_field( wp_unslash( $_GET['campaign_name'] ) );
70
71 if ( 'campaign' !== $post_type || '' === trim( $campaign_name ) ) {
72 return $title;
73 }
74 // phpcs:enable WordPress.Security.NonceVerification.Recommended
75
76 return urldecode( esc_html( $campaign_name ) );
77 }
78
79 /**
80 * Returns and/or create the single instance of this class.
81 *
82 * @since 1.2.0
83 *
84 * @return Charitable_Campaign_Meta_Boxes
85 */
86 public static function get_instance() {
87 if ( is_null( self::$instance ) ) {
88 self::$instance = new self();
89 }
90
91 return self::$instance;
92 }
93
94 /**
95 * Return the instance of the meta box helper.
96 *
97 * @since 1.6.0
98 *
99 * @return Charitable_Meta_Box_Helper
100 */
101 public function get_meta_box_helper() {
102 return $this->meta_box_helper;
103 }
104
105 /**
106 * Add meta boxes.
107 *
108 * @see add_meta_boxes hook
109 *
110 * @since 1.0.0
111 *
112 * @return void
113 */
114 public function add_meta_boxes() {
115 $meta_boxes = array_merge(
116 $this->get_campaign_top_meta_boxes(),
117 array(
118 array(
119 'id' => 'campaign-settings',
120 'title' => __( 'Campaign Settings', 'charitable' ),
121 'context' => 'normal',
122 'priority' => 'high',
123 'view' => 'metaboxes/campaign-settings',
124 ),
125 )
126 );
127
128 /**
129 * Filter campaign meta boxes.
130 *
131 * @since 1.0.0
132 *
133 * @param array $meta_boxes The meta boxes.
134 */
135 $meta_boxes = apply_filters( 'charitable_campaign_meta_boxes', $meta_boxes );
136
137 foreach ( $meta_boxes as $meta_box ) {
138 add_meta_box(
139 $meta_box['id'],
140 $meta_box['title'],
141 array( $this->meta_box_helper, 'metabox_display' ),
142 Charitable::CAMPAIGN_POST_TYPE,
143 $meta_box['context'],
144 $meta_box['priority'],
145 $meta_box
146 );
147 }
148 }
149
150 /**
151 * Display fields at the very top of the page.
152 *
153 * @since 1.0.0
154 *
155 * @param WP_Post $post Current post.
156 * @return void
157 */
158 public function campaign_form_top( $post ) {
159 if ( Charitable::CAMPAIGN_POST_TYPE === $post->post_type ) {
160 do_meta_boxes( Charitable::CAMPAIGN_POST_TYPE, 'campaign-top', $post );
161 }
162 }
163
164 /**
165 * Return campaign settings panels.
166 *
167 * @since 1.6.0
168 *
169 * @return array
170 */
171 public function get_campaign_settings_panels() {
172 $panels = charitable()->campaign_fields()->get_sections( 'admin' );
173
174 foreach ( $panels as $section => $label ) {
175 $panels[ $section ] = array(
176 'title' => $label,
177 'fields' => $this->get_section_fields( $section ),
178 );
179
180 if ( 'campaign-donation-options' === $section ) {
181 /**
182 * Filter the fields in the Donation Options panel.
183 *
184 * @since 1.0.0
185 *
186 * @param array $fields The array of fields.
187 */
188 $panels[ $section ]['fields'] = apply_filters( 'charitable_campaign_donation_options_fields', $panels[ $section ]['fields'] );
189 }
190 }
191
192 $panels = $this->add_legacy_meta_boxes( $panels );
193
194 /**
195 * Filter out empty panels.
196 */
197 $panels = array_filter( $panels, array( $this, 'panel_has_fields' ) );
198
199 /**
200 * Filter the array of settings panels.
201 *
202 * @since 1.6.0
203 *
204 * @param $panels array Set of panels.
205 */
206 return apply_filters( 'charitable_campaign_settings_panels', $panels );
207 }
208
209 /**
210 * Save meta for the campaign.
211 *
212 * @since 1.0.0
213 * @version 1.8.9.1
214 *
215 * @param int $campaign_id The campaign ID.
216 * @param WP_Post $post Current Post object.
217 * @return void
218 */
219 public function save_campaign( $campaign_id, WP_Post $post ) {
220 if ( ! $this->meta_box_helper->user_can_save( $campaign_id ) ) {
221 return;
222 }
223
224 $meta_keys = $this->get_campaign_meta_keys();
225 // phpcs:disable WordPress.Security.NonceVerification.Missing
226 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array data, sanitized in loop
227 $submitted = $_POST;
228 // phpcs:enable WordPress.Security.NonceVerification.Missing
229 $data = array(
230 'ID' => $campaign_id,
231 );
232
233 foreach ( $meta_keys as $key ) {
234 $value = isset( $submitted[ $key ] ) ? $submitted[ $key ] : false;
235
236 /**
237 * This filter is deprecated. Use charitable_sanitize_campaign_meta{$key} instead.
238 *
239 * @deprecated 1.7.0
240 *
241 * @since 1.4.12 Deprecated.
242 */
243 $value = apply_filters( 'charitable_sanitize_campaign_meta', $value, $key, $submitted, $campaign_id );
244
245 if ( $key === '_campaign_suggested_donations_default' && isset( $value[0] ) && isset( $submitted['_campaign_suggested_donations'][ $value[0] ]['amount'] ) ) {
246 $value = ( false !== $submitted['_campaign_suggested_donations'][ $value[0] ]['amount'] ) ? array( $submitted['_campaign_suggested_donations'][ $value[0] ]['amount'] ) : false;
247 }
248
249 /**
250 * Filter this meta value. The filter hook is
251 * charitable_sanitize_campaign_meta{$key}. For example,
252 * for _campaign_end_date the filter hook will be:
253 * charitable_sanitize_campaign_meta_campaign_end_date
254 *
255 * @since 1.4.12
256 *
257 * @param mixed $value The submitted value, or false.
258 * @param array $submitted All of the submitted values.
259 * @param int $campaign_id The campaign ID.
260 */
261 $data[ $key ] = apply_filters( 'charitable_sanitize_campaign_meta' . $key, $value, $submitted, $campaign_id );
262 }
263
264 $processor = new Charitable_Campaign_Processor( $data, $meta_keys );
265 $processor->save_meta();
266
267 /**
268 * Do something with the posted data.
269 *
270 * @since 1.0.0
271 *
272 * @param WP_Post $post An instance of `WP_Post`.
273 */
274 do_action( 'charitable_campaign_save', $post );
275 }
276
277 /**
278 * Set default post content when the extended description is left empty.
279 *
280 * @since 1.4.0
281 *
282 * @param array $data Submitted data.
283 * @return array
284 */
285 public function set_default_post_content( $data ) {
286 if ( Charitable::CAMPAIGN_POST_TYPE !== $data['post_type'] ) {
287 return $data;
288 }
289
290 if ( 0 === strlen( $data['post_content'] ) ) {
291 $data['post_content'] = '<!-- Code is poetry -->';
292 }
293
294 return $data;
295 }
296
297 /**
298 * Sets the placeholder text of the campaign title field.
299 *
300 * @since 1.0.0
301 * @version 1.8.1.15
302 *
303 * @param string $placeholder Placeholder text.
304 * @param WP_Post $post Current Post object.
305 * @return string
306 */
307 public function campaign_enter_title( $placeholder, WP_Post $post ) {
308 if ( 'campaign' == $post->post_type ) {
309 $placeholder = __( 'Enter campaign title', 'charitable' );
310 }
311
312 return $placeholder;
313 }
314
315 /**
316 * Change messages when a post type is updated.
317 *
318 * @global WP_Post $post
319 * @global int $post_ID
320 * @param array $messages Messages to display.
321 * @return array
322 */
323 public function post_messages( $messages ) {
324 global $post, $post_ID;
325
326 $messages[ Charitable::CAMPAIGN_POST_TYPE ] = array(
327 // Unused. Messages start at index 1.
328 0 => '',
329 1 => sprintf(
330 // Translators: %s is the permalink to the campaign.
331 __( 'Campaign updated. <a href="%s">View Campaign</a>', 'charitable' ),
332 esc_url( get_permalink( $post_ID ) )
333 ),
334 2 => __( 'Custom field updated.', 'charitable' ),
335 3 => __( 'Custom field deleted.', 'charitable' ),
336 4 => __( 'Campaign updated.', 'charitable' ),
337 5 => // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only, no action
338 isset( $_GET['revision'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
339 ? sprintf(
340 // Translators: %s is the revision date.
341 __( 'Campaign restored to revision from %s', 'charitable' ),
342 wp_post_revision_title( (int) sanitize_text_field( wp_unslash( $_GET['revision'] ) ), false ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
343 )
344 : false,
345 6 => sprintf(
346 // Translators: %s is the permalink to the campaign.
347 __( 'Campaign published. <a href="%s">View Campaign</a>', 'charitable' ),
348 esc_url( get_permalink( $post_ID ) )
349 ),
350 7 => __( 'Campaign saved.', 'charitable' ),
351 8 => sprintf(
352 // Translators: %s is the preview link to the campaign.
353 __( 'Campaign submitted. <a target="_blank" href="%s">Preview Campaign</a>', 'charitable' ),
354 esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) )
355 ),
356 9 => sprintf(
357 // Translators: %1$s is the date the campaign is scheduled for, %2$s is the preview link to the campaign.
358 __( 'Campaign scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview Campaign</a>', 'charitable' ),
359 date_i18n( 'M j, Y @ G:i', strtotime( $post->post_date ) ),
360 esc_url( get_permalink( $post_ID ) )
361 ),
362 10 => sprintf(
363 // Translators: %s is the preview link to the campaign.
364 __( 'Campaign draft updated. <a target="_blank" href="%s">Preview Campaign</a>', 'charitable' ),
365 esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) )
366 ),
367 );
368
369 return $messages;
370 }
371
372 /**
373 * Return the set of meta boxes to be shown in the "campaign-top" section of the campaign editor.
374 *
375 * @since 1.6.0
376 *
377 * @return array
378 */
379 private function get_campaign_top_meta_boxes() {
380 $boxes = $this->get_section_fields( 'campaign-top' );
381
382 foreach ( $boxes as $key => $box ) {
383 $box['id'] = str_replace( '_', '-', substr( $key, 1 ) );
384 $box['title'] = $box['label'];
385 $box['context'] = 'campaign-top';
386 $box['priority'] = 'high';
387 $boxes[ $key ] = $box;
388 }
389
390 return $boxes;
391 }
392
393 /**
394 * Add panels for any meta boxes previously added to the campaign-advanced section
395 * using the `charitable_campaign_meta_boxes` filter.
396 *
397 * @since 1.6.0
398 *
399 * @param array $panels Core registered panels.
400 * @return array
401 */
402 private function add_legacy_meta_boxes( $panels ) {
403 foreach ( apply_filters( 'charitable_campaign_meta_boxes', array() ) as $panel ) {
404 if ( 'campaign-advanced' != $panel['context'] ) {
405 continue;
406 }
407
408 $id = $panel['id'];
409
410 unset( $panel['id'] );
411
412 $panels[ $id ] = $panel;
413 }
414
415 return $panels;
416 }
417
418 /**
419 * Return the campaign meta keys to save.
420 *
421 * @since 1.6.0
422 *
423 * @return array
424 */
425 public function get_campaign_meta_keys() {
426 $registry = charitable()->campaign_fields();
427 $keys = $registry->get_sanitized_keys( $registry->get_admin_form_fields() );
428
429 $keys[] = '_campaign_suggested_donations_default';
430 $keys[] = '_campaign_hide_amount_donated';
431 $keys[] = '_campaign_hide_number_of_donors';
432 $keys[] = '_campaign_hide_percent_raised';
433 $keys[] = '_campaign_hide_time_remaining';
434 $keys[] = '_campaign_donate_button_text';
435
436 /**
437 * Filter the set of meta keys to be saved.
438 *
439 * @since 1.0.0
440 *
441 * @param string[] $keys An array of strings representing meta keys.
442 */
443 return apply_filters( 'charitable_campaign_meta_keys', $keys );
444 }
445
446 /**
447 * Set a field's initial value.
448 *
449 * @since 1.6.0
450 *
451 * @param array $field Field definition.
452 * @param string $key The key of the field.
453 * @return array
454 */
455 private function sanitize_campaign_field( $field, $key ) {
456 $field_name = preg_replace( '/^_campaign_/', '', $key );
457
458 // phpcs:disable WordPress.Security.NonceVerification.Missing
459 if ( array_key_exists( $key, $_POST ) ) {
460 $field['value'] = sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
461 return $field;
462 }
463 // phpcs:enable WordPress.Security.NonceVerification.Missing
464
465 /* Checkboxes don't need a value set. */
466 if ( 'checkbox' != $field['type'] ) {
467 $field['value'] = array_key_exists( 'default', $field ) ? $field['default'] : '';
468 }
469
470 // phpcs:disable WordPress.Security.NonceVerification.Recommended
471 if ( ! array_key_exists( 'post', $_GET ) ) {
472 return $field;
473 }
474
475 $campaign = charitable_get_campaign( intval( wp_unslash( $_GET['post'] ) ) );
476 // phpcs:enable WordPress.Security.NonceVerification.Recommended
477
478 if ( in_array( $campaign->post_status, array( 'auto-draft' ) ) ) {
479 return $field;
480 }
481
482 if ( array_key_exists( 'value_callback', $field ) && is_callable( $field['value_callback'] ) ) {
483 $field['value'] = call_user_func( $field['value_callback'], $campaign, $key );
484 return $field;
485 }
486
487 $value = $campaign->get( $field_name );
488
489 if ( ! $value ) {
490 return $field;
491 }
492
493 if ( 'checkbox' == $field['type'] ) {
494 $field['checked'] = $value;
495 } else {
496 $field['value'] = $value;
497 }
498
499 return $field;
500 }
501
502 /**
503 * Return the fields in a particular section.
504 *
505 * @since 1.6.0
506 *
507 * @param string $section The section.
508 * @return array
509 */
510 private function get_section_fields( $section ) {
511 $registry = charitable()->campaign_fields();
512 $fields = $registry->get_admin_form_fields( $section );
513
514 if ( empty( $fields ) ) {
515 return array();
516 }
517
518 $keys = $registry->get_sanitized_keys( $fields, false );
519
520 return array_combine(
521 $keys,
522 array_map( array( $this, 'sanitize_campaign_field' ), wp_list_pluck( $fields, 'admin_form' ), $keys )
523 );
524 }
525
526 /**
527 * Returns whether a particular panel has fields.
528 *
529 * @since 1.6.0
530 *
531 * @param array $panel The panel definition.
532 * @return boolean
533 */
534 private function panel_has_fields( $panel ) {
535 return array_key_exists( 'view', $panel ) || count( $panel['fields'] );
536 }
537 }
538
539 endif;
540