PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
← All changes | admin-core/inc/meta-ops.php +48 -6 3.1.3 → 3.3.0 View file →
@@ -23,12 +23,14 @@
23 23 * @param int $post_id post id.
24 24 * @param array $post_meta options to store.
25 25 * @param string $action action to check nonce.
26 26 *
27 - * @return void
27 + * @return array<int, string> Meta keys skipped because the user cannot author scripts.
28 28 */
29 29 public static function save_meta_fields( $post_id, $post_meta, $action = '' ) {
30 30
31 + $skipped_fields = array();
32 +
31 33 if ( ! check_ajax_referer( $action, 'security', false ) ) {
32 34 $response_data = array( 'message' => __( 'Nonce validation failed', 'cartflows' ) );
33 35 wp_send_json_error( $response_data );
34 36 }
@@ -33,9 +35,9 @@
33 35 wp_send_json_error( $response_data );
34 36 }
35 37
36 38 if ( ! ( $post_id && is_array( $post_meta ) ) ) {
37 - return;
39 + return $skipped_fields;
38 40 }
39 41
40 42 $allowed_html = array(
41 43 'a' => array(
@@ -52,9 +54,14 @@
52 54 if ( ! isset( $_POST[ $key ] ) ) {
53 55 continue;
54 56 }
55 57
56 - $meta_value = false;
58 + /*
59 + * null leaves the stored meta alone; false deletes it. Starting at null means a
60 + * sanitize case that forgets to assign can no longer silently destroy saved data
61 + * — every branch that genuinely wants a delete now says so explicitly.
62 + */
63 + $meta_value = null;
57 64
58 65 // Sanitize values.
59 66 $sanitize_filter = ( isset( $data['sanitize'] ) ) ? $data['sanitize'] : 'FILTER_DEFAULT';
60 67
@@ -73,8 +80,11 @@
73 80
74 81 case 'FILTER_CARTFLOWS_ARRAY':
75 82 if ( isset( $_POST[ $key ] ) && is_array( $_POST[ $key ] ) ) {
76 83 $meta_value = array_map( 'sanitize_text_field', wp_unslash( $_POST[ $key ] ) );
84 + } else {
85 + // A posted-but-not-array value has always cleared this meta; keep it.
86 + $meta_value = false;
77 87 }
78 88 break;
79 89
80 90 case 'FILTER_SANITIZE_COLOR':
@@ -96,8 +106,10 @@
96 106 // Custom JS/CSS sinks are output raw on the front end. Restrict authoring to
97 107 // users with `unfiltered_html` so per-plugin caps cannot grant script write
98 108 // access to lower roles (e.g. Editors via the role manager).
99 109 if ( ! current_user_can( 'unfiltered_html' ) ) {
110 + // Record it — skipping silently let the caller report a clean save.
111 + $skipped_fields[] = $key;
100 112 continue 2;
101 113 }
102 114 // Reason for ignoring phpcs rule: Here we are saving the custom JS/CSS script. Encoding it before saving to db. No escaping function working here.
103 115 // We first decode any existing HTML entities to prevent double-encoding on multiple saves, then encode once.
@@ -136,13 +148,19 @@
136 148
137 149 $i++;
138 150 }
139 151
140 - // Preserve original delete-on-empty semantics (line ~270): only override $meta_value when products were collected.
141 152 if ( ! empty( $checkout_products ) ) {
142 153 $meta_value = $checkout_products;
143 154 }
144 155 }
156 +
157 + if ( null === $meta_value ) {
158 + // Delete-on-empty is deliberate here: clearing every product removes the
159 + // meta. The repeater posts an empty scalar, not an array, once the last
160 + // product is removed, so this has to sit outside the is_array() guard.
161 + $meta_value = false;
162 + }
145 163 break;
146 164
147 165 case 'FILTER_CARTFLOWS_CHECKOUT_FIELDS':
148 166 $count = 10;
@@ -154,9 +172,11 @@
154 172 $post_data = $_POST[ $key ]; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
155 173
156 174 if ( 'wcf_field_order_billing' == $key || 'wcf_field_order_shipping' == $key ) {
157 175
158 - $type_of_fields = ltrim( $key, 'wcf_field_order_' );
176 + // ltrim() takes a character set, not a prefix — it only produced the
177 + // right answer here because 'b' and 's' happen to be absent from it.
178 + $type_of_fields = str_replace( 'wcf_field_order_', '', $key );
159 179 $billing_shipping_fields = \Cartflows_Helper::get_checkout_fields( $type_of_fields, $post_id );
160 180
161 181 foreach ( $post_data as $field_key_name => $value ) {
162 182
@@ -200,8 +220,16 @@
200 220 $meta_value = $ordered_fields;
201 221 }
202 222 }
203 223
224 + /*
225 + * Deliberately no `null === $meta_value` normalisation here. Unlike the
226 + * checkout products and FILTER_CARTFLOWS_ARRAY cases, no shipped field posts
227 + * a scalar or an unhandled key to this filter — the only keys that use it are
228 + * handled above, and the field-order editor always posts per-field arrays. If
229 + * that ever changes, leaving the saved ordering alone is the safe outcome;
230 + * deleting it would be the silent data loss this sentinel exists to prevent.
231 + */
204 232 break;
205 233
206 234 case 'FILTER_CARTFLOWS_OPTIN_FIELDS':
207 235 $count = 10;
@@ -256,8 +284,14 @@
256 284
257 285 $meta_value = $ordered_fields;
258 286 }
259 287 }
288 +
289 + /*
290 + * Deliberately no `null === $meta_value` normalisation here either — same
291 + * reasoning as FILTER_CARTFLOWS_CHECKOUT_FIELDS above. `wcf-optin-fields-billing`
292 + * is the only key that uses this filter and it is handled.
293 + */
260 294 break;
261 295
262 296 default:
263 297 if ( 'FILTER_DEFAULT' === $sanitize_filter ) {
@@ -262,9 +296,15 @@
262 296 default:
263 297 if ( 'FILTER_DEFAULT' === $sanitize_filter ) {
264 298 $meta_value = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : '';
265 299 } else {
266 - $meta_value = apply_filters( 'cartflows_admin_save_meta_field_values', $meta_value, $post_id, $key, $sanitize_filter, $action );
300 + /*
301 + * CartFlows Pro hooks this filter and several of its cases leave the
302 + * incoming value untouched when the field was not posted, relying on
303 + * it being false so the meta is deleted. Pass false rather than the
304 + * new null sentinel so that contract is unchanged.
305 + */
306 + $meta_value = apply_filters( 'cartflows_admin_save_meta_field_values', false, $post_id, $key, $sanitize_filter, $action );
267 307 }
268 308
269 309 break;
270 310 }
@@ -283,6 +323,8 @@
283 323 // To delete the wcf-checkout-products if empty.
284 324 delete_post_meta( $post_id, $key );
285 325 }
286 326 }
327 +
328 + return $skipped_fields;
287 329 }
288 330 }