PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.11.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.11.3
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / abstracts / abstract-class-charitable-admin-actions.php

abstract-class-charitable-admin-actions.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.11.3, at includes/abstracts/abstract-class-charitable-admin-actions.php

458 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Registers and performs admin actions.
4 *
5 * @package Charitable/Classes/Charitable_Admin_Actions
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.8.9.1
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Admin_Actions' ) ) :
19
20 /**
21 * Charitable_Admin_Actions
22 *
23 * @since 1.5.0
24 */
25 abstract class Charitable_Admin_Actions implements Charitable_Admin_Actions_Interface {
26
27 /**
28 * Registered actions.
29 *
30 * @since 1.5.0
31 *
32 * @var array
33 */
34 private $actions;
35
36 /**
37 * Registered groups.
38 *
39 * @since 1.5.0
40 *
41 * @var array
42 */
43 private $groups;
44
45 /**
46 * The result of the most recently executed action.
47 *
48 * @since 1.5.0
49 *
50 * @var int
51 */
52 private $result_message;
53
54 /**
55 * Create class object.
56 *
57 * @since 1.5.0
58 */
59 public function __construct() {
60 $this->actions = array();
61 $this->groups = array(
62 '' => array(),
63 );
64 }
65
66 /**
67 * Return the array of actions.
68 *
69 * @since 1.5.0
70 *
71 * @return array
72 */
73 public function get_actions() {
74 return $this->actions;
75 }
76
77 /**
78 * Get available actions, given an object id and set of arguments.
79 *
80 * @since 1.5.0
81 *
82 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
83 * @param array $args Optional. Mixed set of arguments.
84 * @return array
85 */
86 public function get_available_actions( $object_id, $args = array() ) {
87 $actions = array();
88
89 foreach ( $this->actions as $action => $action_args ) {
90 if ( $this->is_action_available( $action_args, $object_id, $args ) ) {
91 $actions[ $action ] = $action_args;
92 }
93 }
94
95 return $actions;
96 }
97
98 /**
99 * Check if there are any available actions, given an object id and set of arguments.
100 *
101 * @since 1.5.0
102 *
103 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
104 * @param array $args Optional. Mixed set of arguments.
105 * @return boolean
106 */
107 public function has_available_actions( $object_id, $args = array() ) {
108 foreach ( $this->actions as $action => $action_args ) {
109 if ( $this->is_action_available( $action_args, $object_id, $args ) ) {
110 return true;
111 }
112 }
113
114 return false;
115 }
116
117 /**
118 * Add action fields, if there are any.
119 *
120 * @since 1.6.0
121 * @version 1.8.9.1
122 *
123 * @param int $object_id The current post ID.
124 * @param string $action_id The key of the action.
125 * @return void
126 */
127 public function add_action_fields( $object_id, $action_id ) {
128 if ( ! array_key_exists( $action_id, $this->actions ) ) {
129 return;
130 }
131
132 $action = $this->actions[ $action_id ];
133
134 if ( ! array_key_exists( 'fields', $action ) || ! $action['fields'] ) {
135 return;
136 }
137
138 ob_start();
139
140 call_user_func( $action['fields'], $object_id, $action );
141
142 $fields = ob_get_clean();
143
144 if ( ! $fields ) {
145 return;
146 }
147
148 $allowed_html = wp_kses_allowed_html( 'post' );
149 $allowed_html['input'] = array(
150 'type' => true,
151 'name' => true,
152 'value' => true,
153 'id' => true,
154 'class' => true,
155 'checked' => true,
156 );
157 echo '<div class="charitable-action-fields" style="display: none;" data-type="' . esc_attr( $action_id ) . '">' . wp_kses( $fields, $allowed_html ) . '</div>';
158 }
159
160 /**
161 * Checks whether an action is available.
162 *
163 * @since 1.5.0
164 *
165 * @param array $action_args Action arguments.
166 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
167 * @param array $args Optional. Mixed set of arguments.
168 * @return boolean
169 */
170 protected function is_action_available( $action_args, $object_id, $args = array() ) {
171 if ( ! array_key_exists( 'active_callback', $action_args ) ) {
172 return true;
173 }
174
175 $args['action_args'] = $action_args;
176
177 return call_user_func( $action_args['active_callback'], $object_id, $args );
178 }
179
180 /**
181 * Return the array of groups.
182 *
183 * @since 1.5.0
184 *
185 * @return array
186 */
187 public function get_groups() {
188 return $this->groups;
189 }
190
191 /**
192 * Returns all groups with at least one action available.
193 *
194 * @since 1.5.0
195 *
196 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
197 * @param array $args Optional. Mixed set of arguments.
198 * @return array
199 */
200 public function get_available_groups( $object_id, $args = array() ) {
201 $groups = array();
202
203 foreach ( $this->groups as $group => $actions ) {
204 foreach ( $actions as $action ) {
205 if ( $this->is_action_available( $this->actions[ $action ], $object_id, $args ) ) {
206 $groups[ $group ] = $actions;
207 break;
208 }
209 }
210 }
211
212 return $groups;
213 }
214
215 /**
216 * Get the result message.
217 *
218 * @since 1.5.0
219 *
220 * @param string $location The message location.
221 * @return string
222 */
223 public function show_result_message( $location ) {
224 return add_query_arg( 'message', $this->result_message, $location );
225 }
226
227 /**
228 * Register a new action.
229 *
230 * @since 1.5.0
231 *
232 * @param string $action The action key.
233 * @param array $args {
234 * Array of arguments for the action.
235 *
236 * @type string $label The label to display in the admin.
237 * @type callable $callback A callback function to run when the action is processed.
238 * @type string $button_text Optional. The text to show in the button when this action is selected.
239 * @type callable $active_callback Optional. Any passed callback will receive a donation ID as its only parameter
240 * and should return a boolean result: TRUE if the action should be shown for
241 * the donation; FALSE if it should not.
242 * @type int $success_message Optional. Message to display when an action is successfully run.
243 * @type int $failure_message Optional. Message to display when an action fails to run.
244 * @type callable $fields Optional. Function returning additional fields or content to show when an action is selected.
245 * }
246 * @param string $group Optional. If set, action will be added to a group of other related actions, which will be
247 * shown as an optgroup.
248 * @return boolean True if the action was registerd. False if not.
249 */
250 public function register( $action, $args, $group = 'default' ) {
251 if ( $this->action_exists( $action ) ) {
252 return false;
253 }
254
255 if ( ! $this->action_is_valid( $args ) ) {
256 return false;
257 }
258
259 $this->groups[ $group ] = array_merge( $this->get_group_actions( $group ), array( $action ) );
260 $this->actions[ $action ] = $this->sanitize_action_args( $args );
261
262 return true;
263 }
264
265 /**
266 * Do a particular action.
267 *
268 * @since 1.5.0
269 * @version 1.8.8.4
270 *
271 * @param string $action The action to do.
272 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
273 * @param array $args Optional. Mixed set of arguments.
274 * @return boolean|WP_Error WP_Error in case of error. Mixed results if the action was performed.
275 */
276 public function do_action( $action, $object_id, $args = array() ) {
277 if ( ! $this->action_exists( $action ) ) {
278 /* translators: %s: action id */
279 return new WP_Error( sprintf( __( 'Action "%s" is not registered.', 'charitable' ), $action ) );
280 }
281
282 $action_args = $this->actions[ $action ];
283
284 // Validate action args structure
285 if ( ! is_array( $action_args ) || ! isset( $action_args['callback'] ) ) {
286 if ( function_exists( 'charitable_is_debug' ) ? charitable_is_debug() : ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
287 error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
288 sprintf(
289 'Charitable: Invalid action args for action "%s". Missing required callback.',
290 $action
291 )
292 );
293 }
294 return false;
295 }
296
297 if ( ! $this->is_action_available( $action_args, $object_id, $args ) ) {
298 return false;
299 }
300
301 $action_hook = sprintf( 'charitable_%s_admin_action_%s', $this->get_type(), $action );
302
303 // Validate callback before registering
304 if ( ! is_callable( $action_args['callback'] ) ) {
305 if ( function_exists( 'charitable_is_debug' ) ? charitable_is_debug() : ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
306 error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
307 sprintf(
308 'Charitable: Invalid callback for action "%s". Callback is not callable.',
309 $action
310 )
311 );
312 }
313 return false;
314 }
315
316 /**
317 * Register the action's callback for the hook.
318 */
319 add_filter( $action_hook, $action_args['callback'], 10, 4 );
320
321 /**
322 * Do something for this action and return a boolean result.
323 *
324 * To find the hook for a particular action, you need to know the type of action (i.e. donation, campaign)
325 * and the action key. The hook is constructed like this:
326 *
327 * charitable_{type}_admin_action_{action key}
328 *
329 * @since 1.5.0
330 *
331 * @param boolean $success Whether the action has been successfully completed.
332 * @param int $object_id The object ID. This could be the ID of the donation, campaign, donor, etc.
333 * @param array $args Optional. Mixed set of arguments.
334 * @param string $action The action we are executing.
335 */
336 try {
337 $success = apply_filters( $action_hook, false, $object_id, $args, $action ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Hook name is dynamically constructed with the plugin prefix 'charitable_{type}_admin_action_'.
338 } catch ( Exception $e ) {
339 // Log the error for debugging
340 if ( function_exists( 'charitable_is_debug' ) ? charitable_is_debug() : ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
341 error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
342 sprintf(
343 'Charitable: Error executing action "%s" for object ID %d: %s',
344 $action,
345 $object_id,
346 $e->getMessage()
347 )
348 );
349 }
350 $success = false;
351 }
352
353 if ( $success && array_key_exists( 'success_message', $action_args ) ) {
354 $this->result_message = $action_args['success_message'];
355 }
356
357 if ( ! $success ) {
358 // Handle both 'failed_message' and 'failure_message' keys for backward compatibility
359 if ( array_key_exists( 'failed_message', $action_args ) ) {
360 $this->result_message = $action_args['failed_message'];
361 } elseif ( array_key_exists( 'failure_message', $action_args ) ) {
362 $this->result_message = $action_args['failure_message'];
363 // Log the deprecated key usage for debugging
364 if ( function_exists( 'charitable_is_debug' ) ? charitable_is_debug() : ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
365 error_log( 'Charitable: Deprecated "failure_message" key used in action args. Please use "failed_message" instead.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
366 }
367 }
368 }
369
370 if ( isset( $this->result_message ) ) {
371 add_action( 'redirect_post_location', array( $this, 'show_result_message' ) );
372 }
373
374 return $success;
375 }
376
377 /**
378 * Returns a link that will execute a particular action.
379 *
380 * @since 1.5.0
381 *
382 * @param string $action The action to be executed.
383 * @param int $object_id The ID of the object we are executing this action for.
384 * @return string
385 */
386 public function get_action_link( $action, $object_id ) {
387 if ( ! $this->action_exists( $action ) ) {
388 return;
389 }
390
391 return esc_url(
392 add_query_arg(
393 array(
394 'charitable_admin_action' => $action,
395 'action_type' => $this->get_type(),
396 'object_id' => $object_id,
397 '_nonce' => wp_create_nonce( 'donation_action' ),
398 )
399 )
400 );
401 }
402
403 /**
404 * Checks whether a particular action has been registered.
405 *
406 * @since 1.5.0
407 *
408 * @param string $action The action to check.
409 * @return boolean
410 */
411 public function action_exists( $action ) {
412 return array_key_exists( $action, $this->actions );
413 }
414
415 /**
416 * Checks whether an action is valid.
417 *
418 * @since 1.5.9
419 *
420 * @param array $args The action args.
421 * @return boolean
422 */
423 protected function action_is_valid( $args ) {
424 return array_key_exists( 'label', $args ) && array_key_exists( 'callback', $args );
425 }
426
427 /**
428 * Return all of a group's registered actions.
429 *
430 * @since 1.5.9
431 *
432 * @param string $group The group.
433 * @return array
434 */
435 protected function get_group_actions( $group ) {
436 return array_key_exists( $group, $this->groups ) ? $this->groups[ $group ] : array();
437 }
438
439 /**
440 * Sanitize an action args, ensuring that all args exist.
441 *
442 * @since 1.5.9
443 *
444 * @param array $args The action args.
445 * @return array
446 */
447 protected function sanitize_action_args( $args ) {
448 $defaults = array(
449 'button_text' => __( 'Submit', 'charitable' ),
450 'fields' => false,
451 );
452
453 return array_merge( $defaults, $args );
454 }
455 }
456
457 endif;
458