PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.3
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.3
3.3.3 3.3.2 3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 158 releases
← All changes | includes/NotificationX.php +145 -0 3.3.1 → 3.3.3 View file →
@@ -305,9 +305,154 @@
305 305 $settings[$key] = isset($settings[$key]) ? $settings[$key] : 0;
306 306 $settings[$key] = is_numeric($settings[$key]) ? $settings[$key] + 0 : 0;
307 307 }
308 308 }
309 + return $this->normalize_field_types($settings, $fields);
310 + }
311 +
312 + /**
313 + * Coerce stored values to the shape their consumers expect, without filling
314 + * defaults. Runs on every read (normalize_post()) and before every save
315 + * (PostType::save_post()), so MCP/REST callers that send the wrong JSON type
316 + * can neither store it nor fatal the front end with it.
317 + *
318 + * @param array $settings Notification settings.
319 + * @param array|null $fields Field list from get_field_names(); fetched when null.
320 + * @return array
321 + */
322 + public function normalize_field_types($settings, $fields = null) {
323 + if (!is_array($settings)) {
324 + return $settings;
325 + }
326 + if (null === $fields) {
327 + $fields = $this->get_field_names();
328 + }
329 + foreach ($fields as $key => $value) {
330 + if (!array_key_exists($key, $settings)) {
331 + continue;
332 + }
333 + if (!empty($value['multiple'])) {
334 + $settings[$key] = $this->normalize_multiple_value($settings[$key]);
335 + } elseif (isset($value['type']) && in_array($value['type'], self::STRING_FIELD_TYPES, true)) {
336 + $settings[$key] = $this->normalize_string_value($settings[$key]);
337 + } elseif (isset($value['type']) && in_array($value['type'], self::ROWS_FIELD_TYPES, true)) {
338 + $settings[$key] = $this->normalize_rows_value($settings[$key]);
339 + }
340 + }
341 +
342 + // The field list above comes from a cached builder config, so a field
343 + // whose module was off when it was built is missing from it. These keys
344 + // fatal the front end when they have the wrong type; always coerce them.
345 + foreach (self::GUARDED_FIELDS as $key => $shape) {
346 + if (!array_key_exists($key, $settings)) {
347 + continue;
348 + }
349 + if ('string' === $shape) {
350 + $settings[$key] = $this->normalize_string_value($settings[$key]);
351 + } elseif ('map' === $shape) {
352 + $settings[$key] = is_array($settings[$key]) ? $settings[$key] : (is_object($settings[$key]) ? (array) $settings[$key] : []);
353 + } elseif ('rows' === $shape) {
354 + $settings[$key] = $this->normalize_rows_value($settings[$key]);
355 + }
356 + }
309 357 return $settings;
358 + }
359 +
360 + /**
361 + * Field types whose value every consumer treats as a string (explode(),
362 + * strtotime(), preg_match_all(), ...).
363 + */
364 + const STRING_FIELD_TYPES = ['text', 'textarea', 'date', 'timepicker', 'advanced-codeviewer'];
365 +
366 + /**
367 + * Field types whose value is a list of rows, each row an array.
368 + */
369 + const ROWS_FIELD_TYPES = ['advanced-repeater', 'repeater'];
370 +
371 + /**
372 + * Keys coerced even when they are missing from the cached field list.
373 + * `notification-template` is a `group` (param => tag map); `custom_contents`
374 + * is the Custom Notification repeater; `custom_ids` / `taxonomy_ids` are the
375 + * Pro "comma separated IDs" text fields that Locations explode().
376 + */
377 + const GUARDED_FIELDS = [
378 + 'notification-template' => 'map',
379 + 'custom_contents' => 'rows',
380 + 'custom_ids' => 'string',
381 + 'taxonomy_ids' => 'string',
382 + ];
383 +
384 + /**
385 + * MCP and REST saves store the JSON the caller sent, so a text field can
386 + * arrive as a list (e.g. custom_ids: [12, 34]). Join a list of scalars with
387 + * commas, the format these fields document; anything else becomes ''.
388 + *
389 + * @param mixed $value Stored value.
390 + * @return mixed The value unchanged when it is not an array/object.
391 + */
392 + public function normalize_string_value($value) {
393 + if (is_object($value)) {
394 + $value = (array) $value;
395 + }
396 + if (!is_array($value)) {
397 + return $value;
398 + }
399 + foreach ($value as $item) {
400 + if (!is_scalar($item)) {
401 + return '';
402 + }
403 + }
404 + return implode(',', $value);
405 + }
406 +
407 + /**
408 + * A repeater value must be a list of rows, each row an array. Drop anything
409 + * else, so row consumers (shuffle(), array_splice(), $row['key'] = ...)
410 + * never meet a string.
411 + *
412 + * @param mixed $value Stored value.
413 + * @return array
414 + */
415 + public function normalize_rows_value($value) {
416 + if (is_object($value)) {
417 + $value = (array) $value;
418 + }
419 + if (!is_array($value)) {
420 + return [];
421 + }
422 + $rows = [];
423 + foreach ($value as $key => $row) {
424 + if (is_object($row)) {
425 + $row = (array) $row;
426 + }
427 + if (is_array($row)) {
428 + $rows[$key] = $row;
429 + }
430 + }
431 + return $rows;
432 + }
433 +
434 + /**
435 + * Multi-select fields (`'multiple' => true`) mostly declare `'default' => ''`,
436 + * and MCP / Quick Builder creates store that '' as-is. Consumers pass these
437 + * values to array_diff() / in_array(), which throw a TypeError on PHP 8 for a
438 + * string. Always hand out an array: '' and null become [], a single scalar
439 + * becomes [ scalar ].
440 + *
441 + * @param mixed $value Stored value.
442 + * @return array
443 + */
444 + public function normalize_multiple_value($value) {
445 + if (is_array($value)) {
446 + return $value;
447 + }
448 + if (is_object($value)) {
449 + return (array) $value;
450 + }
451 + if (null === $value || '' === $value || false === $value) {
452 + return [];
453 + }
454 + return [$value];
310 455 }
311 456
312 457 public function get_tab(){
313 458 $tabs = get_transient('nx_builder_fields');