Issues (712)

classes/class-object-sync-sf-mapping.php (42 issues)

1
<?php
2
/**
3
 * Map objects and records between WordPress and Salesforce
4
 *
5
 * @class   Object_Sync_Sf_Mapping
6
 * @package Object_Sync_Salesforce
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Object_Sync_Sf_Mapping class.
13
 */
14
class Object_Sync_Sf_Mapping {
15
16
	/**
17
	 * Current version of the plugin
18
	 *
19
	 * @var string
20
	 */
21
	public $version;
22
23
	/**
24
	 * The main plugin file
25
	 *
26
	 * @var string
27
	 */
28
	public $file;
29
30
	/**
31
	 * Global object of `$wpdb`, the WordPress database
32
	 *
33
	 * @var object
34
	 */
35
	public $wpdb;
36
37
	/**
38
	 * The plugin's slug so we can include it when necessary
39
	 *
40
	 * @var string
41
	 */
42
	public $slug;
43
44
	/**
45
	 * The plugin's prefix when saving options to the database
46
	 *
47
	 * @var string
48
	 */
49
	public $option_prefix;
50
51
	/**
52
	 * Object_Sync_Sf_Logging class
53
	 *
54
	 * @var object
55
	 */
56
	public $logging;
57
58
	/**
59
	 * The database table for fieldmaps
60
	 *
61
	 * @var string
62
	 */
63
	public $fieldmap_table;
64
65
	/**
66
	 * The database table for object maps
67
	 *
68
	 * @var string
69
	 */
70
	public $object_map_table;
71
72
	/**
73
	 * Possible status values for fieldmaps
74
	 *
75
	 * @var array
76
	 */
77
	public $fieldmap_statuses;
78
79
	/**
80
	 * The active status values for fieldmaps
81
	 *
82
	 * @var string
83
	 */
84
	public $active_fieldmap_conditions;
85
86
	/**
87
	 * Bitmap value for when sync is off
88
	 *
89
	 * @var string
90
	 */
91
	public $sync_off;
92
93
	/**
94
	 * Bitmap value for when sync is is on for WordPress create events
95
	 *
96
	 * @var string
97
	 */
98
	public $sync_wordpress_create;
99
100
	/**
101
	 * Bitmap value for when sync is is on for WordPress update events
102
	 *
103
	 * @var string
104
	 */
105
	public $sync_wordpress_update;
106
107
	/**
108
	 * Bitmap value for when sync is is on for WordPress delete events
109
	 *
110
	 * @var string
111
	 */
112
	public $sync_wordpress_delete;
113
114
	/**
115
	 * Bitmap value for when sync is is on for Salesforce create events
116
	 *
117
	 * @var string
118
	 */
119
	public $sync_sf_create;
120
121
	/**
122
	 * Bitmap value for when sync is is on for Salesforce update events
123
	 *
124
	 * @var string
125
	 */
126
	public $sync_sf_update;
127
128
	/**
129
	 * Bitmap value for when sync is is on for Salesforce delete events
130
	 *
131
	 * @var string
132
	 */
133
	public $sync_sf_delete;
134
135
	/**
136
	 * Which events are run by WordPress
137
	 *
138
	 * @var array
139
	 */
140
	public $wordpress_events;
141
142
	/**
143
	 * Which events are run by Salesforce
144
	 *
145
	 * @var array
146
	 */
147
	public $salesforce_events;
148
149
	/**
150
	 * The direction from WordPress to Salesforce
151
	 *
152
	 * @var string
153
	 */
154
	public $direction_wordpress_sf;
155
156
	/**
157
	 * The direction from Salesforce to WordPress
158
	 *
159
	 * @var string
160
	 */
161
	public $direction_sf_wordpress;
162
163
	/**
164
	 * The direction to sync both ways
165
	 *
166
	 * @var string
167
	 */
168
	public $direction_sync;
169
170
	/**
171
	 * WordPress directions, including sync
172
	 *
173
	 * @var array
174
	 */
175
	public $direction_wordpress;
176
177
	/**
178
	 * Salesforce directions, including sync
179
	 *
180
	 * @var array
181
	 */
182
	public $direction_salesforce;
183
184
	/**
185
	 * Default record type when using a Salesforce object that has a default or Master record type
186
	 *
187
	 * @var string
188
	 */
189
	public $salesforce_default_record_type;
190
191
	/**
192
	 * Delimiter for arrays coming from Salesforce
193
	 *
194
	 * @var string
195
	 */
196
	public $array_delimiter;
197
198
	/**
199
	 * Data in Salesforce that is stored as an array
200
	 *
201
	 * @var array
202
	 */
203
	public $array_types_from_salesforce;
204
205
	/**
206
	 * Data in Salesforce that is stored as a date
207
	 *
208
	 * @var array
209
	 */
210
	public $date_types_from_salesforce;
211
212
	/**
213
	 * Data in Salesforce that is stored as an integer
214
	 *
215
	 * @var array
216
	 */
217
	public $int_types_from_salesforce;
218
219
	/**
220
	 * How long can a mapping field be
221
	 *
222
	 * @var int
223
	 */
224
	public $name_length;
225
226
	/**
227
	 * Status flag for success
228
	 *
229
	 * @var int
230
	 */
231
	public $status_success;
232
233
	/**
234
	 * Status flag for error
235
	 *
236
	 * @var int
237
	 */
238
	public $status_error;
239
240
	/**
241
	 * Option value for whether the plugin is in debug mode
242
	 *
243
	 * @var bool
244
	 */
245
	public $debug;
246
247
	/**
248
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
249
	 *
250
	 * @var int
251
	 */
252
	public $sync_off_v1;
253
254
	/**
255
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
256
	 *
257
	 * @var int
258
	 */
259
	public $sync_wordpress_create_v1;
260
261
	/**
262
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
263
	 *
264
	 * @var int
265
	 */
266
	public $sync_wordpress_update_v1;
267
268
	/**
269
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
270
	 *
271
	 * @var int
272
	 */
273
	public $sync_wordpress_delete_v1;
274
275
	/**
276
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
277
	 *
278
	 * @var int
279
	 */
280
	public $sync_sf_create_v1;
281
282
	/**
283
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
284
	 *
285
	 * @var int
286
	 */
287
	public $sync_sf_update_v1;
288
289
	/**
290
	 * Deprecated hex parameter which defines when syncing should occur on each field map.
291
	 *
292
	 * @var int
293
	 */
294
	public $sync_sf_delete_v1;
295
296
	/**
297
	 * Deprecated hex event values.
298
	 *
299
	 * @var array
300
	 */
301
	public $wordpress_events_v1;
302
303
	/**
304
	 * Deprecated hex event values.
305
	 *
306
	 * @var array
307
	 */
308
	public $salesforce_events_v1;
309
310
	/**
311
	 * Constructor for mapping class
312
	 */
313
	public function __construct() {
314
		$this->version       = object_sync_for_salesforce()->version;
315
		$this->file          = object_sync_for_salesforce()->file;
316
		$this->wpdb          = object_sync_for_salesforce()->wpdb;
317
		$this->slug          = object_sync_for_salesforce()->slug;
318
		$this->option_prefix = object_sync_for_salesforce()->option_prefix;
319
320
		$this->logging = object_sync_for_salesforce()->logging;
321
322
		$this->fieldmap_table   = $this->wpdb->prefix . 'object_sync_sf_field_map';
323
		$this->object_map_table = $this->wpdb->prefix . 'object_sync_sf_object_map';
324
325
		$this->fieldmap_statuses          = array(
326
			'active'   => 'Active',
327
			'inactive' => 'Inactive',
328
			'any'      => '',
329
		);
330
		$this->active_fieldmap_conditions = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('fieldmap_status' => 'active') of type array<string,string> is incompatible with the declared type string of property $active_fieldmap_conditions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
331
			'fieldmap_status' => 'active',
332
		);
333
334
		/*
335
		 * These parameters are how we define when syncing should occur on each field map.
336
		 * They get used in the admin settings, as well as the push/pull methods to see if something should happen.
337
		*/
338
		$this->sync_off              = 'off';
339
		$this->sync_wordpress_create = 'wp_create';
340
		$this->sync_wordpress_update = 'wp_update';
341
		$this->sync_wordpress_delete = 'wp_delete';
342
		$this->sync_sf_create        = 'sf_create';
343
		$this->sync_sf_update        = 'sf_update';
344
		$this->sync_sf_delete        = 'sf_delete';
345
346
		// deprecated bit flags from version 1.x. Deprecated since 2.0.0.
347
		$this->sync_off_v1              = 0x0000;
348
		$this->sync_wordpress_create_v1 = 0x0001;
349
		$this->sync_wordpress_update_v1 = 0x0002;
350
		$this->sync_wordpress_delete_v1 = 0x0004;
351
		$this->sync_sf_create_v1        = 0x0008;
352
		$this->sync_sf_update_v1        = 0x0010;
353
		$this->sync_sf_delete_v1        = 0x0020;
354
355
		// Define which events are initialized by which system.
356
		$this->wordpress_events  = array( $this->sync_wordpress_create, $this->sync_wordpress_update, $this->sync_wordpress_delete );
357
		$this->salesforce_events = array( $this->sync_sf_create, $this->sync_sf_update, $this->sync_sf_delete );
358
359
		// deprecated bit flags from version 1.x. Deprecated since 2.0.0.
360
		$this->wordpress_events_v1  = array( $this->sync_wordpress_create_v1, $this->sync_wordpress_update_v1, $this->sync_wordpress_delete_v1 );
361
		$this->salesforce_events_v1 = array( $this->sync_sf_create_v1, $this->sync_sf_update_v1, $this->sync_sf_delete_v1 );
362
363
		// Constants for the directions to map things.
364
		$this->direction_wordpress_sf = 'wp_sf';
365
		$this->direction_sf_wordpress = 'sf_wp';
366
		$this->direction_sync         = 'sync';
367
368
		$this->direction_wordpress  = array( $this->direction_wordpress_sf, $this->direction_sync );
369
		$this->direction_salesforce = array( $this->direction_sf_wordpress, $this->direction_sync );
370
371
		// This is used when we map a record with default or Master.
372
		$this->salesforce_default_record_type = 'default';
373
374
		// Salesforce has multipicklists and they have a delimiter.
375
		$this->array_delimiter = ';';
376
		// What data types in Salesforce should be an array?
377
		$this->array_types_from_salesforce = array( 'multipicklist' );
378
		// What data types in Salesforce should be a date field?
379
		$this->date_types_from_salesforce = array( 'date', 'datetime' );
380
		// What data types in Salesforce should be an integer?
381
		$this->int_types_from_salesforce = array( 'integer', 'boolean' );
382
383
		// Max length for a mapping field.
384
		$this->name_length = 128;
385
386
		// Statuses for object sync.
387
		$this->status_success = 1;
388
		$this->status_error   = 0;
389
390
		// use the option value for whether we're in debug mode.
391
		$this->debug = filter_var( get_option( $this->option_prefix . 'debug_mode', false ), FILTER_VALIDATE_BOOLEAN );
0 ignored issues
show
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

391
		$this->debug = filter_var( /** @scrutinizer ignore-call */ get_option( $this->option_prefix . 'debug_mode', false ), FILTER_VALIDATE_BOOLEAN );
Loading history...
392
393
		// action hooks.
394
		$this->add_actions();
395
	}
396
397
	/**
398
	 * Run init actions.
399
	 */
400
	public function add_actions() {
401
		// public actions.
402
		add_action( 'init', array( $this, 'init' ) );
0 ignored issues
show
The function add_action was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

402
		/** @scrutinizer ignore-call */ 
403
  add_action( 'init', array( $this, 'init' ) );
Loading history...
403
	}
404
405
	/**
406
	 * Initialize. things that have to run after init.
407
	 */
408
	public function init() {
409
		$this->fieldmap_statuses = array(
410
			'active'   => esc_html__( 'Active', 'object-sync-for-salesforce' ),
0 ignored issues
show
The function esc_html__ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

410
			'active'   => /** @scrutinizer ignore-call */ esc_html__( 'Active', 'object-sync-for-salesforce' ),
Loading history...
411
			'inactive' => esc_html__( 'Inactive', 'object-sync-for-salesforce' ),
412
			'any'      => '',
413
		);
414
	}
415
416
	/**
417
	 * Create a fieldmap row between a WordPress and Salesforce object
418
	 *
419
	 * @param array $posted The results of $_POST.
420
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
421
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
422
	 * @return int  the last inserted ID.
423
	 */
424
	public function create_fieldmap( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array() ) {
425
		$data = $this->setup_fieldmap_data( $posted, $wordpress_fields, $salesforce_fields );
426
		if ( ! isset( $posted['version'] ) && version_compare( $this->version, '1.2.5', '>=' ) ) {
427
			$data['version'] = $this->version;
428
		}
429
		$insert = $this->wpdb->insert( $this->fieldmap_table, $data );
430
		if ( 1 === $insert ) {
431
			return $this->wpdb->insert_id;
432
		} else {
433
			return false;
434
		}
435
	}
436
437
	/**
438
	 * Get one or more fieldmap rows between a WordPress and Salesforce object
439
	 *
440
	 * @param int   $id The ID of a desired mapping.
441
	 * @param array $conditions Array of key=>value to match the mapping by.
442
	 * @param bool  $reset Unused parameter.
443
	 * @return array $map a single mapping or $mappings, an array of mappings.
444
	 */
445
	public function get_fieldmaps( $id = null, $conditions = array(), $reset = false ) {
0 ignored issues
show
The parameter $reset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

445
	public function get_fieldmaps( $id = null, $conditions = array(), /** @scrutinizer ignore-unused */ $reset = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
446
		$table = $this->fieldmap_table;
447
		if ( null !== $id ) { // get one fieldmap.
448
			$map        = $this->wpdb->get_row( 'SELECT * FROM ' . $table . ' WHERE id = ' . $id, ARRAY_A );
0 ignored issues
show
The constant ARRAY_A was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
449
			$mappings   = array();
450
			$mappings[] = $map;
451
			$mappings   = $this->prepare_fieldmap_data( $mappings );
452
			$map        = $mappings[0];
453
			return $map;
454
		} elseif ( ! empty( $conditions ) ) { // get multiple but with a limitation.
455
			$mappings    = array();
456
			$record_type = '';
457
458
			// Assemble the SQL.
459
			if ( ! empty( $conditions ) ) {
460
				$where = '';
461
				$i     = 0;
462
				foreach ( $conditions as $key => $value ) {
463
					// if 'any' is the value for fieldmap status, we keep it off the WHERE statement.
464
					if ( 'fieldmap_status' === $key && 'any' === $value ) {
465
						continue;
466
					}
467
					if ( 'salesforce_record_type' === $key ) {
468
						$record_type = sanitize_text_field( $value );
0 ignored issues
show
The function sanitize_text_field was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

468
						$record_type = /** @scrutinizer ignore-call */ sanitize_text_field( $value );
Loading history...
469
					} else {
470
						$i++;
471
						if ( $i > 1 ) {
472
							$where .= ' AND ';
473
						}
474
						$where .= '`' . $key . '` = "' . $value . '"';
475
					}
476
				}
477
				if ( '' !== $where ) {
0 ignored issues
show
The condition '' !== $where is always false.
Loading history...
478
					$where = ' WHERE ' . $where;
479
				}
480
			} else {
481
				$where = '';
482
			}
483
484
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . ' ORDER BY `fieldmap_status`, `weight`', ARRAY_A );
485
486
			if ( ! empty( $mappings ) ) {
487
				$mappings = $this->prepare_fieldmap_data( $mappings, $record_type );
488
			}
489
490
			return $mappings;
491
492
		} else { // get all of the mappings. ALL THE MAPPINGS.
493
494
			// This is the default query for loading fieldmaps.
495
			$mappings = $this->wpdb->get_results( "SELECT * FROM $table ORDER BY `fieldmap_status`", ARRAY_A );
496
497
			// lower than 2.0.0 versions.
498
			// @deprecated
499
			// will be removed in version 3.0.0.
500
			if ( version_compare( $this->version, '2.0.0', '<' ) ) {
501
				// if the version is greater than or equal to 1.5.0, the fieldmap table has a pull_to_drafts column.
502
				if ( version_compare( $this->version, '1.5.0', '>=' ) ) {
503
					$mappings = $this->wpdb->get_results( "SELECT `id`, `label`, `wordpress_object`, `salesforce_object`, `salesforce_record_types_allowed`, `salesforce_record_type_default`, `fields`, `pull_trigger_field`, `sync_triggers`, `push_async`, `push_drafts`, `pull_to_drafts`, `weight`, `version` FROM $table", ARRAY_A );
504
				} elseif ( version_compare( $this->version, '1.2.5', '>=' ) ) {
505
					// if the version is greater than or equal to 1.2.5, the fieldmap table has a version column.
506
					$mappings = $this->wpdb->get_results( "SELECT `id`, `label`, `wordpress_object`, `salesforce_object`, `salesforce_record_types_allowed`, `salesforce_record_type_default`, `fields`, `pull_trigger_field`, `sync_triggers`, `push_async`, `push_drafts`, `weight`, `version` FROM $table", ARRAY_A );
507
				} else {
508
					$mappings = $this->wpdb->get_results( "SELECT `id`, `label`, `wordpress_object`, `salesforce_object`, `salesforce_record_types_allowed`, `salesforce_record_type_default`, `fields`, `pull_trigger_field`, `sync_triggers`, `push_async`, `push_drafts`, `weight` FROM $table", ARRAY_A );
509
				}
510
			}
511
512
			if ( ! empty( $mappings ) ) {
513
				$mappings = $this->prepare_fieldmap_data( $mappings );
514
			}
515
516
			return $mappings;
517
		} // End if statement.
518
	}
519
520
	/**
521
	 * For a mapping, get the fieldmaps associated with it.
522
	 *
523
	 * @param array $mapping The mapping for which we are getting the fieldmaps.
524
	 * @param array $directions The direction of the mapping: from WP to SF or vice-versa.
525
	 * @see Object_Sync_Sf_Salesforce_Pull::get_pull_query()
526
	 * @return array of mapped fields
527
	 */
528
	public function get_mapped_fields( $mapping, $directions = array() ) {
529
		$mapped_fields = array();
530
		foreach ( $mapping['fields'] as $fields ) {
531
			if ( empty( $directions ) || in_array( $fields['direction'], $directions, true ) ) {
532
533
				// in version 1.2.0, we provided an option for API name vs label for Salesforce fields.
534
				if ( version_compare( $this->version, '1.2.0', '>=' ) && isset( $fields['salesforce_field']['name'] ) ) {
535
					$array_key = 'name';
536
				} else {
537
					$array_key = 'label';
538
				}
539
540
				// Some field map types (Relation) store a collection of SF objects.
541
				if ( is_array( $fields['salesforce_field'] ) && ! isset( $fields['salesforce_field'][ $array_key ] ) ) {
542
					foreach ( $fields['salesforce_field'] as $sf_field ) {
543
						$mapped_fields[ $sf_field[ $array_key ] ] = $sf_field[ $array_key ];
544
					}
545
				} else { // The rest are just a name/value pair.
546
					$mapped_fields[ $fields['salesforce_field'][ $array_key ] ] = $fields['salesforce_field'][ $array_key ];
547
				}
548
			}
549
		}
550
551
		if ( ! empty( $this->get_mapped_record_types ) ) {
552
			$mapped_fields['RecordTypeId'] = 'RecordTypeId';
553
		}
554
555
		return $mapped_fields;
556
	}
557
558
	/**
559
	 * Get the mapped record types for a given mapping.
560
	 *
561
	 * @param array $mapping A mapping from which we wish to estract the record type.
562
	 * @return array of mappings. Empty if the mapping's record type is default, else full of the record types.
563
	 */
564
	public function get_mapped_record_types( $mapping ) {
565
		return $mapping['salesforce_record_type_default'] === $this->salesforce_default_record_type ? array() : array_filter( maybe_unserialize( $mapping['salesforce_record_types_allowed'] ) );
0 ignored issues
show
The function maybe_unserialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

565
		return $mapping['salesforce_record_type_default'] === $this->salesforce_default_record_type ? array() : array_filter( /** @scrutinizer ignore-call */ maybe_unserialize( $mapping['salesforce_record_types_allowed'] ) );
Loading history...
566
	}
567
568
	/**
569
	 * Update a fieldmap row between a WordPress and Salesforce object
570
	 *
571
	 * @param array $posted It's $_POST.
572
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
573
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
574
	 * @param int   $id The ID of the mapping.
575
	 * @return boolean whether it was updated
576
	 */
577
	public function update_fieldmap( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array(), $id = '' ) {
578
		$data = $this->setup_fieldmap_data( $posted, $wordpress_fields, $salesforce_fields );
579
		if ( version_compare( $this->version, '1.2.5', '>=' ) && ! isset( $data['updated'] ) ) {
580
			$data['version'] = $this->version;
581
		}
582
		$update = $this->wpdb->update(
583
			$this->fieldmap_table,
584
			$data,
585
			array(
586
				'id' => $id,
587
			)
588
		);
589
		if ( false === $update ) {
590
			return false;
591
		} else {
592
			return true;
593
		}
594
	}
595
596
	/**
597
	 * Setup fieldmap data
598
	 * Sets up the database entry for mapping the object types between Salesforce and WordPress
599
	 *
600
	 * @param array $posted It's $_POST.
601
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
602
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
603
	 * @return array $data the fieldmap's data for the database
604
	 */
605
	private function setup_fieldmap_data( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array() ) {
606
		$data = array(
607
			'label'             => $posted['label'],
608
			'name'              => sanitize_title( $posted['label'] ),
0 ignored issues
show
The function sanitize_title was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

608
			'name'              => /** @scrutinizer ignore-call */ sanitize_title( $posted['label'] ),
Loading history...
609
			'salesforce_object' => $posted['salesforce_object'],
610
			'wordpress_object'  => $posted['wordpress_object'],
611
		);
612
		// when importing a fieldmap, there might already be a saved version. if there is, keep it.
613
		$data['version'] = isset( $posted['version'] ) ? $posted['version'] : $this->version;
614
		if ( isset( $posted['wordpress_field'] ) && is_array( $posted['wordpress_field'] ) && isset( $posted['salesforce_field'] ) && is_array( $posted['salesforce_field'] ) ) {
615
			$setup['fields'] = array();
616
			foreach ( $posted['wordpress_field'] as $key => $value ) {
617
				$method_key = array_search( $value, array_column( $wordpress_fields, 'key' ), true );
618
				if ( ! isset( $posted['direction'][ $key ] ) ) {
619
					$posted['direction'][ $key ] = 'sync';
620
				}
621
				if ( ! isset( $posted['is_prematch'][ $key ] ) ) {
622
					$posted['is_prematch'][ $key ] = false;
623
				}
624
				if ( ! isset( $posted['is_key'][ $key ] ) ) {
625
					$posted['is_key'][ $key ] = false;
626
				}
627
				if ( ! isset( $posted['is_delete'][ $key ] ) ) {
628
					$posted['is_delete'][ $key ] = false;
629
				}
630
				if ( false === $posted['is_delete'][ $key ] ) {
631
					// I think it's good to over-mention that updateable is really how the Salesforce api spells it.
632
					$updateable_key = array_search( $posted['salesforce_field'][ $key ], array_column( $salesforce_fields, 'name' ), true );
633
634
					$salesforce_field_attributes = array();
635
					foreach ( $salesforce_fields[ $updateable_key ] as $sf_key => $sf_value ) {
636
						if ( isset( $sf_value ) && ! is_array( $sf_value ) ) {
637
							$salesforce_field_attributes[ $sf_key ] = esc_attr( $sf_value );
0 ignored issues
show
The function esc_attr was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

637
							$salesforce_field_attributes[ $sf_key ] = /** @scrutinizer ignore-call */ esc_attr( $sf_value );
Loading history...
638
						} elseif ( ! empty( $sf_value ) && is_array( $sf_value ) ) {
639
							$salesforce_field_attributes[ $sf_key ] = maybe_unserialize( $sf_value );
0 ignored issues
show
The function maybe_unserialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

639
							$salesforce_field_attributes[ $sf_key ] = /** @scrutinizer ignore-call */ maybe_unserialize( $sf_value );
Loading history...
640
						} else {
641
							$salesforce_field_attributes[ $sf_key ] = '';
642
						}
643
					}
644
645
					$setup['fields'][ $key ] = array(
646
						'wordpress_field'  => array(
647
							'label'    => sanitize_text_field( $posted['wordpress_field'][ $key ] ),
0 ignored issues
show
The function sanitize_text_field was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

647
							'label'    => /** @scrutinizer ignore-call */ sanitize_text_field( $posted['wordpress_field'][ $key ] ),
Loading history...
648
							'methods'  => maybe_unserialize( $wordpress_fields[ $method_key ]['methods'] ),
649
							'type'     => isset( $wordpress_fields[ $method_key ]['type'] ) ? sanitize_text_field( $wordpress_fields[ $method_key ]['type'] ) : 'text',
650
							'editable' => isset( $wordpress_fields[ $method_key ]['editable'] ) ? (bool) $wordpress_fields[ $method_key ]['editable'] : true,
651
						),
652
						'salesforce_field' => $salesforce_field_attributes,
653
						'is_prematch'      => sanitize_text_field( $posted['is_prematch'][ $key ] ),
654
						'is_key'           => sanitize_text_field( $posted['is_key'][ $key ] ),
655
						'direction'        => sanitize_text_field( $posted['direction'][ $key ] ),
656
						'is_delete'        => sanitize_text_field( $posted['is_delete'][ $key ] ),
657
					);
658
659
					// If the WordPress key or the Salesforce key are blank, remove this incomplete mapping.
660
					// This prevents https://github.com/MinnPost/object-sync-for-salesforce/issues/82 .
661
					if (
662
						empty( $setup['fields'][ $key ]['wordpress_field']['label'] )
663
						||
664
						empty( $setup['fields'][ $key ]['salesforce_field']['name'] )
665
					) {
666
						unset( $setup['fields'][ $key ] );
667
					}
668
				}
669
			} // End foreach() on WordPress fields.
670
			$data['fields'] = maybe_serialize( $setup['fields'] );
0 ignored issues
show
The function maybe_serialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

670
			$data['fields'] = /** @scrutinizer ignore-call */ maybe_serialize( $setup['fields'] );
Loading history...
671
		} elseif ( isset( $posted['fields'] ) && is_array( $posted['fields'] ) ) {
672
			// if $posted['fields'] is already set, use that.
673
			$data['fields'] = maybe_serialize( $posted['fields'] );
674
		} // End if() WordPress fields are present.
675
676
		if ( isset( $posted['salesforce_record_types_allowed'] ) ) {
677
			$data['salesforce_record_types_allowed'] = maybe_serialize( $posted['salesforce_record_types_allowed'] );
678
		} else {
679
			$data['salesforce_record_types_allowed'] = maybe_serialize(
680
				array(
681
					$this->salesforce_default_record_type => $this->salesforce_default_record_type,
682
				)
683
			);
684
		}
685
		if ( isset( $posted['salesforce_record_type_default'] ) ) {
686
			$data['salesforce_record_type_default'] = $posted['salesforce_record_type_default'];
687
		} else {
688
			$data['salesforce_record_type_default'] = maybe_serialize( $this->salesforce_default_record_type );
689
		}
690
		if ( isset( $posted['pull_trigger_field'] ) ) {
691
			$data['pull_trigger_field'] = $posted['pull_trigger_field'];
692
		}
693
		if ( isset( $posted['sync_triggers'] ) && is_array( $posted['sync_triggers'] ) ) {
694
			$setup['sync_triggers'] = array();
695
			foreach ( $posted['sync_triggers'] as $key => $value ) {
696
				$setup['sync_triggers'][ $key ] = esc_html( $posted['sync_triggers'][ $key ] );
0 ignored issues
show
The function esc_html was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

696
				$setup['sync_triggers'][ $key ] = /** @scrutinizer ignore-call */ esc_html( $posted['sync_triggers'][ $key ] );
Loading history...
697
			}
698
			// format the sync triggers. if necessary, update the database.
699
			$sync_triggers          = $this->maybe_upgrade_sync_triggers( $setup['sync_triggers'], $data['version'] );
700
			$setup['sync_triggers'] = $sync_triggers;
701
		} else {
702
			$setup['sync_triggers'] = array();
703
		}
704
		$data['sync_triggers'] = maybe_serialize( $setup['sync_triggers'] );
705
		if ( isset( $posted['pull_trigger_field'] ) ) {
706
			$data['pull_trigger_field'] = $posted['pull_trigger_field'];
707
		}
708
		$data['fieldmap_status']                     = isset( $posted['fieldmap_status'] ) ? $posted['fieldmap_status'] : 'active';
709
		$data['push_async']                          = isset( $posted['push_async'] ) ? $posted['push_async'] : '';
710
		$data['push_drafts']                         = isset( $posted['push_drafts'] ) ? $posted['push_drafts'] : '';
711
		$data['pull_to_drafts']                      = isset( $posted['pull_to_drafts'] ) ? $posted['pull_to_drafts'] : '';
712
		$data['weight']                              = isset( $posted['weight'] ) ? $posted['weight'] : '';
713
		$data['always_delete_object_maps_on_delete'] = isset( $posted['always_delete_object_maps_on_delete'] ) ? $posted['always_delete_object_maps_on_delete'] : '0';
714
		return $data;
715
	}
716
717
	/**
718
	 * Delete a fieldmap row between a WordPress and Salesforce object
719
	 *
720
	 * @param int $id The ID of a field mapping.
721
	 * @return bool whether it was deleted
722
	 */
723
	public function delete_fieldmap( $id = '' ) {
724
		$data   = array(
725
			'id' => $id,
726
		);
727
		$delete = $this->wpdb->delete( $this->fieldmap_table, $data );
728
		if ( 1 === $delete ) {
729
			return true;
730
		} else {
731
			return false;
732
		}
733
	}
734
735
	/**
736
	 * Create an object map row between a WordPress and Salesforce object
737
	 *
738
	 * @param array $posted It's $_POST.
739
	 * @return false|int of field mapping between WordPress and Salesforce objects
740
	 */
741
	public function create_object_map( $posted = array() ) {
742
		$data            = $this->setup_object_map_data( $posted );
743
		$data['created'] = current_time( 'mysql' );
0 ignored issues
show
The function current_time was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

743
		$data['created'] = /** @scrutinizer ignore-call */ current_time( 'mysql' );
Loading history...
744
		// Check to see if we don't know the salesforce id and it is not a temporary id, or if this is pending.
745
		// If it is using a temporary id, the map will get updated after it finishes running; it won't call this method unless there's an error, which we should log.
746
		if ( substr( $data['salesforce_id'], 0, 7 ) !== 'tmp_sf_' || ( isset( $data['action'] ) && 'pending' === $data['action'] ) ) {
747
			unset( $data['action'] );
748
			$insert = $this->wpdb->insert( $this->object_map_table, $data );
749
		} else {
750
			$status = 'error';
751
			$this->logging->setup(
752
				sprintf(
753
					// translators: %1$s is the log status, %2$s is the name of a WordPress object. %3$s is the id of that object.
754
					esc_html__( '%1$s Mapping: error caused by trying to map the WordPress %2$s with ID of %3$s to Salesforce ID starting with "tmp_sf_", which is invalid.', 'object-sync-for-salesforce' ),
0 ignored issues
show
The function esc_html__ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

754
					/** @scrutinizer ignore-call */ 
755
     esc_html__( '%1$s Mapping: error caused by trying to map the WordPress %2$s with ID of %3$s to Salesforce ID starting with "tmp_sf_", which is invalid.', 'object-sync-for-salesforce' ),
Loading history...
755
					ucfirst( esc_attr( $status ) ),
0 ignored issues
show
The function esc_attr was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

755
					ucfirst( /** @scrutinizer ignore-call */ esc_attr( $status ) ),
Loading history...
756
					esc_attr( $data['wordpress_object'] ),
757
					absint( $data['wordpress_id'] )
0 ignored issues
show
The function absint was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

757
					/** @scrutinizer ignore-call */ 
758
     absint( $data['wordpress_id'] )
Loading history...
758
				),
759
				'',
760
				0,
761
				0,
762
				$status
763
			);
764
			return false;
765
		}
766
		if ( 1 === $insert ) {
767
			return $this->wpdb->insert_id;
768
		} elseif ( false !== strpos( $this->wpdb->last_error, 'Duplicate entry' ) ) {
769
			// this error should never happen now, I think. But let's watch and see.
770
			$mapping = $this->load_object_maps_by_salesforce_id( $data['salesforce_id'] )[0];
771
			$id      = $mapping['id'];
772
			$status  = 'error';
773
			$this->logging->setup(
774
				sprintf(
775
					// translators: %1$s is the status word "Error". %2$s is the Id of a Salesforce object. %3$s is the ID of a mapping object.
776
					esc_html__( '%1$s: Mapping: there is already a WordPress object mapped to the Salesforce object %2$s and the mapping object ID is %3$s', 'object-sync-for-salesforce' ),
777
					ucfirst( esc_attr( $status ) ),
778
					esc_attr( $data['salesforce_id'] ),
779
					absint( $id )
780
				),
781
				print_r( $mapping, true ), // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
782
				0,
783
				0,
784
				$status
785
			);
786
			return $id;
787
		} else {
788
			return false;
789
		}
790
	}
791
792
	/**
793
	 * Get all object map rows between WordPress and Salesforce objects.
794
	 *
795
	 * This replaces previous functionality that would return a single object map if there was only one, rather than a multi-dimensional array.
796
	 *
797
	 * @param array $conditions Limitations on the SQL query for object mapping rows.
798
	 * @param bool  $reset Unused parameter.
799
	 * @return $mappings
0 ignored issues
show
Documentation Bug introduced by
The doc comment $mappings at position 0 could not be parsed: Unknown type name '$mappings' at position 0 in $mappings.
Loading history...
800
	 */
801
	public function get_all_object_maps( $conditions = array(), $reset = false ) {
0 ignored issues
show
The parameter $reset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

801
	public function get_all_object_maps( $conditions = array(), /** @scrutinizer ignore-unused */ $reset = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
802
		$table = $this->object_map_table;
803
		$order = ' ORDER BY object_updated, created';
804
		if ( ! empty( $conditions ) ) { // get multiple but with a limitation.
805
			$mappings = array();
806
807
			if ( ! empty( $conditions ) ) {
808
				$where = ' WHERE ';
809
				$i     = 0;
810
				foreach ( $conditions as $key => $value ) {
811
					$i++;
812
					if ( $i > 1 ) {
813
						$where .= ' AND ';
814
					}
815
					$where .= '`' . $key . '` = "' . $value . '"';
816
				}
817
			} else {
818
				$where = '';
819
			}
820
821
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . $order, ARRAY_A );
0 ignored issues
show
The constant ARRAY_A was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
822
		} else { // get all of the mappings. ALL THE MAPPINGS.
823
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $order, ARRAY_A );
824
		}
825
826
		return $mappings;
827
828
	}
829
830
	/**
831
	 * Get one or more object map rows between WordPress and Salesforce objects
832
	 *
833
	 * @param array $conditions Limitations on the SQL query for object mapping rows.
834
	 * @param bool  $reset Unused parameter.
835
	 * @return array $map or $mappings
836
	 * @deprecated since 1.8.0
837
	 */
838
	public function get_object_maps( $conditions = array(), $reset = false ) {
0 ignored issues
show
The parameter $reset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

838
	public function get_object_maps( $conditions = array(), /** @scrutinizer ignore-unused */ $reset = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
839
		$table = $this->object_map_table;
840
		$order = ' ORDER BY object_updated, created';
841
		if ( ! empty( $conditions ) ) { // get multiple but with a limitation.
842
			$mappings = array();
843
844
			if ( ! empty( $conditions ) ) {
845
				$where = ' WHERE ';
846
				$i     = 0;
847
				foreach ( $conditions as $key => $value ) {
848
					$i++;
849
					if ( $i > 1 ) {
850
						$where .= ' AND ';
851
					}
852
					$where .= '`' . $key . '` = "' . $value . '"';
853
				}
854
			} else {
855
				$where = '';
856
			}
857
858
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . $order, ARRAY_A );
0 ignored issues
show
The constant ARRAY_A was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
859
			if ( ! empty( $mappings ) && 1 === $this->wpdb->num_rows ) {
860
				$mappings = $mappings[0];
861
			}
862
		} else { // get all of the mappings. ALL THE MAPPINGS.
863
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $order, ARRAY_A );
864
			if ( ! empty( $mappings ) && 1 === $this->wpdb->num_rows ) {
865
				$mappings = $mappings[0];
866
			}
867
		}
868
869
		return $mappings;
870
871
	}
872
873
	/**
874
	 * Update an object map row between a WordPress and Salesforce object
875
	 *
876
	 * @param array $posted It's $_POST.
877
	 * @param array $id The ID of the object map row.
878
	 * @return boolean whether it was updated
879
	 */
880
	public function update_object_map( $posted = array(), $id = '' ) {
881
		$data = $this->setup_object_map_data( $posted );
882
		if ( ! isset( $data['object_updated'] ) ) {
883
			$data['object_updated'] = current_time( 'mysql' );
0 ignored issues
show
The function current_time was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

883
			$data['object_updated'] = /** @scrutinizer ignore-call */ current_time( 'mysql' );
Loading history...
884
		}
885
		if ( isset( $data['action'] ) ) {
886
			unset( $data['action'] );
887
		}
888
		$update = $this->wpdb->update(
889
			$this->object_map_table,
890
			$data,
891
			array(
892
				'id' => $id,
893
			)
894
		);
895
		if ( false === $update ) {
896
			return false;
897
		} else {
898
			return true;
899
		}
900
	}
901
902
	/**
903
	 * Setup the data for the object map
904
	 *
905
	 * @param array $posted It's $_POST.
906
	 * @return array $data Filtered array with only the keys that are in the object map database table. Strips out things from WordPress form if they're present.
907
	 */
908
	private function setup_object_map_data( $posted = array() ) {
909
		$allowed_fields   = $this->wpdb->get_col( "DESC {$this->object_map_table}", 0 );
910
		$allowed_fields[] = 'action'; // we use this in both directions even though it isn't in the database; we remove it from the array later if it is present.
911
912
		$data = array_intersect_key( $posted, array_flip( $allowed_fields ) );
913
		return $data;
914
	}
915
916
	/**
917
	 * Delete an object map row between a WordPress and Salesforce object
918
	 *
919
	 * @param int|array $id The ID or IDs of the object map row(s).
920
	 * @return bool whether it was deleted
921
	 */
922
	public function delete_object_map( $id = '' ) {
923
		if ( is_string( $id ) || is_int( $id ) ) {
924
			$data   = array(
925
				'id' => $id,
926
			);
927
			$delete = $this->wpdb->delete( $this->object_map_table, $data );
928
			if ( 1 === $delete ) {
929
				return true;
930
			} else {
931
				return false;
932
			}
933
		} elseif ( is_array( $id ) ) {
0 ignored issues
show
The condition is_array($id) is always true.
Loading history...
934
			$ids    = implode( ',', array_map( 'absint', $id ) );
935
			$delete = $this->wpdb->query( "DELETE FROM $this->object_map_table WHERE ID IN ($ids)" );
936
			if ( false !== $delete ) {
937
				return true;
938
			} else {
939
				return false;
940
			}
941
		}
942
	}
943
944
	/**
945
	 * Generate a temporary ID to store while waiting for a push or pull to complete, before the record has been assigned a new ID
946
	 *
947
	 * @param string $direction Whether this is part of a push or pull action.
948
	 * @return string $id is a temporary string that will be replaced if the modification is successful.
949
	 */
950
	public function generate_temporary_id( $direction ) {
951
		if ( 'push' === $direction ) {
952
			$prefix = 'tmp_sf_';
953
		} elseif ( 'pull' === $direction ) {
954
			$prefix = 'tmp_wp_';
955
		}
956
		$id = uniqid( $prefix, true );
957
		return $id;
958
	}
959
960
	/**
961
	 * Returns Salesforce object mappings for a given WordPress object.
962
	 *
963
	 * @param string $object_type Type of object to load.
964
	 * @param int    $object_id Unique identifier of the target object to load.
965
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
966
	 *
967
	 * @return array of object maps
968
	 */
969
	public function load_all_by_wordpress( $object_type, $object_id, $reset = false ) {
970
		$conditions = array(
971
			'wordpress_id'     => $object_id,
972
			'wordpress_object' => $object_type,
973
		);
974
		return $this->get_all_object_maps( $conditions, $reset );
975
	}
976
977
	/**
978
	 * Returns Salesforce object mappings for a given Salesforce object.
979
	 *
980
	 * @param string $salesforce_id Type of object to load.
981
	 * @param array  $fieldmap the fieldmap this object map works with.
982
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
983
	 *
984
	 * @return array $maps all the object maps that match the Salesforce Id
985
	 */
986
	public function load_object_maps_by_salesforce_id( $salesforce_id, $fieldmap = array(), $reset = false ) {
987
		$conditions = array(
988
			'salesforce_id' => $salesforce_id,
989
		);
990
		if ( is_array( $fieldmap ) && ! empty( $fieldmap ) ) {
991
			$conditions['wordpress_object'] = $fieldmap['wordpress_object'];
992
		}
993
		$maps = $this->get_all_object_maps( $conditions, $reset );
994
995
		return $maps;
996
	}
997
998
	/**
999
	 * Returns Salesforce object mappings for a given Salesforce object.
1000
	 *
1001
	 * @param string $salesforce_id Type of object to load.
1002
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
1003
	 *
1004
	 * @return array $maps all the object maps that match the Salesforce Id
1005
	 * @deprecated since 2.1.0. Will be removed in 3.0.0.
1006
	 */
1007
	public function load_all_by_salesforce( $salesforce_id, $reset = false ) {
1008
		$maps = $this->load_object_maps_by_salesforce_id( $salesforce_id, array(), $reset );
1009
		return $maps;
1010
	}
1011
1012
	/**
1013
	 * Map values between WordPress and Salesforce objects.
1014
	 *
1015
	 * @param array  $mapping Mapping object.
1016
	 * @param array  $object WordPress or Salesforce object data.
1017
	 * @param array  $trigger The thing that triggered this mapping.
1018
	 * @param bool   $use_soap Flag to enforce use of the SOAP API.
1019
	 * @param bool   $is_new Indicates whether a mapping object for this entity already exists.
1020
	 * @param string $object_id_field optionally pass the object id field name.
1021
	 * @return array Associative array of key value pairs.
1022
	 */
1023
	public function map_params( $mapping, $object, $trigger, $use_soap = false, $is_new = true, $object_id_field = '' ) {
1024
1025
		$params = array();
1026
1027
		$has_missing_required_salesforce_field = false;
1028
		foreach ( $mapping['fields'] as $fieldmap ) {
1029
1030
			$wordpress_haystack  = array_values( $this->wordpress_events );
1031
			$salesforce_haystack = array_values( $this->salesforce_events );
1032
1033
			$fieldmap['wordpress_field']['methods'] = maybe_unserialize( $fieldmap['wordpress_field']['methods'] );
0 ignored issues
show
The function maybe_unserialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1033
			$fieldmap['wordpress_field']['methods'] = /** @scrutinizer ignore-call */ maybe_unserialize( $fieldmap['wordpress_field']['methods'] );
Loading history...
1034
1035
			$wordpress_field = $fieldmap['wordpress_field']['label'];
1036
1037
			if ( version_compare( $this->version, '1.2.0', '>=' ) && isset( $fieldmap['salesforce_field']['name'] ) ) {
1038
				$salesforce_field = $fieldmap['salesforce_field']['name'];
1039
				// Load the type of the Salesforce field. We can use this to handle Salesforce field value issues that come up based on what the field sends into WordPress or expects from WordPress.
1040
				$salesforce_field_type = $fieldmap['salesforce_field']['type'];
1041
			} else {
1042
				$salesforce_field = $fieldmap['salesforce_field']['label'];
1043
			}
1044
1045
			// A WordPress event caused this.
1046
			if ( in_array( $trigger, array_values( $wordpress_haystack ), true ) ) {
1047
1048
				// Is the field in WordPress an array, if we unserialize it? Salesforce wants it to be an imploded string.
1049
				if ( is_array( maybe_unserialize( $object[ $wordpress_field ] ) ) ) {
1050
					// if the WordPress field is a list of capabilities (the source field is wp_capabilities), we need to get the array keys from WordPress to send them to Salesforce.
1051
					if ( $this->wpdb->prefix . 'capabilities' === $wordpress_field ) {
1052
						$object[ $wordpress_field ] = implode( $this->array_delimiter, array_keys( $object[ $wordpress_field ] ) );
1053
					} else {
1054
						$object[ $wordpress_field ] = implode( $this->array_delimiter, $object[ $wordpress_field ] );
1055
					}
1056
				}
1057
1058
				if ( isset( $salesforce_field_type ) ) {
1059
					// Is the Salesforce field a date, and is the WordPress value a valid date?
1060
					// According to https://salesforce.stackexchange.com/questions/57032/date-format-with-salesforce-rest-api.
1061
					if ( in_array( $salesforce_field_type, $this->date_types_from_salesforce, true ) ) {
1062
						if ( '' === $object[ $wordpress_field ] ) {
1063
							$object[ $wordpress_field ] = null;
1064
						} else {
1065
							if ( false !== strtotime( $object[ $wordpress_field ] ) ) {
1066
								$timestamp = strtotime( $object[ $wordpress_field ] );
1067
							} else {
1068
								$timestamp = $object[ $wordpress_field ];
1069
							}
1070
							if ( 'datetime' === $salesforce_field_type ) {
1071
								$object[ $wordpress_field ] = date_i18n( 'c', $timestamp );
0 ignored issues
show
The function date_i18n was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1071
								$object[ $wordpress_field ] = /** @scrutinizer ignore-call */ date_i18n( 'c', $timestamp );
Loading history...
1072
							} else {
1073
								$object[ $wordpress_field ] = date_i18n( 'Y-m-d', $timestamp );
1074
							}
1075
						}
1076
					}
1077
1078
					// Boolean SF fields only want real boolean values. NULL is also not allowed.
1079
					if ( 'boolean' === $salesforce_field_type ) {
1080
						$object[ $wordpress_field ] = (bool) $object[ $wordpress_field ];
1081
					}
1082
				}
1083
1084
				$params[ $salesforce_field ] = $object[ $wordpress_field ];
1085
1086
				// If the field is a key in Salesforce, remove it from $params to avoid upsert errors from Salesforce,
1087
				// but still put its name in the params array so we can check for it later.
1088
				if ( '1' === $fieldmap['is_key'] ) {
1089
					if ( ! $use_soap ) {
1090
						unset( $params[ $salesforce_field ] );
1091
					}
1092
					$params['key'] = array(
1093
						'salesforce_field' => $salesforce_field,
1094
						'wordpress_field'  => $wordpress_field,
1095
						'value'            => $object[ $wordpress_field ],
1096
					);
1097
				}
1098
1099
				// If the field is a prematch in Salesforce, put its name in the params array so we can check for it later.
1100
				if ( '1' === $fieldmap['is_prematch'] ) {
1101
					$params['prematch'] = array(
1102
						'salesforce_field' => $salesforce_field,
1103
						'wordpress_field'  => $wordpress_field,
1104
						'value'            => $object[ $wordpress_field ],
1105
					);
1106
				}
1107
1108
				// Skip fields that aren't being pushed to Salesforce.
1109
				if ( ! in_array( $fieldmap['direction'], array_values( $this->direction_wordpress ), true ) ) {
1110
					// The trigger is a WordPress trigger, but the fieldmap direction is not a WordPress direction.
1111
					unset( $params[ $salesforce_field ] );
1112
				}
1113
1114
				// we need to check if the user has permission to modify the fields in Salesforce in the way we're trying to modify them.
1115
				if ( true === $is_new ) {
1116
					// I think it's good to over-mention that createable is really how the Salesforce api spells it.
1117
					// Skip fields that aren't createable when mapping params because Salesforce will error otherwise.
1118
					// This happens after dealing with the field types because key and prematch should still be available to the plugin, even if the values are not createable in Salesforce.
1119
					if ( 1 !== (int) $fieldmap['salesforce_field']['createable'] ) {
1120
						unset( $params[ $salesforce_field ] );
1121
					}
1122
				} elseif ( false === $is_new ) {
1123
					// if we're updating an existing record in Salesforce, check for updateable fields.
1124
					// I think it's good to over-mention that updateable is really how the Salesforce api spells it.
1125
					// Skip fields that aren't updateable when mapping params because Salesforce will error otherwise.
1126
					// This happens after dealing with the field types because key and prematch should still be available to the plugin, even if the values are not updateable in Salesforce.
1127
					if ( 1 !== (int) $fieldmap['salesforce_field']['updateable'] ) {
1128
						unset( $params[ $salesforce_field ] );
1129
					}
1130
				}
1131
1132
				// This case means the following:
1133
				// this field is expected by the fieldmap
1134
				// Salesforce's api reports that this field is required
1135
				// we do not have a WordPress value for this field, or it's empty
1136
				// it also means the field has not been unset by prematch, createable/updateable, key, or directional flags prior to this check.
1137
				// When this happens, we should flag that we're missing a required Salesforce field.
1138
				if ( in_array( $salesforce_field, $params, true ) && false === filter_var( $fieldmap['salesforce_field']['nillable'], FILTER_VALIDATE_BOOLEAN ) && ( ! isset( $object[ $wordpress_field ] ) || '' === $object[ $wordpress_field ] ) ) {
1139
					$has_missing_required_salesforce_field = true;
1140
				}
1141
1142
				// we don't need a continue with the unset methods because there's no array being created down here.
1143
			} elseif ( in_array( $trigger, $salesforce_haystack, true ) ) {
1144
1145
				// A Salesforce event caused this.
1146
1147
				if ( isset( $salesforce_field_type ) && isset( $object[ $salesforce_field ] ) && ! is_null( $object[ $salesforce_field ] ) ) {
1148
					// Salesforce provides multipicklist values as a delimited string. If the
1149
					// destination field in WordPress accepts multiple values, explode the string into an array and then serialize it.
1150
					if ( in_array( $salesforce_field_type, $this->array_types_from_salesforce, true ) ) {
1151
						$object[ $salesforce_field ] = explode( $this->array_delimiter, $object[ $salesforce_field ] );
1152
						// if the WordPress field is a list of capabilities (the destination field is wp_capabilities), we need to set the array for WordPress to save it.
1153
						if ( $this->wpdb->prefix . 'capabilities' === $wordpress_field ) {
1154
							$capabilities = array();
1155
							foreach ( $object[ $salesforce_field ] as $capability ) {
1156
								$capabilities[ $capability ] = true;
1157
							}
1158
							$object[ $salesforce_field ] = $capabilities;
1159
						}
1160
					}
1161
1162
					// Handle specific data types from Salesforce.
1163
					switch ( $salesforce_field_type ) {
1164
						case ( in_array( $salesforce_field_type, $this->date_types_from_salesforce, true ) ):
1165
							$format = get_option( 'date_format', 'U' );
0 ignored issues
show
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1165
							$format = /** @scrutinizer ignore-call */ get_option( 'date_format', 'U' );
Loading history...
1166
							if ( isset( $fieldmap['wordpress_field']['type'] ) && 'datetime' === $fieldmap['wordpress_field']['type'] ) {
1167
								$format = 'Y-m-d H:i:s';
1168
							}
1169
							if ( 'tribe_events' === $mapping['wordpress_object'] && class_exists( 'Tribe__Events__Main' ) ) {
1170
								$format = 'Y-m-d H:i:s';
1171
							}
1172
							if ( 'datetime' === $salesforce_field_type ) {
1173
								// Note: the Salesforce REST API appears to always return datetimes as GMT values. We should retrieve them that way, then format them to deal with them in WordPress appropriately.
1174
								// We should not do any converting unless it's a datetime, because if it's a date, Salesforce stores it as midnight. We don't want to convert that.
1175
								$object[ $salesforce_field ] = get_date_from_gmt( $object[ $salesforce_field ], 'Y-m-d\TH:i:s\Z' ); // convert from GMT to local date/time based on WordPress time zone setting.
0 ignored issues
show
The function get_date_from_gmt was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1175
								$object[ $salesforce_field ] = /** @scrutinizer ignore-call */ get_date_from_gmt( $object[ $salesforce_field ], 'Y-m-d\TH:i:s\Z' ); // convert from GMT to local date/time based on WordPress time zone setting.
Loading history...
1176
							}
1177
							$object[ $salesforce_field ] = date_i18n( $format, strtotime( $object[ $salesforce_field ] ) );
1178
							break;
1179
						case ( in_array( $salesforce_field_type, $this->int_types_from_salesforce, true ) ):
1180
							$object[ $salesforce_field ] = isset( $object[ $salesforce_field ] ) ? (int) $object[ $salesforce_field ] : 0;
1181
							break;
1182
						case 'text':
1183
							$object[ $salesforce_field ] = (string) $object[ $salesforce_field ];
1184
							break;
1185
						case 'url':
1186
							$object[ $salesforce_field ] = esc_url_raw( $object[ $salesforce_field ] );
0 ignored issues
show
The function esc_url_raw was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1186
							$object[ $salesforce_field ] = /** @scrutinizer ignore-call */ esc_url_raw( $object[ $salesforce_field ] );
Loading history...
1187
							break;
1188
					}
1189
				}
1190
1191
				// Make an array because we need to store the methods for each field as well.
1192
				if ( isset( $object[ $salesforce_field ] ) ) {
1193
					$params[ $wordpress_field ]          = array();
1194
					$params[ $wordpress_field ]['value'] = $object[ $salesforce_field ];
1195
				} elseif ( is_null( $object[ $salesforce_field ] ) ) {
1196
					// Salesforce returns blank fields as null fields; set them to blank.
1197
					$params[ $wordpress_field ]          = array();
1198
					$params[ $wordpress_field ]['value'] = '';
1199
				} else {
1200
					// prevent fields that don't exist from being passed.
1201
					continue;
1202
				}
1203
1204
				// If the field is a key in Salesforce, disregard since this is caused by a Salesforce event. We're setting up data to be stored in WordPress here, and WordPress is not concerned with external key designations in Salesforce.
1205
1206
				// If the field is a prematch in Salesforce, put its name in the params array so we can check for it later.
1207
				if ( '1' === $fieldmap['is_prematch'] ) {
1208
					$params['prematch'] = array(
1209
						'salesforce_field' => $salesforce_field,
1210
						'wordpress_field'  => $wordpress_field,
1211
						'value'            => $object[ $salesforce_field ],
1212
						'method_match'     => isset( $fieldmap['wordpress_field']['methods']['match'] ) ? $fieldmap['wordpress_field']['methods']['match'] : $fieldmap['wordpress_field']['methods']['read'],
1213
						'method_read'      => $fieldmap['wordpress_field']['methods']['read'],
1214
						'method_create'    => $fieldmap['wordpress_field']['methods']['create'],
1215
						'method_update'    => $fieldmap['wordpress_field']['methods']['update'],
1216
					);
1217
				}
1218
1219
				// Skip fields that aren't being pulled from Salesforce.
1220
				if ( ! in_array( $fieldmap['direction'], array_values( $this->direction_salesforce ), true ) ) {
1221
					// The trigger is a Salesforce trigger, but the fieldmap direction is not a Salesforce direction.
1222
					unset( $params[ $wordpress_field ] );
1223
					// we also need to continue here, so it doesn't create an empty array below for fields that are WordPress -> Salesforce only.
1224
					continue;
1225
				}
1226
1227
				// Skip fields that aren't editable by the plugin when mapping params.
1228
				// By default this is only the WordPress object's ID field.
1229
				// @see $wordpress->object_fields() method and object_sync_for_salesforce_wordpress_field_is_editable filter.
1230
				if ( isset( $fieldmap['wordpress_field']['editable'] ) && true !== (bool) $fieldmap['wordpress_field']['editable'] ) {
1231
					unset( $params[ $wordpress_field ] );
1232
					continue;
1233
				}
1234
1235
				switch ( $trigger ) {
1236
					case $this->sync_sf_create:
1237
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['create'];
1238
						break;
1239
					case $this->sync_sf_update:
1240
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['update'];
1241
						break;
1242
					case $this->sync_sf_delete:
1243
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['delete'];
1244
						break;
1245
				}
1246
1247
				// always allow for the delete and read methods.
1248
				$params[ $wordpress_field ]['method_delete'] = $fieldmap['wordpress_field']['methods']['delete'];
1249
				$params[ $wordpress_field ]['method_read']   = $fieldmap['wordpress_field']['methods']['read'];
1250
1251
			} // End if() statement.
1252
		} // End foreach() loop.
1253
1254
		if ( true === $has_missing_required_salesforce_field ) {
1255
			update_option( $this->option_prefix . 'missing_required_data_id_' . $object[ $object_id_field ], true, false );
0 ignored issues
show
The function update_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1255
			/** @scrutinizer ignore-call */ 
1256
   update_option( $this->option_prefix . 'missing_required_data_id_' . $object[ $object_id_field ], true, false );
Loading history...
1256
			return array();
1257
		}
1258
1259
		return $params;
1260
1261
	}
1262
1263
	/**
1264
	 * Prepare field map data for use
1265
	 *
1266
	 * @param array  $mappings Array of fieldmaps.
1267
	 * @param string $record_type Optional Salesforce record type to see if it is allowed or not.
1268
	 *
1269
	 * @return array $mappings Associative array of field maps ready to use
1270
	 */
1271
	private function prepare_fieldmap_data( $mappings, $record_type = '' ) {
1272
		foreach ( $mappings as $id => $mapping ) {
1273
			$mappings[ $id ]['salesforce_record_types_allowed'] = isset( $mapping['salesforce_record_types_allowed'] ) ? maybe_unserialize( $mapping['salesforce_record_types_allowed'] ) : array();
0 ignored issues
show
The function maybe_unserialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1273
			$mappings[ $id ]['salesforce_record_types_allowed'] = isset( $mapping['salesforce_record_types_allowed'] ) ? /** @scrutinizer ignore-call */ maybe_unserialize( $mapping['salesforce_record_types_allowed'] ) : array();
Loading history...
1274
			$mappings[ $id ]['fields']                          = isset( $mapping['fields'] ) ? maybe_unserialize( $mapping['fields'] ) : array();
1275
			$mappings[ $id ]['sync_triggers']                   = isset( $mapping['sync_triggers'] ) ? maybe_unserialize( $mapping['sync_triggers'] ) : array();
1276
			// format the sync triggers.
1277
			$sync_triggers                    = $this->maybe_upgrade_sync_triggers( $mappings[ $id ]['sync_triggers'], $mapping['version'], $mapping['id'] );
1278
			$mappings[ $id ]['sync_triggers'] = $sync_triggers;
1279
			if ( '' !== $record_type && ! in_array( $record_type, $mappings[ $id ]['salesforce_record_types_allowed'], true ) ) {
1280
				unset( $mappings[ $id ] );
1281
			}
1282
		}
1283
		return $mappings;
1284
	}
1285
1286
	/**
1287
	 * Format the sync trigger values for storage in the database.
1288
	 *
1289
	 * @param array  $sync_triggers Array of sync triggers.
1290
	 * @param string $mapping_version the database version when the fieldmmap was saved.
1291
	 * @param int    $mapping_id if the fieldmap already exists, this is the ID.
1292
	 *
1293
	 * @return array $sync_triggers possibly updated array of sync triggers.
1294
	 */
1295
	private function maybe_upgrade_sync_triggers( $sync_triggers, $mapping_version, $mapping_id = '' ) {
1296
		// in v2 of this plugin, we replaced the bit flags with strings to make them more legible.
1297
		if ( version_compare( $mapping_version, '2.0.0', '<' ) ) {
1298
			// check if the triggers stored in the database are up to date. if not, update them.
1299
			$intersect = array_intersect( $sync_triggers, array_merge( $this->wordpress_events, $this->salesforce_events ) );
1300
			if ( empty( $intersect ) ) {
1301
				$updated_sync_triggers = array();
1302
				foreach ( $sync_triggers as $key => $value ) {
1303
					if ( $value === (string) $this->sync_off_v1 ) {
1304
						$updated_sync_triggers[] = $this->sync_off;
1305
					}
1306
					if ( $value === (string) $this->sync_wordpress_create_v1 ) {
1307
						$updated_sync_triggers[] = $this->sync_wordpress_create;
1308
					}
1309
					if ( $value === (string) $this->sync_wordpress_update_v1 ) {
1310
						$updated_sync_triggers[] = $this->sync_wordpress_update;
1311
					}
1312
					if ( $value === (string) $this->sync_wordpress_delete_v1 ) {
1313
						$updated_sync_triggers[] = $this->sync_wordpress_delete;
1314
					}
1315
					if ( $value === (string) $this->sync_sf_create_v1 ) {
1316
						$updated_sync_triggers[] = $this->sync_sf_create;
1317
					}
1318
					if ( $value === (string) $this->sync_sf_update_v1 ) {
1319
						$updated_sync_triggers[] = $this->sync_sf_update;
1320
					}
1321
					if ( $value === (string) $this->sync_sf_delete_v1 ) {
1322
						$updated_sync_triggers[] = $this->sync_sf_delete;
1323
					}
1324
				}
1325
1326
				if ( '' !== $mapping_id ) {
1327
					// format the fieldmap update query for the database.
1328
					$data = array();
1329
					if ( ! empty( $updated_sync_triggers ) ) {
1330
						$data['sync_triggers'] = array();
1331
						foreach ( $updated_sync_triggers as $key => $value ) {
1332
							$updated_sync_triggers[ $key ] = esc_html( $updated_sync_triggers[ $key ] );
0 ignored issues
show
The function esc_html was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1332
							$updated_sync_triggers[ $key ] = /** @scrutinizer ignore-call */ esc_html( $updated_sync_triggers[ $key ] );
Loading history...
1333
						}
1334
						$data['sync_triggers'] = maybe_serialize( $updated_sync_triggers );
0 ignored issues
show
The function maybe_serialize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1334
						$data['sync_triggers'] = /** @scrutinizer ignore-call */ maybe_serialize( $updated_sync_triggers );
Loading history...
1335
						$data['version']       = get_option( $this->option_prefix . 'db_version', $this->version );
0 ignored issues
show
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1335
						$data['version']       = /** @scrutinizer ignore-call */ get_option( $this->option_prefix . 'db_version', $this->version );
Loading history...
1336
						// update the sync triggers and version fieldmap in the database.
1337
						$update = $this->wpdb->update(
1338
							$this->fieldmap_table,
1339
							$data,
1340
							array(
1341
								'id' => $mapping_id,
1342
							)
1343
						);
1344
					}
1345
				}
1346
			}
1347
		}
1348
		// whether it was updated or not, this is the array of sync triggers.
1349
		return $sync_triggers;
1350
	}
1351
1352
	/**
1353
	 * Check object map table to see if there have been any failed object map create attempts
1354
	 *
1355
	 * @return array $errors Associative array of rows that failed to finish from either system
1356
	 */
1357
	public function get_failed_object_maps() {
1358
		$table                 = $this->object_map_table;
1359
		$errors                = array();
1360
		$items_per_page        = (int) get_option( $this->option_prefix . 'errors_per_page', 50 );
0 ignored issues
show
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1360
		$items_per_page        = (int) /** @scrutinizer ignore-call */ get_option( $this->option_prefix . 'errors_per_page', 50 );
Loading history...
1361
		$current_error_page    = isset( $_GET['error_page'] ) ? (int) $_GET['error_page'] : 1;
1362
		$offset                = ( $current_error_page * $items_per_page ) - $items_per_page;
1363
		$all_errors            = $this->wpdb->get_results( "SELECT * FROM {$table} WHERE salesforce_id LIKE 'tmp_sf_%' OR wordpress_id LIKE 'tmp_wp_%' OR last_sync_status = 0 LIMIT {$offset}, {$items_per_page}", ARRAY_A );
0 ignored issues
show
The constant ARRAY_A was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1364
		$errors_total          = $this->wpdb->get_var( "SELECT COUNT(`id`) FROM {$table} WHERE salesforce_id LIKE 'tmp_sf_%' OR wordpress_id LIKE 'tmp_wp_%' OR last_sync_status = 0" );
1365
		$errors['total_pages'] = ceil( $errors_total / $items_per_page );
1366
		$errors['pagination']  = paginate_links(
0 ignored issues
show
The function paginate_links was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1366
		$errors['pagination']  = /** @scrutinizer ignore-call */ paginate_links(
Loading history...
1367
			array(
1368
				'base'      => add_query_arg( 'error_page', '%#%' ),
0 ignored issues
show
The function add_query_arg was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1368
				'base'      => /** @scrutinizer ignore-call */ add_query_arg( 'error_page', '%#%' ),
Loading history...
1369
				'format'    => '',
1370
				'total'     => $errors['total_pages'],
1371
				'prev_text' => __( '&laquo;', 'object-sync-for-salesforce' ),
0 ignored issues
show
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1371
				'prev_text' => /** @scrutinizer ignore-call */ __( '&laquo;', 'object-sync-for-salesforce' ),
Loading history...
1372
				'next_text' => __( '&raquo;', 'object-sync-for-salesforce' ),
1373
				'current'   => $current_error_page,
1374
			)
1375
		);
1376
		$errors['error_page']  = $current_error_page;
1377
		$errors['all_errors']  = $all_errors;
1378
		$errors['total']       = $errors_total;
1379
		return $errors;
1380
	}
1381
1382
	/**
1383
	 * Check object map table to see if there have been any failed object map create attempts
1384
	 *
1385
	 * @param int $id The ID of a desired mapping.
1386
	 * @return array $error Associative array of single row that failed to finish based on id
1387
	 */
1388
	public function get_failed_object_map( $id ) {
1389
		$table     = $this->object_map_table;
1390
		$error     = array();
1391
		$error_row = $this->wpdb->get_row( 'SELECT * FROM ' . $table . ' WHERE id = "' . $id . '"', ARRAY_A );
0 ignored issues
show
The constant ARRAY_A was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1392
		if ( ! empty( $error_row ) ) {
1393
			$error = $error_row;
1394
		}
1395
		return $error;
1396
	}
1397
}
1398