PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.11.1
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.11.1
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 / admin / campaign-builder / ajax-actions.php

ajax-actions.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.11.1, at includes/admin/campaign-builder/ajax-actions.php

1,409 lines 53.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Exit if accessed directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Ajax actions related to the campaign builder used in by admin.
10 *
11 * @package Charitable
12 * @since 1.8.0
13 * @version 1.8.9.1
14 */
15
16 /**
17 * Get and return template data via ajax.
18 *
19 * @since 1.8.0
20 */
21 function charitable_get_campaign_builder_template_data() {
22
23 if ( empty( $_POST['id'] ) ) { // phpcs:ignore
24 wp_send_json_error( esc_html__( 'Something went wrong while performing this action.', 'charitable' ) );
25 }
26
27 $builder_template = new Charitable_Campaign_Builder_Templates();
28 $template_id = ! empty( $_POST['id'] ) ? esc_attr( $_POST['id'] ) : false; // phpcs:ignore
29
30 if ( ! $template_id ) {
31 wp_send_json_error( esc_html__( 'Something went wrong while performing this action.', 'charitable' ) );
32 }
33
34 // get the title if passed so we can create a temp $campaign_data array to custom populare a template down the road.
35 $campaign_title = ! empty( $_POST['title'] ) ? esc_html( $_POST['title'] ) : false; // phpcs:ignore
36 $campaign_data = array();
37 if ( $campaign_title ) {
38 $campaign_data['title'] = $campaign_title;
39 }
40
41 $no_cache = apply_filters( 'charitable_campaign_builder_ajax_template_no_cache', true );
42
43 $template_data = $builder_template->get_template_data( $template_id, $campaign_data, $no_cache );
44
45 if ( $template_data ) {
46
47 wp_send_json_success( $template_data );
48
49 } else {
50
51 wp_send_json_error( esc_html__( 'Something went wrong while attempting to get template information.', 'charitable' ) );
52 }
53 }
54 add_action( 'wp_ajax_charitable_get_campaign_builder_template_data', 'charitable_get_campaign_builder_template_data' );
55
56 /**
57 * Get and return template preview via ajax.
58 *
59 * @since 1.8.0
60 */
61 function charitable_get_campaign_builder_template_preview() {
62
63 if ( empty( $_POST['id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
64 wp_send_json_error( esc_html__( 'Something went wrong while performing this action.', 'charitable' ) );
65 }
66
67 $template_id = intval( $_POST['id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
68
69 if ( 0 === $template_id ) {
70 wp_send_json_error( esc_html__( 'Something went wrong with getting the template while performing this action.', 'charitable' ) );
71 }
72
73 $builder_template = new Charitable_Campaign_Builder_Templates();
74 $template_preview = $builder_template->get_template_preview( $template_id );
75
76 if ( $template_preview ) {
77
78 $response = array( 'html' => $template_preview );
79
80 wp_send_json_success( $response );
81
82 } else {
83
84 wp_send_json_error( esc_html__( 'Something went wrong while attempting to get the template information.', 'charitable' ) );
85
86 }
87 }
88 add_action( 'wp_ajax_charitable_get_campaign_builder_template_preview', 'charitable_get_campaign_builder_template_preview' );
89
90
91 /**
92 * Save a campaign via ajax.
93 *
94 * @since 1.8.0
95 */
96 function charitable_save_campaign() {
97
98 // Run a security check.
99 if ( ! check_ajax_referer( 'charitable-builder', 'nonce', false ) ) {
100 wp_send_json_error( esc_html__( 'Your session expired. Please reload the builder.', 'charitable' ) );
101 }
102
103 // Check for permissions.
104 if ( ! charitable_current_user_can( 'edit_campaigns' ) ) {
105 wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'charitable' ) );
106 }
107
108 // Check for form data.
109 if ( empty( $_POST['data'] ) ) {
110 wp_send_json_error( esc_html__( 'Something went wrong while performing this action.', 'charitable' ) );
111 }
112
113 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
114 // $campaign_post = json_decode( wp_unslash( $_POST['data'] ), ARRAY_A );
115 $campaign_post = array_column( json_decode( wp_unslash( $_POST['data'] ), ARRAY_A ), null, 'name' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
116 $is_preview = isset( $_POST['preview'] ) && 'true' === $_POST['preview'] ? true : false;
117 $data = [
118 'fields' => [],
119 ];
120
121 $campaign_settings_v2 = array();
122
123 // Note: We have to first get the layout from the template because the incoming data is only the fields the user has placed into the form.
124 // If we don't do this, if the user doesn't put anything into a particular column or row, that won't be included in the POST and we'll lose that data.
125
126 $campaign_settings_v2 = charitable_template_layout_to_campaign_layout( $campaign_settings_v2, $campaign_post );
127
128 // this tracks where tabs might be in the layout.
129 $campaign_tabs_location = array();
130
131 if ( $campaign_post ) {
132
133 foreach ( $campaign_post as $post_field_name => $post_field_data ) {
134
135 // This process has to check for various formats of fields that appear in the form on the screen hence the multiple if statements.
136
137 if ( strpos( $post_field_name, '__' ) !== false && strpos( $post_field_name, 'xxx' ) === false ) { // this covers fields that are like EXAMPLE__EXAMPLE__EXAMPLE.
138
139 $array_level = explode( '__', $post_field_name );
140 if ( count( $array_level ) === 4 ) {
141 $campaign_settings_v2[ $array_level[0] ][ $array_level[1] ][ $array_level[2] ][ $array_level[3] ] = $post_field_data['value'];
142 } elseif ( count( $array_level ) === 3 ) {
143 $campaign_settings_v2[ $array_level[0] ][ $array_level[1] ][ $array_level[2] ] = $post_field_data['value'];
144 } elseif ( count( $array_level ) === 2 ) {
145 $campaign_settings_v2[ $array_level[0] ][ $array_level[1] ] = $post_field_data['value'];
146 } elseif ( count( $array_level ) === 2 ) {
147 $campaign_settings_v2[ $array_level[0] ] = $post_field_data['value'];
148 }
149
150 } elseif ( strpos( $post_field_name, '[donation_amounts]' ) !== false && strpos( $post_field_name, '[campaign_suggested_donations_default]' ) !== false ) {
151
152 $campaign_settings_v2['settings']['donation-options']['suggested_donations_default'] = $post_field_data['value'];
153
154 } elseif ( strpos( $post_field_name, 'donation_amounts' ) !== false && strpos( $post_field_name, '_fields' ) !== false ) {
155
156 continue;
157
158 } elseif ( strpos( $post_field_name, '[donation_amounts]' ) !== false && strpos( $post_field_name, '[campaign_suggested_donations_default]' ) === false ) {
159
160 $post_field_name = str_replace( 'settings]', '', $post_field_name );
161 $post_field_name = str_replace( '[donation-options]', '', $post_field_name );
162 $post_field_name = str_replace( '[donation_amounts]', '', $post_field_name );
163
164 $output = charitable_convert_campaign_string_to_array( $post_field_name, $post_field_data['value'] );
165 $_tmp_array = $output[ array_key_first( $output ) ];
166
167 $first_key = esc_attr( array_key_first( $output ) );
168 $second_key = esc_attr( array_key_first( $_tmp_array ) );
169
170 // this would avoid field 0 which should be a hidden field for the purpose of adding dynamic rows via Javascript.
171 if ( 0 === intval( $first_key ) ) {
172 continue;
173 }
174
175 $value = ( 'amount' === esc_attr( $second_key ) ) ? charitable_sanitize_amount( $post_field_data['value'] ) : wp_strip_all_tags( $post_field_data['value'] );
176
177 // if ( intval( $first_key ) !== 0 ) { // this would avoid field 0 which should be a hidden field for the purpose of adding dynamic rows via Javascript.
178 $campaign_settings_v2['settings']['donation-options']['donation_amounts'][ $first_key ][ $second_key ] = $value;
179 // }
180
181 } elseif ( strpos( $post_field_name, '[recurring_donation_amounts]' ) !== false && strpos( $post_field_name, '[campaign_suggested_recurring_donations_default]' ) !== false ) {
182
183 $campaign_settings_v2['settings']['donation-options']['suggested_recurring_donations_default'] = $post_field_data['value'];
184
185 } elseif ( strpos( $post_field_name, '[recurring_donation_amounts]' ) !== false && strpos( $post_field_name, '[campaign_suggested_recurring_donations_default]' ) === false ) {
186
187 $post_field_name = str_replace( 'settings]', '', $post_field_name );
188 $post_field_name = str_replace( '[donation-options]', '', $post_field_name );
189 $post_field_name = str_replace( '[recurring_donation_amounts]', '', $post_field_name );
190
191 $output = charitable_convert_campaign_string_to_array( $post_field_name, $post_field_data['value'] );
192 $_tmp_array = $output[ array_key_first( $output ) ];
193
194 $first_key = esc_attr( array_key_first( $output ) );
195 $second_key = esc_attr( array_key_first( $_tmp_array ) );
196
197 // this would avoid field 0 which should be a hidden field for the purpose of adding dynamic rows via Javascript.
198 if ( 0 === intval( $first_key ) ) {
199 continue;
200 }
201
202 $value = ( 'amount' === esc_attr( $second_key ) ) ? charitable_sanitize_amount( $post_field_data['value'] ) : wp_strip_all_tags( $post_field_data['value'] );
203
204 // if ( intval( $first_key ) !== 0 ) { // this would avoid field 0 which should be a hidden field for the purpose of adding dynamic rows via Javascript.
205 $campaign_settings_v2['settings']['donation-options']['recurring_donation_amounts'][ $first_key ][ $second_key ] = $value;
206 // }
207
208 } elseif ( strpos( $post_field_name, '[recurring_donation_periods_to_display]' ) !== false ) {
209
210 $post_field_name = str_replace( 'settings[', '', $post_field_name );
211 $post_field_name = str_replace( 'donation-options]', '', $post_field_name );
212 $post_field_name = str_replace( '[recurring_donation_periods_to_display]', '', $post_field_name );
213
214 $output = charitable_convert_campaign_string_to_array_new( $post_field_name, $post_field_data['value'] );
215
216 $first_key = esc_attr( array_key_first( $output ) );
217 $value = wp_strip_all_tags( $post_field_data['value'] );
218
219 $campaign_settings_v2['settings']['donation-options']['recurring_donation_periods_to_display'][ $first_key ] = $value;
220
221 } elseif ( strpos( $post_field_name, '[row]' ) !== false && strpos( $post_field_name, '[column]' ) !== false && strpos( $post_field_name, '[row-type-tabs]' ) === false ) {
222
223 $post_field_name = str_replace( 'layout[row]', '', $post_field_name );
224 $post_field_name = str_replace( '[column]', '', $post_field_name );
225 $post_field_name = str_replace( '[section]', '', $post_field_name );
226 $post_field_name = str_replace( '[fields]', '', $post_field_name );
227
228 $output = charitable_convert_campaign_string_to_array_new( $post_field_name, $post_field_data['value'], 'photo' );
229
230 $row_type = array_key_first( $output );
231 $row_id = array_key_first( $output[ $row_type ] );
232 $row_css = array_key_first( $output[ $row_type ][ $row_id ] );
233 $column_id = array_key_first( $output[ $row_type ][ $row_id ][ $row_css ] );
234 $section_type = array_key_first( $output[ $row_type ][ $row_id ][ $row_css ][ $column_id ] );
235 $section_id = array_key_first( $output[ $row_type ][ $row_id ][ $row_css ][ $column_id ][ $section_type ] );
236
237 $row_type = str_replace( 'row-type-', '', $row_type );
238 $section_type = str_replace( 'section-type-', '', $section_type );
239
240 if ( 'row' === $row_type || 'header' === $row_type ) {
241
242 $campaign_settings_v2['layout']['rows'][ $row_id ]['type'] = ( false !== $row_type ) ? esc_attr( $row_type ) : 'row';
243
244 if ( 'tabs' === $section_type ) {
245
246 $tab_id = array_key_first( $output[ 'row-type-' . $row_type ][ $row_id ][ $row_css ][ $column_id ][ 'section-type-' . $section_type ][ $section_id ]['tabs'] );
247 $field_id_key = array_key_first( $output[ 'row-type-' . $row_type ][ $row_id ][ $row_css ][ $column_id ][ 'section-type-' . $section_type ][ $section_id ]['tabs'][ $tab_id ] );
248
249 $campaign_settings_v2['layout']['rows'][ $row_id ]['type'] = $row_type;
250 $campaign_settings_v2['layout']['rows'][ $row_id ]['css_class'] = esc_attr( $row_css );
251 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['type'] = 'tabs';
252 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['tabs'][ $tab_id ]['title'] = $campaign_post[ 'tabs__' . $tab_id . '__title' ]['value'];
253 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['tabs'][ $tab_id ]['type'] = 'fields';
254 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['tabs'][ $tab_id ]['slug'] = '';
255 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['tabs'][ $tab_id ]['fields'][] = $field_id_key;
256
257 $campaign_tabs_location = array(
258 'row_id' => $row_id,
259 'column_id' => $column_id,
260 'section_id' => $section_id,
261 );
262
263 } elseif ( 'fields' === $section_type ) {
264
265 $field_id_key = array_key_first( $output[ 'row-type-' . $row_type ][ $row_id ][ $row_css ][ $column_id ][ 'section-type-' . $section_type ][ $section_id ] );
266
267 $campaign_settings_v2['layout']['rows'][ $row_id ]['type'] = $row_type;
268 $campaign_settings_v2['layout']['rows'][ $row_id ]['css_class'] = esc_attr( $row_css );
269 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['type'] = 'fields';
270 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['fields'][] = $field_id_key;
271
272 }
273
274 $campaign_settings_v2['layout']['rows'][ $row_id ]['fields'][ $field_id_key ] = $post_field_data['value'];
275
276 }
277
278 } elseif ( strpos( $post_field_name, '[row]' ) !== false && strpos( $post_field_name, '[blocks]' ) !== false && strpos( $post_field_name, '[row-type-tabs]' ) !== false ) {
279
280 $post_field_name = str_replace( 'layout[row][row-type-tabs][999]', '', $post_field_name );
281 $post_field_name = str_replace( '[blocks]', '', $post_field_name );
282
283 $output = charitable_convert_campaign_string_to_array( $post_field_name, $post_field_data['value'] );
284
285 $_tmp_array = $output[ array_key_first( $output ) ];
286
287 $tab_key = array_key_first( $output );
288 $field_key = array_key_first( $_tmp_array );
289
290 $campaign_settings_v2['layout']['rows'][999]['type'] = 'tabs';
291 $campaign_settings_v2['layout']['rows'][999]['fields'][ $field_key ] = $post_field_data['value'];
292 $campaign_settings_v2['layout']['rows'][999]['tabs'][ $tab_key ][] = $field_key;
293
294 } elseif ( strpos( $post_field_name, '_fields' ) !== false ) {
295
296 $post_field_name = str_replace( '_fields', '', $post_field_name );
297
298 $output = charitable_convert_campaign_string_to_array( $post_field_name, $post_field_data['value'] );
299
300 $_tmp_array = $output[ array_key_first( $output ) ];
301 $_tmp_array_2 = is_array( $_tmp_array ) ? $_tmp_array[ array_key_first( $_tmp_array ) ] : false;
302
303 $first_key = array_key_first( $output );
304 $second_key = array_key_first( $_tmp_array );
305 $third_key = $_tmp_array_2 !== false && is_array( $_tmp_array_2 ) && '' !== array_key_first( $_tmp_array_2 ) ? array_key_first( $_tmp_array_2 ) : false;
306
307 // Santitize here.
308 // todo: this needs to be cleaned up.
309 if ( in_array( $second_key, array( 'css_class' ), true ) ) {
310 // Remove all characters from this string that would not make it a valid css value or attribute.
311 $post_field_data['value'] = str_replace( array( '"', "'", '`' ), '', $post_field_data['value'] );
312 $post_field_data['value'] = preg_replace( '/[^a-zA-Z0-9_,\/$:\-\.!\s;]/', '', $post_field_data['value'] );
313 $post_field_data['value'] = esc_attr( $post_field_data['value'] );
314 }
315
316 if ( null !== $third_key && false !== $third_key && '' !== trim( $third_key ) ) {
317 $campaign_settings_v2['fields'][ $first_key ][ $second_key ][ $third_key ] = $post_field_data['value'];
318 } else {
319 $campaign_settings_v2['fields'][ $first_key ][ $second_key ] = $post_field_data['value'];
320 }
321
322 } elseif ( strpos( $post_field_name, '][' ) !== false ) { // this covers EXAMPLE[EXAMPLE][EXAMPLE].
323
324 $output = array();
325 $pointer = &$output;
326 $input = $post_field_name;
327 $counter = ( substr_count( $input, '[' ) ) - 1;
328 while ( ( $index = strpos( $input, '[' ) ) !== false ) {
329 if ( $index != 0 ) {
330 $key = substr( $input, 0, $index );
331 $pointer[ $key ] = array();
332 $pointer = &$pointer[ $key ];
333 $input = substr( $input, $index );
334 continue;
335 }
336 $end_index = strpos( $input, ']' );
337 $array_key = substr( $input, $index + 1, $end_index - 1 );
338 if ( $counter <= 1 ) {
339 $pointer[ $array_key ] = array();
340 } elseif ( is_array( $pointer ) ) {
341 $pointer[ $array_key ] = isset( $post_field_data['value'] ) ? $post_field_data['value'] : false;
342 }
343 if ( is_array( $pointer ) ) {
344 $pointer = &$pointer[ $array_key ];
345 }
346 $input = substr( $input, $end_index + 1 );
347 ++$counter;
348 }
349
350 $campaign_settings_v2 = array_merge_recursive( $campaign_settings_v2, $output );
351
352 } elseif ( strpos( $post_field_name, 'xxx' ) === false ) {
353
354 $campaign_settings_v2[ $post_field_name ] = $post_field_data['value'];
355
356 }
357
358 }
359 }
360
361 if ( empty( $campaign_tabs_location ) ) {
362 // either there is NO tabs in this template, or there's at least one tab but has no fields in it. Let's got hunting for tabs.
363 foreach ( $campaign_settings_v2['layout']['rows'] as $row_id => $row ) {
364 foreach ( $row['columns'] as $column_id => $column ) {
365 foreach ( $column['sections'] as $section_id => $section ) {
366 if ( 'tabs' === $section['type'] ) {
367 $campaign_tabs_location = array(
368 'row_id' => $row_id,
369 'column_id' => $column_id,
370 'section_id' => $section_id,
371 );
372 break;
373 }
374 }
375 }
376 }
377 }
378
379 // We still check the tabs location because the above might not have found any tabs (likely because on purpose the template doesn't have any).
380 if ( ! empty( $campaign_tabs_location ) ) {
381 // get the list of current tabs in the layout from what we remembered/recorded from the location when we when through things above.
382 $campaign_tabs = $campaign_settings_v2['layout']['rows'][ $campaign_tabs_location['row_id'] ]['columns'][ $campaign_tabs_location['column_id'] ]['sections'][ $campaign_tabs_location['section_id'] ]['tabs'];
383 if ( ! empty( $campaign_tabs ) && ! empty( $campaign_settings_v2['tabs'] ) ) {
384 foreach ( $campaign_settings_v2['tabs'] as $settings_campaign_tab_id => $settings_campaign_tab ) {
385 // if there is no tab in the tabs field in a section, but there's a tab registered in the "settings" tab area, then add it to the layout (likely an empty tab?).
386 if ( ! isset( $campaign_tabs[ $settings_campaign_tab_id ] ) || empty( $campaign_tabs[ $settings_campaign_tab_id ] ) ) {
387 // tab section - ensure even empty tabs are included.
388 $campaign_settings_v2['layout']['rows'][ $campaign_tabs_location['row_id'] ]['columns'][ $campaign_tabs_location['column_id'] ]['sections'][ $campaign_tabs_location['section_id'] ]['tabs'][ $settings_campaign_tab_id ] = array(
389 'title' => $settings_campaign_tab['title'],
390 'type' => 'fields',
391 'slug' => '',
392 'fields' => array(),
393 );
394 }
395 }
396 }
397 }
398
399 // Remove any tabs in the layout tab section that aren't in the main tabs directory.
400 if ( ! empty( $campaign_tabs_location ) ) {
401 $campaign_tabs = $campaign_settings_v2['layout']['rows'][ $campaign_tabs_location['row_id'] ]['columns'][ $campaign_tabs_location['column_id'] ]['sections'][ $campaign_tabs_location['section_id'] ]['tabs'];
402 foreach ( $campaign_tabs as $section_tab_id => $campaign_tab ) {
403 // if there is a tab in the tabs field in a section, but there's no tab registered in the "settings" tab area, then remove it from the layout.
404 if ( ! isset( $campaign_settings_v2['tabs'][ $section_tab_id ] ) || empty( $campaign_settings_v2['tabs'][ $section_tab_id ] ) ) {
405 unset( $campaign_tabs[ $section_tab_id ] );
406 }
407 }
408 $campaign_settings_v2['layout']['rows'][ $campaign_tabs_location['row_id'] ]['columns'][ $campaign_tabs_location['column_id'] ]['sections'][ $campaign_tabs_location['section_id'] ]['tabs'] = $campaign_tabs;
409 }
410
411 // If there are tabs in the section/layout but the tab directory is empty, we need to clear out the section/layout tabs.
412 if ( ! empty( $campaign_tabs_location ) && empty( $campaign_settings_v2['tabs'] ) ) {
413 $campaign_settings_v2['layout']['rows'][ $campaign_tabs_location['row_id'] ]['columns'][ $campaign_tabs_location['column_id'] ]['sections'][ $campaign_tabs_location['section_id'] ]['tabs'] = array();
414 }
415
416 // tags.
417 $custom_tax_terms = array();
418 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
419 if ( ! empty( $_POST['data'] ) ) {
420 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
421 $_tmp_data = json_decode( wp_unslash( $_POST['data'] ) );
422 foreach ( $_tmp_data as $index => $object ) {
423 if ( $object->name === 'settings[general][tags]' ) {
424 $custom_tax_terms[] = intval( $object->value );
425 }
426 }
427 $campaign_settings_v2['settings']['general']['tags'] = $custom_tax_terms;
428 }
429
430 // categories.
431 $custom_cat_terms = array();
432 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
433 if ( ! empty( $_POST['data'] ) ) {
434 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, decoded and sanitized in loop
435 $_tmp_data = json_decode( wp_unslash( $_POST['data'] ) );
436 foreach ( $_tmp_data as $index => $object ) {
437 if ( strpos( $object->name, 'categories-' ) !== false ) {
438 $custom_cat_terms[] = intval( $object->value );
439 }
440 }
441 }
442
443 // Starting cleaning up.
444
445 // Remove any donation option that doesn't have an amount.
446 if ( isset( $campaign_settings_v2['settings']['donation-options']['donation_amounts'] ) ) {
447 foreach ( $campaign_settings_v2['settings']['donation-options']['donation_amounts'] as $key => $info ) {
448 if ( isset( $info['amount'] ) && 0 === intval( $info['amount'] ) ) {
449 unset( $campaign_settings_v2['settings']['donation-options']['donation_amounts'][ $key ] );
450 }
451 }
452 }
453
454 // Remove any fields that are associated with feedback forms.
455 foreach ( $campaign_settings_v2 as $key => $info ) {
456 if ( strpos( $key, '-feedback-form' ) !== false ) {
457 unset( $campaign_settings_v2[ $key ] );
458 }
459 }
460
461 // convert date formats.
462 if ( isset( $campaign_settings_v2['settings']['general']['end_date'] ) ) {
463 $campaign_settings_v2['settings']['general']['end_date'] = Charitable_Campaign::sanitize_campaign_end_date( $campaign_settings_v2['settings']['general']['end_date'] );
464 }
465
466 // Sanitize some misc fields.
467 $campaign_settings_v2['settings']['general']['goal'] = ! empty( $campaign_settings_v2['settings']['general']['goal'] ) ? Charitable_Currency::get_instance()->sanitize_monetary_amount( (string) $campaign_settings_v2['settings']['general']['goal'] ) : false;
468 $campaign_settings_v2['settings']['general']['goal'] = '0' == $campaign_settings_v2['settings']['general']['goal'] ? '' : $campaign_settings_v2['settings']['general']['goal'];
469
470 // if the money values are negative, make it positive.
471 if ( ! empty( $campaign_settings_v2['settings']['general']['goal'] ) && '-' === substr( $campaign_settings_v2['settings']['general']['goal'], 0, 1 ) ) {
472 $campaign_settings_v2['settings']['general']['goal'] = substr( $campaign_settings_v2['settings']['general']['goal'], 1 );
473 }
474 if ( ! empty( $campaign_settings_v2['settings']['donation-options']['minimum_donation_amount'] ) && '-' === substr( $campaign_settings_v2['settings']['donation-options']['minimum_donation_amount'], 0, 1 ) ) {
475 $campaign_settings_v2['settings']['donation-options']['minimum_donation_amount'] = substr( $campaign_settings_v2['settings']['donation-options']['minimum_donation_amount'], 1 );
476 }
477
478 $campaign_settings_v2['field_id'] = charitable_find_highest_field_key( $campaign_settings_v2 );
479
480 // Update OLD v1 campaign data.
481 if ( intval( $campaign_post['id']['value'] ) === 0 ) {
482 // new campaign.
483 $campaign_id = charitable_create_campaign(
484 array(
485 'title' => isset( $campaign_post['title']['value'] ) ? $campaign_post['title']['value'] : false,
486 'description' => isset( $campaign_post['setings_desc']['value'] ) ? $campaign_post['setings_desc']['value'] : false,
487 )
488 );
489 } else {
490 // existing campaign.
491 $campaign_id = charitable_create_campaign(
492 array(
493 'ID' => isset( $campaign_post['id']['value'] ) ? $campaign_post['id']['value'] : false,
494 'title' => isset( $campaign_post['title']['value'] ) ? $campaign_post['title']['value'] : false,
495 'description' => isset( $campaign_post['setings_desc'] ) ? $campaign_post['setings_desc']['value'] : false,
496 )
497 );
498 }
499
500 // Update campaign data for v1.
501 $processor = new Charitable_Campaign_Processor();
502 $processor->campaign_id = $campaign_id;
503
504 // Base meta values.
505 $base_meta_keys = array(
506 'goal' => 0,
507 'end_date' => 0,
508 'suggested_donations' => array(),
509 'suggested_donations_default' => 0,
510 'allow_custom_donations' => 0,
511 'minimum_donation_amount' => 0,
512 'donate_button_text' => '',
513 ); // taken from class-charitable-campaign-processor.php.
514 foreach ( $base_meta_keys as $base_meta_key => $base_meta_value ) {
515 $value = charitable_search_settings( $campaign_settings_v2['settings'], $base_meta_key );
516 if ( $value ) {
517 $processor->save_meta_field( $value, $base_meta_key );
518 } else {
519 $processor->save_meta_field( $base_meta_value, $base_meta_key );
520 }
521 }
522
523 if ( ! empty( $campaign_settings_v2['settings']['donation-options']['donation_amounts'] ) ) {
524 update_post_meta( $campaign_id, '_campaign_suggested_donations', ( $campaign_settings_v2['settings']['donation-options']['donation_amounts'] ) );
525 } else {
526 update_post_meta( $campaign_id, '_campaign_suggested_donations', ( array() ) );
527 }
528
529 if ( ! empty( $campaign_settings_v2['settings']['donation-options']['suggested_donations_default'] ) ) {
530 $suggest_donation_row_id = intval( $campaign_settings_v2['settings']['donation-options']['suggested_donations_default'] );
531 $array_to_serialize = array( html_entity_decode( charitable_format_money( $campaign_settings_v2['settings']['donation-options']['donation_amounts'][ $suggest_donation_row_id ]['amount'] ) ) );
532 update_post_meta( $campaign_id, '_campaign_suggested_donations_default', $array_to_serialize );
533 } else {
534 update_post_meta( $campaign_id, '_campaign_suggested_donations_default', ( serialize( array() ) ) );
535 }
536
537 /* descriptions */
538 if ( ! empty( $campaign_settings_v2['settings']['general']['description'] ) ) {
539 update_post_meta( $campaign_id, '_campaign_description', trim( $campaign_settings_v2['settings']['general']['description'] ) ); // todo: santitize this.
540 } else {
541 update_post_meta( $campaign_id, '_campaign_description', false );
542 }
543 if ( ! empty( $campaign_settings_v2['settings']['general']['extended_description'] ) ) {
544 $campaign_post_update = array(
545 'ID' => $campaign_id,
546 'post_content' => $campaign_settings_v2['settings']['general']['extended_description'],
547 );
548 } elseif ( ! empty( $campaign_settings_v2['settings']['general']['description'] ) ) {
549 $campaign_post_update = array(
550 'ID' => $campaign_id,
551 'post_content' => $campaign_settings_v2['settings']['general']['description'],
552 );
553 } else {
554 $campaign_post_update = array(
555 'ID' => $campaign_id,
556 'post_content' => '',
557 );
558 }
559
560 /* update campaign author which should be the campaign_creator_id, if it exists (otherwise leave it alone) */
561 if ( ! empty( $campaign_settings_v2['settings']['general']['campaign_creator_id'] ) ) {
562 $campaign_post_update['post_author'] = intval( $campaign_settings_v2['settings']['general']['campaign_creator_id'] );
563 }
564
565 /* tags */
566 wp_set_post_terms( $campaign_id, $custom_tax_terms, 'campaign_tag' );
567
568 /* categories */
569 wp_set_post_terms( $campaign_id, $custom_cat_terms, 'campaign_category' );
570
571 // Update the post status of the campaign.
572 $post_status = false;
573 // In the next statement, false means simply nothing was passed and if that happens we don't change anything.
574 $desired_post_status = isset( $_POST['status'] ) ? esc_attr( $_POST['status'] ) : false; // phpcs:ignore
575
576 if ( $desired_post_status ) {
577
578 switch ( $desired_post_status ) {
579 case 'draft':
580 case 'pending':
581 case 'publish':
582 case 'private':
583 case 'future':
584 $post_status = $desired_post_status;
585 break;
586
587 case 'switch-draft':
588 $post_status = 'draft';
589 break;
590
591 case 'switch-review':
592 $post_status = 'pending';
593 break;
594
595 default:
596 break;
597 }
598
599 if ( $post_status ) {
600 $campaign_post_update['post_status'] = apply_filters( 'charitable_campaign_builder_post_status_on_save', $post_status, $campaign_id );
601 }
602
603 $campaign_settings_v2['post_status'] = $post_status;
604 $campaign_settings_v2['post_status_label'] = ucfirst( $post_status );
605
606 }
607
608 // IF this isn't a preview request, update the post into the database.
609 if ( ! $is_preview ) :
610 wp_update_post( $campaign_post_update );
611 endif;
612
613 /* donation summary */
614 $summary_hide_keys = array(
615 'campaign_hide_amount_donated',
616 'campaign_hide_number_of_donors',
617 'campaign_hide_percent_raised',
618 'campaign_hide_time_remaining',
619 );
620
621 if ( ! empty( $campaign_settings_v2['settings']['campaign-summary'] ) ) {
622 foreach ( $summary_hide_keys as $meta_key ) {
623 if ( ! empty( $campaign_settings_v2['settings']['campaign-summary'][ $meta_key ] ) ) {
624 update_post_meta( $campaign_id, '_' . $meta_key, array( str_replace( 'campaign_', '', $meta_key ) ) );
625 } else {
626 update_post_meta( $campaign_id, '_' . $meta_key, '' );
627 }
628 }
629 } else {
630 foreach ( $summary_hide_keys as $meta_key ) {
631 update_post_meta( $campaign_id, '_' . $meta_key, '' );
632 }
633 }
634
635 if ( ! empty( $campaign_settings_v2['settings']['campaign-summary']['donation_button_text'] ) ) {
636 update_post_meta( $campaign_id, '_campaign_donate_button_text', trim( wp_strip_all_tags( $campaign_settings_v2['settings']['campaign-summary']['donation_button_text'] ) ) );
637 } else {
638 update_post_meta( $campaign_id, '_campaign_donate_button_text', false );
639 }
640
641 if ( $campaign_id && 0 === intval( $campaign_settings_v2['id'] ) ) {
642 $campaign_settings_v2['id'] = intval( $campaign_id );
643 }
644
645 // Adjust campaign creator ID if applicable.
646 if ( isset( $campaign_settings_v2['settings']['campaign-creator']['campaign_creator_id'] ) ) {
647 $creator_id = intval( $campaign_settings_v2['settings']['campaign-creator']['campaign_creator_id'] );
648 if ( 0 !== $creator_id ) { // user id must exist in the posted data.
649 $user = get_userdata( $creator_id ); // user must exist.
650 if ( $user !== false ) {
651 $arg = array(
652 'ID' => $campaign_id,
653 'post_author' => $creator_id,
654 );
655 wp_update_post( $arg );
656 }
657 }
658
659 }
660
661 if ( isset( $campaign_settings_v2['tabs'] ) && is_array( $campaign_settings_v2['tabs'] ) ) :
662 $campaign_settings_v2['tab_order'] = array_keys( $campaign_settings_v2['tabs'] );
663 endif;
664
665 if ( isset( $campaign_settings_v2['layout']['_header']['fields'] ) && is_array( $campaign_settings_v2['layout']['_header']['fields'] ) ) :
666 $campaign_settings_v2['layout']['_header']['order'] = array_keys( $campaign_settings_v2['layout']['_header']['fields'] );
667 endif;
668 if ( isset( $campaign_settings_v2['layout']['_tabs'] ) && is_array( $campaign_settings_v2['layout']['_tabs'] ) ) :
669 foreach ( $campaign_settings_v2['layout']['_tabs'] as $tab_id => $tab_info ) :
670 $campaign_settings_v2['layout']['_tabs'][ $tab_id ]['order'] = array_keys( $campaign_settings_v2['layout']['_tabs'][ $tab_id ]['fields'] );
671 endforeach;
672 endif;
673
674
675 // todo: remove this at some point - this was likely just because of testing.
676 unset( $campaign_settings_v2['settings]'] );
677
678 // Allow addons to update, sync, or override settings before they are saved.
679 $campaign_settings_v2 = apply_filters( 'charitable_campaign_builder_save_campaign_settings', $campaign_settings_v2, $campaign_id );
680
681 // Save the settings as post metadata.
682 if ( $is_preview ) {
683 // If this is a preview request, save the settings as a transient ONLY.
684 set_transient( 'charitable_campaign_preview_' . $campaign_id, $campaign_settings_v2, DAY_IN_SECONDS );
685 } else {
686 // IF this isn't a preview request, update the post into the database and update the transient (the preview at this point should match the real thing).
687 set_transient( 'charitable_campaign_preview_' . $campaign_id, $campaign_settings_v2, DAY_IN_SECONDS );
688 update_post_meta( $campaign_id, 'campaign_settings_v2', $campaign_settings_v2 );
689 }
690
691 /**
692 * Fires after updating campaign data.
693 *
694 * @since 1.8.0
695 *
696 * @param int $campaign_id campaign ID.
697 * @param array $data campaign data.
698 */
699 do_action( 'charitable_builder_save_campaign', $campaign_id, $campaign_settings_v2, $campaign_post, $is_preview );
700
701 if ( ! $campaign_id ) {
702 wp_send_json_error( esc_html__( 'Something went wrong while saving the campaign.', 'charitable' ) );
703 }
704
705 // Response data, start with the campaign settings.
706 $response_data = $campaign_settings_v2;
707 $response_data['redirect'] = admin_url( 'admin.php?page=charitable-overview' );
708 $response_data['campaign_id'] = $campaign_id;
709 $response_data['preview_url'] = charitable_get_campaign_preview_url( $campaign_id, true ); // get_preview_post_link( $campaign_id );
710 $response_data['permalink'] = get_permalink( $campaign_id );
711
712 /**
713 * Allows filtering ajax response data after form was saved.
714 *
715 * @since 1.8.0
716 *
717 * @param array $response_data The data to be sent in the response.
718 * @param int $campaign_id Campaign ID.
719 * @param array $data Campaign data.
720 */
721 $response_data = apply_filters(
722 'charitable_builder_save_campaign_response_data',
723 $response_data,
724 $campaign_id,
725 $data
726 );
727
728 wp_send_json_success( $response_data );
729 }
730 add_action( 'wp_ajax_charitable_save_campaign', 'charitable_save_campaign' );
731
732 /**
733 * Converts the template layout to campaign layout.
734 *
735 * @param array $campaign_settings_v2 The campaign settings.
736 * @param array $campaign_post The campaign post data.
737 *
738 * @return array The updated campaign settings.
739 */
740 function charitable_template_layout_to_campaign_layout( $campaign_settings_v2, $campaign_post ) {
741
742 $builder_template = new Charitable_Campaign_Builder_Templates();
743 $template_id = ! empty( $campaign_post['template_id']['value'] ) ? esc_attr( $campaign_post['template_id']['value'] ) : false;
744 $template_data = ( false !== $template_id ) ? $builder_template->get_template_data( $template_id ) : array();
745
746 $campaign_settings_v2['layout']['rows'] = array();
747
748 $row_id = 0;
749 $column_id = 0;
750 $section_id = 0;
751
752 foreach ( $template_data['layout'] as $row_id => $row ) :
753
754 $campaign_settings_v2['layout']['rows'][ $row_id ]['type'] = ! empty( $row['type'] ) ? $row['type'] : 'row';
755 $campaign_settings_v2['layout']['rows'][ $row_id ]['css_class'] = ! empty( $row['css_class'] ) ? $row['css_class'] : false;
756
757 foreach ( $row['columns'] as $column_key => $column ) :
758
759 foreach ( $column as $section_key => $section ) :
760
761 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'] = array();
762
763 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['type'] = esc_attr( $section['type'] );
764
765 switch ( $section['type'] ) {
766 case 'fields':
767 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['fields'] = array(); // [0] => 1 /// (array) $section['fields'];
768
769 break;
770 case 'tabs':
771 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ]['tabs'] = array(); // [0] => 1 /// (array) $section['fields'];
772
773 if ( ! empty( $section['tabs'] ) ) {
774 foreach ( $section['tabs'] as $tab_id => $tab_info ) :
775 $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ][ $section['type'] ][ $tab_id ] = array(
776 'title' => ! empty( $campaign_post[ 'tabs__' . $tab_id . '__title' ]['value'] ) ? sanitize_text_field( $campaign_post[ 'tabs__' . $tab_id . '__title' ]['value'] ) : $tab_info['title'],
777 'type' => $tab_info['type'],
778 'slug' => sanitize_title( $tab_info['slug'] ),
779 'fields' => array(),
780 );
781 endforeach;
782 }
783
784 break;
785 default:
786 // $campaign_settings_v2['layout']['rows'][ $row_id ]['columns'][ $column_id ]['sections'][ $section_id ][ $section['type'] ] = $section[ $section['type'] ];
787 break;
788 }
789
790 ++$column_id;
791 ++$section_id;
792
793 endforeach;
794
795 // $column_id++;
796
797 endforeach;
798
799 $campaign_settings_v2['layout']['rows'][ $row_id ]['fields'] = array();
800
801 ++$row_id;
802
803 endforeach;
804
805 return $campaign_settings_v2;
806 }
807
808 /**
809 * Searches for a key in a multidimensional array.
810 *
811 * @param array $array The array to search.
812 * @param mixed $key The key to search for.
813 * @return mixed|null The value of the key if found, null otherwise.
814 */
815 function charitable_search_settings( $array, $key ) {
816 // If the key exists in the current level of the array, return its value.
817 if ( array_key_exists( $key, $array ) ) {
818 return $array[ $key ];
819 }
820
821 // Iterate over each element in the array.
822 foreach ( $array as $value ) {
823 // If the element is an array, recursively search it for the key.
824 if ( is_array( $value ) ) {
825 $result = charitable_search_settings( $value, $key );
826
827 // If the key is found in the nested array, return its value.
828 if ( $result !== null ) {
829 return $result;
830 }
831 }
832 }
833
834 // The key was not found in the array.
835 return null;
836 }
837
838
839 /**
840 * Find the highest field ID field so we can keep the max number accurate.
841 *
842 * @since 1.8.0
843 *
844 * @param array $array Settings data.
845 */
846 function charitable_find_highest_field_key( $array ) {
847 $highest_key = null;
848
849 // Iterate through each element in the array.
850 foreach ( $array as $key => $value ) {
851 // Check if the current element is an array.
852 if ( is_array( $value ) ) {
853 // Recursively call the function to search through nested arrays.
854 $field_key = charitable_find_highest_field_key( $value );
855
856 // Check if the key is numeric and greater than the current highest key.
857 if ( is_numeric( $field_key ) && ( $highest_key === null || $field_key > $highest_key ) ) {
858 $highest_key = $field_key;
859 }
860 } else {
861 // Check if the key is numeric and greater than the current highest key.
862 if ( is_numeric( $key ) && ( $highest_key === null || $key > $highest_key ) ) {
863 $highest_key = $key;
864 }
865 }
866 }
867
868 return $highest_key;
869 }
870
871 /**
872 * A very particular util array for the builder.
873 *
874 * @since 1.8.0
875 *
876 * @param string $string The string from the settings data.
877 * @param string $value Value.
878 * @param string $end_key End key.
879 */
880 function charitable_convert_campaign_string_to_array( $string, $value, $end_key = 'amount' ) {
881
882 $keys = explode( '][', trim( $string, '[]' ) ); // split the string into an array of keys.
883 $array = [];
884 $current = &$array;
885 foreach ( $keys as $key ) {
886 $current[ $key ] = [];
887 if ( $key === $end_key ) {
888 $current[ $key ] = $value; // $value;
889 }
890 $current = &$current[ $key ];
891 }
892
893 return $array;
894 }
895
896 /**
897 * Convert a campaign string to an array.
898 *
899 * @param string $input The input string to convert.
900 * @param mixed $value The default value to use for each key.
901 * @param string $end_key The key to use for the final value.
902 * @return array The resulting array.
903 */
904 function charitable_convert_campaign_string_to_array_new( $input, $value, $end_key = 'amount' ) { // phpcs:ignore
905
906 $output = array();
907 $pointer = &$output;
908
909 while ( ( $index = strpos( $input, '[' ) ) !== false ) {
910 if ( $index != 0 ) {
911 $key = substr( $input, 0, $index );
912 $pointer[ $key ] = array();
913 $pointer = &$pointer[ $key ];
914 $input = substr( $input, $index );
915 continue;
916 }
917 $end_index = strpos( $input, ']' );
918 $array_key = substr( $input, $index + 1, $end_index - 1 );
919 $pointer[ $array_key ] = array();
920 $pointer = &$pointer[ $array_key ];
921 $input = substr( $input, $end_index + 1 );
922 }
923
924 return( $output );
925 }
926
927 /**
928 * Create a new form.
929 *
930 * @since 1.8.0
931 */
932 function charitable_new_campaign() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
933
934 check_ajax_referer( 'charitable-builder', 'nonce' );
935
936 if ( empty( $_POST['title'] ) ) {
937 wp_send_json_error(
938 [
939 'error_type' => 'missing_campaign_title',
940 'message' => esc_html__( 'No form name provided.', 'charitable' ),
941 ]
942 );
943 }
944
945 $campaign_title = sanitize_text_field( wp_unslash( $_POST['title'] ) );
946 $campaign_template = empty( $_POST['template'] ) ? 'blank' : sanitize_text_field( wp_unslash( $_POST['template'] ) );
947
948 if ( ! charitable()->get( 'builder_templates' )->is_valid_template( $campaign_template ) ) {
949 wp_send_json_error(
950 [
951 'error_type' => 'invalid_template',
952 'message' => esc_html__( 'The template you selected is currently not available, but you can try again later. If you continue to have trouble, please reach out to support.', 'charitable' ),
953 ]
954 );
955 }
956
957 // phpcs:ignore WordPress.WP.DeprecatedFunctions.get_page_by_titleFound -- Legacy support, will be replaced with WP_Query in future version
958 $title_exists = get_page_by_title( $campaign_title, 'OBJECT', 'charitable' );
959 $campaign_id = charitable()->get( 'form' )->add(
960 $campaign_title,
961 [],
962 [
963 'template' => $campaign_template,
964 ]
965 );
966
967 if ( $title_exists !== null ) {
968
969 // Skip creating a revision for this action.
970 remove_action( 'post_updated', 'wp_save_post_revision' );
971
972 wp_update_post(
973 [
974 'ID' => $campaign_id,
975 'post_title' => $campaign_title . ' (ID #' . $campaign_id . ')',
976 ]
977 );
978
979 // Restore the initial revisions state.
980 add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
981 }
982
983 if ( ! $campaign_id ) {
984 wp_send_json_error(
985 [
986 'error_type' => 'cant_create_campaign',
987 'message' => esc_html__( 'Error creating form.', 'charitable' ),
988 ]
989 );
990 }
991
992 if ( charitable_current_user_can( 'edit_campaign_single', $campaign_id ) ) {
993 wp_send_json_success(
994 [
995 'id' => $campaign_id,
996 'redirect' => add_query_arg(
997 [
998 'view' => 'fields',
999 'campaign_id' => $campaign_id,
1000 'newform' => '1',
1001 ],
1002 admin_url( 'admin.php?page=charitable-builder' )
1003 ),
1004 ]
1005 );
1006 }
1007
1008 if ( charitable_current_user_can( 'view_campaigns' ) ) {
1009 wp_send_json_success( [ 'redirect' => admin_url( 'admin.php?page=charitable-overview' ) ] );
1010 }
1011
1012 wp_send_json_success( [ 'redirect' => admin_url() ] );
1013 }
1014
1015 add_action( 'wp_ajax_charitable_new_campaign', 'charitable_new_campaign' );
1016
1017 /**
1018 * Update form template.
1019 *
1020 * @since 1.0.0
1021 */
1022 function charitable_update_campaign_template() {
1023
1024 // Run a security check.
1025 check_ajax_referer( 'charitable-builder', 'nonce' );
1026
1027 // Check for form name.
1028 if ( empty( $_POST['campaign_id'] ) ) {
1029 wp_send_json_error(
1030 [
1031 'error_type' => 'invalid_campaign_id',
1032 'message' => esc_html__( 'No form ID provided.', 'charitable' ),
1033 ]
1034 );
1035 }
1036
1037 $campaign_id = absint( $_POST['campaign_id'] );
1038 $campaign_template = empty( $_POST['template'] ) ? 'blank' : sanitize_text_field( wp_unslash( $_POST['template'] ) );
1039
1040 if ( ! charitable()->get( 'builder_templates' )->is_valid_template( $campaign_template ) ) {
1041 wp_send_json_error(
1042 [
1043 'error_type' => 'invalid_template',
1044 'message' => esc_html__( 'The template you selected is currently not available, but you can try again later. If you continue to have trouble, please reach out to support.', 'charitable' ),
1045 ]
1046 );
1047 }
1048
1049 $data = charitable()->get( 'form' )->get(
1050 $campaign_id,
1051 [
1052 'content_only' => true,
1053 ]
1054 );
1055
1056 if ( ! empty( $_POST['title'] ) ) {
1057 $data['settings']['campaign_title'] = sanitize_text_field( wp_unslash( $_POST['title'] ) );
1058 }
1059
1060 $updated = (bool) charitable()->get( 'form' )->update(
1061 $campaign_id,
1062 $data,
1063 [
1064 'template' => $campaign_template,
1065 ]
1066 );
1067
1068 if ( $updated ) {
1069 wp_send_json_success(
1070 [
1071 'id' => $campaign_id,
1072 'redirect' => add_query_arg(
1073 [
1074 'view' => 'fields',
1075 'campaign_id' => $campaign_id,
1076 ],
1077 admin_url( 'admin.php?page=charitable-builder' )
1078 ),
1079 ]
1080 );
1081 }
1082
1083 wp_send_json_error(
1084 [
1085 'error_type' => 'cant_update',
1086 'message' => esc_html__( 'Error updating form template.', 'charitable' ),
1087 ]
1088 );
1089 }
1090
1091 add_action( 'wp_ajax_charitable_update_campaign_template', 'charitable_update_campaign_template' );
1092
1093 /**
1094 * Campaign Builder update next field ID.
1095 *
1096 * @since 1.8.0
1097 */
1098 function charitable_builder_increase_next_field_id() {
1099
1100 // Run a security check.
1101 check_ajax_referer( 'charitable-builder', 'nonce' );
1102
1103 // Check for permissions.
1104 if ( ! charitable_current_user_can( 'edit_campaigns' ) ) {
1105 wp_send_json_error();
1106 }
1107
1108 // Check for required items.
1109 if ( empty( $_POST['campaign_id'] ) ) {
1110 wp_send_json_error();
1111 }
1112
1113 $args = [];
1114
1115 // In the case of duplicating the Layout field that contains a bunch of fields,
1116 // we need to set the next `field_id` to the desired value which is passed via POST argument.
1117 if ( ! empty( $_POST['field_id'] ) ) {
1118 $args['field_id'] = absint( $_POST['field_id'] );
1119 }
1120
1121 charitable()->get( 'form' )->next_field_id( absint( $_POST['campaign_id'] ), $args );
1122
1123 wp_send_json_success();
1124 }
1125
1126 add_action( 'wp_ajax_charitable_builder_increase_next_field_id', 'charitable_builder_increase_next_field_id' );
1127
1128 /**
1129 * Campaign Builder Dynamic Choices option toggle.
1130 *
1131 * This can be triggered with select/radio/checkbox fields.
1132 *
1133 * @since 1.8.0
1134 */
1135 function charitable_builder_dynamic_choices() {
1136
1137 // Run a security check.
1138 check_ajax_referer( 'charitable-builder', 'nonce' );
1139
1140 // Check for permissions.
1141 if ( ! charitable_current_user_can( 'edit_campaigns' ) ) {
1142 wp_send_json_error();
1143 }
1144
1145 // Check for valid/required items.
1146 if ( ! isset( $_POST['field_id'] ) || empty( $_POST['type'] ) || ! in_array( $_POST['type'], [ 'post_type', 'taxonomy' ], true ) ) {
1147 wp_send_json_error();
1148 }
1149
1150 $type = sanitize_key( $_POST['type'] );
1151 $id = absint( $_POST['field_id'] );
1152
1153 // Fetch the option row HTML to be returned to the builder.
1154 $field = new Charitable_Field_Select( false );
1155 $field_args = [
1156 'id' => $id,
1157 'dynamic_choices' => $type,
1158 ];
1159 $option_row = $field->field_option( 'dynamic_choices_source', $field_args, [], false );
1160
1161 wp_send_json_success(
1162 [
1163 'markup' => $option_row,
1164 ]
1165 );
1166 }
1167
1168 add_action( 'wp_ajax_charitable_builder_dynamic_choices', 'charitable_builder_dynamic_choices' );
1169
1170 /**
1171 * Content preview via ajax.
1172 *
1173 * @since 1.8.0
1174 */
1175 function charitable_builder_tab_content_preview() {
1176 // phpcs:disable WordPress.Security.NonceVerification.Missing
1177 $type = isset( $_POST['type'] ) ? sanitize_key( wp_unslash( $_POST['type'] ) ) : '';
1178 // phpcs:enable WordPress.Security.NonceVerification.Missing
1179 $output = charitable_builder_tab_content_preview_by_type( $type );
1180
1181 wp_send_json_success(
1182 [
1183 'output' => $output,
1184 ]
1185 );
1186 }
1187
1188 add_action( 'wp_ajax_charitable_tab_content_preview', 'charitable_builder_tab_content_preview' );
1189
1190 /**
1191 * Content preview via ajax.
1192 *
1193 * @since 1.8.0
1194 */
1195 function charitable_builder_field_content_preview() {
1196 // phpcs:disable WordPress.Security.NonceVerification.Missing
1197 if ( ! empty( $_POST['field_type'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below
1198
1199 $type = 'Charitable_Field_' . str_replace( ' ', '_', ( ucwords( str_replace( '-', ' ', sanitize_key( wp_unslash( $_POST['field_type'] ) ) ) ) ) );
1200
1201 if ( class_exists( $type ) ) :
1202
1203 $class = new $type();
1204 $field_id = isset( $_POST['field_id'] ) ? intval( wp_unslash( $_POST['field_id'] ) ) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1205 $campaign_data = isset( $_POST['campaign_id'] ) ? get_post_meta( intval( wp_unslash( $_POST['campaign_id'] ) ), 'campaign_settings_v2', true ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1206 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array data, sanitized in field_preview method
1207 $field_data = $_POST;
1208
1209 ob_start();
1210
1211 echo $class->field_preview( $field_data, $campaign_data, $field_id, false, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1212
1213 $output = ob_get_clean();
1214
1215 wp_send_json_success(
1216 [
1217 'output' => $output,
1218 ]
1219 );
1220
1221 endif;
1222 // phpcs:enable WordPress.Security.NonceVerification.Missing
1223 }
1224
1225 wp_send_json_error();
1226 }
1227
1228 add_action( 'wp_ajax_charitable_builder_field_content_preview', 'charitable_builder_field_content_preview' );
1229
1230 /**
1231 * Perform test connection to verify that the current web host can successfully
1232 * make outbound SSL connections.
1233 *
1234 * @since 1.8.0
1235 */
1236 function charitable_verify_ssl_ajax() {
1237
1238 // Run a security check.
1239 check_ajax_referer( 'charitable-admin', 'nonce' );
1240
1241 // Check for permissions.
1242 if ( ! charitable_current_user_can() ) {
1243 wp_send_json_error(
1244 [
1245 'msg' => esc_html__( 'You do not have permission to perform this operation.', 'charitable' ),
1246 ]
1247 );
1248 }
1249
1250 $response = wp_remote_post( 'https://wpcharitable.com/connection-test.php' );
1251
1252 if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
1253 wp_send_json_success(
1254 [
1255 'msg' => esc_html__( 'Success! Your server can make SSL connections.', 'charitable' ),
1256 ]
1257 );
1258 }
1259
1260 wp_send_json_error(
1261 [
1262 'msg' => esc_html__( 'There was an error and the connection failed. Please contact your web host with the technical details below.', 'charitable' ),
1263 'debug' => '<pre>' . print_r( map_deep( $response, 'wp_strip_all_tags' ), true ) . '</pre>', // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1264 ]
1265 );
1266 }
1267 // add_action( 'wp_ajax_charitable_verify_ssl', 'charitable_verify_ssl_ajax' ); // Disabled - conflicts with handler in charitable-core-admin-functions.php
1268
1269
1270 /**
1271 * Update form template.
1272 *
1273 * @since 1.8.0
1274 */
1275 function charitable_builder_ajax_update_campaign_creator() {
1276
1277 // Run a security check.
1278 check_ajax_referer( 'charitable-builder', 'nonce' );
1279
1280 $campaign_id = ! empty( $_POST['campaign_id'] ) ? absint( $_POST['campaign_id'] ) : false;
1281 $new_creator_id = ! empty( $_POST['creator_id'] ) ? absint( $_POST['creator_id'] ) : false;
1282
1283 if ( false === $new_creator_id ) {
1284 wp_send_json_error(
1285 [
1286 'error_type' => 'invalid_campaign_id',
1287 'message' => esc_html__( 'No creator ID provided.', 'charitable' ),
1288 ]
1289 );
1290 }
1291
1292 // check to see if the user exists.
1293 $user = get_userdata( $new_creator_id );
1294 if ( $user === false ) {
1295 wp_send_json_error(
1296 [
1297 'error_type' => 'invalid_campaign_id',
1298 'message' => esc_html__( 'Invalid creator ID provided.', 'charitable' ),
1299 ]
1300 );
1301 }
1302
1303 // todo: check creds?
1304
1305 if ( $user ) {
1306
1307 wp_send_json_success(
1308 [
1309 'id' => $campaign_id,
1310 'new_creator_id' => $new_creator_id,
1311 'avatar_url' => esc_url( get_avatar_url( $new_creator_id ) ),
1312 'creator_name' => $user->display_name . ' (USER ID ' . $new_creator_id . ')',
1313 'public_profile_link' => get_author_posts_url( $new_creator_id ),
1314 'edit_profile_link' => admin_url( 'user-edit.php?user_id=' . $new_creator_id ),
1315 ]
1316 );
1317 }
1318
1319 wp_send_json_error(
1320 [
1321 'error_type' => 'cant_update',
1322 'message' => esc_html__( 'Error updating campaign creator.', 'charitable' ),
1323 ]
1324 );
1325 }
1326 add_action( 'wp_ajax_charitable_update_campaign_creator', 'charitable_builder_ajax_update_campaign_creator' );
1327
1328 /**
1329 * Update form template.
1330 *
1331 * @since 1.8.0
1332 */
1333 function charitable_builder_ajax_update_campaign_status_link() {
1334
1335 // Run a security check.
1336 check_ajax_referer( 'charitable-builder', 'nonce' );
1337
1338 // Check for campaign ID.
1339 if ( empty( $_POST['campaign_id'] ) || 0 === intval( $_POST['campaign_id'] ) ) {
1340 wp_send_json_error(
1341 [
1342 'error_type' => 'invalid_campaign_id',
1343 'message' => esc_html__( 'No campaign ID provided.', 'charitable' ),
1344 ]
1345 );
1346 }
1347
1348 $campaign_id = absint( $_POST['campaign_id'] );
1349
1350 // get status.
1351 $campaign = $campaign_id ? charitable_get_campaign( $campaign_id ) : false;
1352 $status = $campaign ? $campaign->get_status() : false;
1353 if ( 'finished' === $status && $campaign->has_goal() ) {
1354 $status = $campaign->has_achieved_goal() ? 'successful' : 'unsuccessful';
1355 }
1356
1357 // campaign status.
1358 $html = '<span class="charitable-view-campaign-external-link"><a href="' . get_permalink( $campaign_id ) . '" target="_blank" title="' . esc_html__( 'View Campaign Page', 'charitable' ) . '"><!--<i class="fa fa-eye"></i>--></a></span>';
1359
1360 wp_send_json_success(
1361 [
1362 'id' => $campaign_id,
1363 'html' => $html,
1364 ]
1365 );
1366 }
1367 add_action( 'wp_ajax_charitable_update_campaign_status_link', 'charitable_builder_ajax_update_campaign_status_link' );
1368
1369 /**
1370 * Receive and process feedback form content.
1371 *
1372 * @since 1.8.0
1373 */
1374 function charitable_campaign_builder_send_feedback_ajax() {
1375 // phpcs:disable WordPress.Security.NonceVerification.Missing
1376 /* Data to send in our API request */
1377 $feedback_params = array(
1378 'feedback_action' => 'template_feedback',
1379 'type' => ! empty( $_POST['data']['type'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['type'] ) ) : false, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1380 'name' => ! empty( $_POST['data']['name'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['name'] ) ) : false, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1381 'email' => ! empty( $_POST['data']['email'] ) ? sanitize_email( wp_unslash( $_POST['data']['email'] ) ) : false, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1382 'feedback' => ! empty( $_POST['data']['feedback'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['feedback'] ) ) : false, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1383 );
1384 // phpcs:enable WordPress.Security.NonceVerification.Missing
1385
1386 /* Call the custom API */
1387 $response = wp_remote_post(
1388 'https://wpcharitable.com',
1389 array(
1390 'timeout' => 15,
1391 'sslverify' => false,
1392 'body' => $feedback_params,
1393 )
1394 );
1395
1396 if ( is_wp_error( $response ) ) {
1397 wp_send_json_error(
1398 );
1399 } else {
1400 wp_send_json_success(
1401 [
1402 'sent' => true,
1403 ]
1404 );
1405 }
1406 }
1407 add_action( 'wp_ajax_charitable_campaign_builder_send_feedback_ajax', 'charitable_campaign_builder_send_feedback_ajax' );
1408
1409