Passed
Pull Request — master (#339)
by Jonathan
10:09 queued 06:14
created

Object_Sync_Sf_Mapping   F

Complexity

Total Complexity 179

Size/Duplication

Total Lines 1080
Duplicated Lines 0 %

Importance

Changes 22
Bugs 4 Features 1
Metric Value
eloc 536
c 22
b 4
f 1
dl 0
loc 1080
rs 2
wmc 179

23 Methods

Rating   Name   Duplication   Size   Complexity  
A get_mapped_record_types() 0 2 2
A update_fieldmap() 0 16 4
A get_all_object_maps() 0 26 5
A generate_temporary_id() 0 8 3
A load_all_by_salesforce() 0 8 1
A setup_object_map_data() 0 6 1
B get_object_maps() 0 32 9
A load_by_wordpress() 0 6 1
A delete_object_map() 0 18 6
A load_all_by_wordpress() 0 6 1
A update_object_map() 0 16 3
B load_by_salesforce() 0 44 9
A __construct() 0 56 3
A create_fieldmap() 0 10 3
A delete_fieldmap() 0 9 2
A prepare_fieldmap_data() 0 18 5
A get_failed_object_maps() 0 23 2
B create_object_map() 0 58 10
B get_mapped_fields() 0 30 11
F setup_fieldmap_data() 0 116 36
C get_fieldmaps() 0 51 12
A get_failed_object_map() 0 8 2
F map_params() 0 222 48

How to fix   Complexity   

Complex Class

Complex classes like Object_Sync_Sf_Mapping often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Object_Sync_Sf_Mapping, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Class file for the Object_Sync_Sf_Mapping class.
4
 *
5
 * @file
6
 */
7
8
if ( ! class_exists( 'Object_Sync_Salesforce' ) ) {
9
	die();
10
}
11
12
/**
13
 * Map objects and records between WordPress and Salesforce
14
 */
15
class Object_Sync_Sf_Mapping {
16
17
	protected $wpdb;
18
	protected $version;
19
	protected $user_installed_version;
20
	protected $slug;
21
	protected $logging;
22
	protected $option_prefix;
23
24
	protected $fieldmap_table;
25
	protected $object_map_table;
26
27
	public $sync_off;
28
	public $sync_wordpress_create;
29
	public $sync_wordpress_update;
30
	public $sync_wordpress_delete;
31
	public $sync_sf_create;
32
	public $sync_sf_update;
33
	public $sync_sf_delete;
34
	public $wordpress_events;
35
	public $salesforce_events;
36
37
	public $direction_wordpress_sf;
38
	public $direction_sf_wordpress;
39
	public $direction_sync;
40
41
	public $direction_wordpress;
42
	public $direction_salesforce;
43
44
	public $salesforce_default_record_type;
45
46
	public $array_delimiter;
47
	public $array_types_from_salesforce;
48
	public $date_types_from_salesforce;
49
	public $int_types_from_salesforce;
50
51
	public $name_length;
52
53
	public $status_success;
54
	public $status_error;
55
56
	public $debug;
57
58
	/**
59
	 * Constructor which sets up links between the systems
60
	 *
61
	 * @param object $wpdb A WPDB object.
62
	 * @param string $version The plugin version.
63
	 * @param string $slug The plugin slug.
64
	 * @param object $logging Object_Sync_Sf_Logging.
65
	 * @param string $option_prefix The plugin option prefix
66
	 * @throws \Exception
67
	 */
68
	public function __construct( $wpdb, $version, $slug, $logging, $option_prefix = '', $user_installed_version = '' ) {
69
		$this->wpdb                   = $wpdb;
70
		$this->version                = $version;
71
		$this->slug                   = $slug;
72
		$this->logging                = $logging;
73
		$this->option_prefix          = isset( $option_prefix ) ? $option_prefix : 'object_sync_for_salesforce_';
74
		$this->user_installed_version = isset( $user_installed_version ) ? $user_installed_version : $version;
75
76
		$this->fieldmap_table   = $this->wpdb->prefix . 'object_sync_sf_field_map';
77
		$this->object_map_table = $this->wpdb->prefix . 'object_sync_sf_object_map';
78
79
		/*
80
		 * These parameters are how we define when syncing should occur on each field map.
81
		 * They get used in the admin settings, as well as the push/pull methods to see if something should happen.
82
		 * It is unclear why the Drupal module used bit flags, but it seems reasonable to keep the convention.
83
		*/
84
		$this->sync_off              = 0x0000;
85
		$this->sync_wordpress_create = 0x0001;
86
		$this->sync_wordpress_update = 0x0002;
87
		$this->sync_wordpress_delete = 0x0004;
88
		$this->sync_sf_create        = 0x0008;
89
		$this->sync_sf_update        = 0x0010;
90
		$this->sync_sf_delete        = 0x0020;
91
92
		// Define which events are initialized by which system.
93
		$this->wordpress_events  = array( $this->sync_wordpress_create, $this->sync_wordpress_update, $this->sync_wordpress_delete );
94
		$this->salesforce_events = array( $this->sync_sf_create, $this->sync_sf_update, $this->sync_sf_delete );
95
96
		// Constants for the directions to map things.
97
		$this->direction_wordpress_sf = 'wp_sf';
98
		$this->direction_sf_wordpress = 'sf_wp';
99
		$this->direction_sync         = 'sync';
100
101
		$this->direction_wordpress  = array( $this->direction_wordpress_sf, $this->direction_sync );
102
		$this->direction_salesforce = array( $this->direction_sf_wordpress, $this->direction_sync );
103
104
		// This is used when we map a record with default or Master.
105
		$this->salesforce_default_record_type = 'default';
106
107
		// Salesforce has multipicklists and they have a delimiter.
108
		$this->array_delimiter = ';';
109
		// What data types in Salesforce should be an array?
110
		$this->array_types_from_salesforce = array( 'multipicklist' );
111
		// What data types in Salesforce should be a date field?
112
		$this->date_types_from_salesforce = array( 'date', 'datetime' );
113
		// What data types in Salesforce should be an integer?
114
		$this->int_types_from_salesforce = array( 'integer', 'boolean' );
115
116
		// Max length for a mapping field.
117
		$this->name_length = 128;
118
119
		// Statuses for object sync.
120
		$this->status_success = 1;
121
		$this->status_error   = 0;
122
123
		$this->debug = get_option( $this->option_prefix . 'debug_mode', false );
124
125
	}
126
127
	/**
128
	 * Create a fieldmap row between a WordPress and Salesforce object
129
	 *
130
	 * @param array $posted The results of $_POST.
131
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
132
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
133
	 * @throws \Exception
134
	 */
135
	public function create_fieldmap( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array() ) {
136
		$data = $this->setup_fieldmap_data( $posted, $wordpress_fields, $salesforce_fields );
137
		if ( version_compare( $this->user_installed_version, '1.2.5', '>=' ) ) {
138
			$data['version'] = $this->user_installed_version;
139
		}
140
		$insert = $this->wpdb->insert( $this->fieldmap_table, $data );
141
		if ( 1 === $insert ) {
142
			return $this->wpdb->insert_id;
143
		} else {
144
			return false;
145
		}
146
	}
147
148
	/**
149
	 * Get one or more fieldmap rows between a WordPress and Salesforce object
150
	 *
151
	 * @param int   $id The ID of a desired mapping.
152
	 * @param array $conditions Array of key=>value to match the mapping by.
153
	 * @param bool  $reset Unused parameter.
154
	 * @return array $map a single mapping or $mappings, an array of mappings.
155
	 * @throws \Exception
156
	 */
157
	public function get_fieldmaps( $id = null, $conditions = array(), $reset = false ) {
0 ignored issues
show
Unused Code introduced by
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

157
	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...
158
		$table = $this->fieldmap_table;
159
		if ( null !== $id ) { // get one fieldmap.
160
			$map        = $this->wpdb->get_row( 'SELECT * FROM ' . $table . ' WHERE id = ' . $id, ARRAY_A );
161
			$mappings[] = $map;
162
			$map        = $this->prepare_fieldmap_data( $mappings )[0];
163
			return $map;
164
		} elseif ( ! empty( $conditions ) ) { // get multiple but with a limitation.
165
			$mappings    = array();
166
			$record_type = '';
167
			// Assemble the SQL.
168
			if ( ! empty( $conditions ) ) {
169
				$where = ' WHERE ';
170
				$i     = 0;
171
				foreach ( $conditions as $key => $value ) {
172
					if ( 'salesforce_record_type' === $key ) {
173
						$record_type = sanitize_text_field( $value );
174
					} else {
175
						$i++;
176
						if ( $i > 1 ) {
177
							$where .= ' AND ';
178
						}
179
						$where .= '`' . $key . '` = "' . $value . '"';
180
					}
181
				}
182
			} else {
183
				$where = '';
184
			}
185
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . ' ORDER BY `weight`', ARRAY_A );
186
			if ( ! empty( $mappings ) ) {
187
				$mappings = $this->prepare_fieldmap_data( $mappings, $record_type );
188
			}
189
			return $mappings;
190
		} else {
191
			// get all of the mappings. ALL THE MAPPINGS.
192
			if ( version_compare( $this->user_installed_version, '1.10.0', '>=' ) ) {
193
				// if the version is greater than or equal to 1.9.0, the fieldmap table has a wordpress_object_default_status column
194
				$mappings = $this->wpdb->get_results( "SELECT `id`, `label`, `wordpress_object`, `wordpress_object_default_status`, `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 ); // WPCS: unprepared SQL OK.
195
			} elseif ( version_compare( $this->user_installed_version, '1.5.0', '>=' ) ) {
196
				// if the version is greater than or equal to 1.5.0, the fieldmap table has a pull_to_drafts column
197
				$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 ); // WPCS: unprepared SQL OK.
198
			} elseif ( version_compare( $this->user_installed_version, '1.2.5', '>=' ) ) {
199
				// if the version is greater than or equal to 1.2.5, the fieldmap table has a version column
200
				$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 ); // WPCS: unprepared SQL OK.
201
			} else {
202
				$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 ); // WPCS: unprepared SQL OK.
203
			}
204
			if ( ! empty( $mappings ) ) {
205
				$mappings = $this->prepare_fieldmap_data( $mappings );
206
			}
207
			return $mappings;
208
		} // End if().
209
	}
210
211
	/**
212
	 * For a mapping, get the fieldmaps associated with it.
213
	 *
214
	 * @param Array $mapping The mapping for which we are getting the fieldmaps.
215
	 * @param Array $directions The direction of the mapping: from WP to SF or vice-versa.
216
	 * @see Object_Sync_Sf_Salesforce_Pull::get_pull_query()
217
	 *
218
	 * @return Array of mapped fields
219
	 */
220
	public function get_mapped_fields( $mapping, $directions = array() ) {
221
		$mapped_fields = array();
222
		if ( is_array( $mapping['fields'] ) ) {
223
			foreach ( $mapping['fields'] as $fields ) {
224
				if ( empty( $directions ) || in_array( $fields['direction'], $directions, true ) ) {
225
226
					// in version 1.2.0, we provided an option for API name vs label for Salesforce fields
227
					if ( version_compare( $this->user_installed_version, '1.2.0', '>=' ) && isset( $fields['salesforce_field']['name'] ) ) {
228
						$array_key = 'name';
229
					} else {
230
						$array_key = 'label';
231
					}
232
233
					// Some field map types (Relation) store a collection of SF objects.
234
					if ( is_array( $fields['salesforce_field'] ) && ! isset( $fields['salesforce_field'][ $array_key ] ) ) {
235
						foreach ( $fields['salesforce_field'] as $sf_field ) {
236
							$mapped_fields[ $sf_field[ $array_key ] ] = $sf_field[ $array_key ];
237
						}
238
					} else { // The rest are just a name/value pair.
239
						$mapped_fields[ $fields['salesforce_field'][ $array_key ] ] = $fields['salesforce_field'][ $array_key ];
240
					}
241
				}
242
			}
243
		}
244
245
		if ( ! empty( $this->get_mapped_record_types ) ) {
246
			$mapped_fields['RecordTypeId'] = 'RecordTypeId';
247
		}
248
249
		return $mapped_fields;
250
	}
251
252
	/**
253
	 * Get the mapped record types for a given mapping.
254
	 *
255
	 * @param Array $mapping A mapping from which we wish to estract the record type.
256
	 * @return Array of mappings. Empty if the mapping's record type is default, else full of the record types.
257
	 */
258
	public function get_mapped_record_types( $mapping ) {
259
		return $mapping['salesforce_record_type_default'] === $this->salesforce_default_record_type ? array() : array_filter( maybe_unserialize( $mapping['salesforce_record_types_allowed'] ) );
260
	}
261
262
	/**
263
	 * Update a fieldmap row between a WordPress and Salesforce object
264
	 *
265
	 * @param array $posted It's $_POST.
266
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
267
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
268
	 * @param int   $id The ID of the mapping.
269
	 * @return boolean
270
	 * @throws \Exception
271
	 */
272
	public function update_fieldmap( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array(), $id = '' ) {
273
		$data = $this->setup_fieldmap_data( $posted, $wordpress_fields, $salesforce_fields );
274
		if ( version_compare( $this->user_installed_version, '1.2.5', '>=' ) && ! isset( $data['updated'] ) ) {
275
			$data['version'] = $this->user_installed_version;
276
		}
277
		$update = $this->wpdb->update(
278
			$this->fieldmap_table,
279
			$data,
280
			array(
281
				'id' => $id,
282
			)
283
		);
284
		if ( false === $update ) {
285
			return false;
286
		} else {
287
			return true;
288
		}
289
	}
290
291
	/**
292
	 * Setup fieldmap data
293
	 * Sets up the database entry for mapping the object types between Salesforce and WordPress
294
	 *
295
	 * @param array $posted It's $_POST.
296
	 * @param array $wordpress_fields The fields for the WordPress side of the mapping.
297
	 * @param array $salesforce_fields The fields for the Salesforce side of the mapping.
298
	 * @return array $data the fieldmap's data for the database
299
	 */
300
	private function setup_fieldmap_data( $posted = array(), $wordpress_fields = array(), $salesforce_fields = array() ) {
301
		$data = array(
302
			'label'             => $posted['label'],
303
			'name'              => sanitize_title( $posted['label'] ),
304
			'salesforce_object' => $posted['salesforce_object'],
305
			'wordpress_object'  => $posted['wordpress_object'],
306
		);
307
		// added in version 1.9.0.
308
		$data['wordpress_object_default_status'] = isset( $posted['wordpress_object_default_status'] ) ? sanitize_text_field( $posted['wordpress_object_default_status'] ) : '';
309
		if ( isset( $posted['wordpress_field'] ) && is_array( $posted['wordpress_field'] ) && isset( $posted['salesforce_field'] ) && is_array( $posted['salesforce_field'] ) ) {
310
			$setup['fields'] = array();
311
			foreach ( $posted['wordpress_field'] as $key => $value ) {
312
				$method_key = array_search( $value, array_column( $wordpress_fields, 'key' ), true );
313
				// in 1.9.0 we added a date format field
314
				if ( ! isset( $posted['date-format'][ $key ] ) ) {
315
					$posted['date-format'][ $key ] = '';
316
				}
317
				if ( ! isset( $posted['direction'][ $key ] ) ) {
318
					$posted['direction'][ $key ] = 'sync';
319
				}
320
				if ( ! isset( $posted['is_prematch'][ $key ] ) ) {
321
					$posted['is_prematch'][ $key ] = false;
322
				}
323
				if ( ! isset( $posted['is_key'][ $key ] ) ) {
324
					$posted['is_key'][ $key ] = false;
325
				}
326
				if ( ! isset( $posted['is_delete'][ $key ] ) ) {
327
					$posted['is_delete'][ $key ] = false;
328
				}
329
				if ( false === $posted['is_delete'][ $key ] ) {
330
					// I think it's good to over-mention that updateable is really how the Salesforce api spells it.
331
					$updateable_key = array_search( $posted['salesforce_field'][ $key ], array_column( $salesforce_fields, 'name' ), true );
332
333
					$salesforce_field_attributes = array();
334
					foreach ( $salesforce_fields[ $updateable_key ] as $sf_key => $sf_value ) {
335
						if ( isset( $sf_value ) && ! is_array( $sf_value ) ) {
336
							$salesforce_field_attributes[ $sf_key ] = esc_attr( $sf_value );
337
						} elseif ( ! empty( $sf_value ) && is_array( $sf_value ) ) {
338
							$salesforce_field_attributes[ $sf_key ] = maybe_unserialize( $sf_value );
0 ignored issues
show
Bug introduced by
$sf_value of type array is incompatible with the type string expected by parameter $data of maybe_unserialize(). ( Ignorable by Annotation )

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

338
							$salesforce_field_attributes[ $sf_key ] = maybe_unserialize( /** @scrutinizer ignore-type */ $sf_value );
Loading history...
339
						} else {
340
							$salesforce_field_attributes[ $sf_key ] = '';
341
						}
342
					}
343
344
					$setup['fields'][ $key ] = array(
345
						'wordpress_field'  => array(
346
							'label'   => sanitize_text_field( $posted['wordpress_field'][ $key ] ),
347
							'methods' => maybe_unserialize( $wordpress_fields[ $method_key ]['methods'] ),
348
							'type'    => isset( $wordpress_fields[ $method_key ]['type'] ) ? sanitize_text_field( $wordpress_fields[ $method_key ]['type'] ) : 'text',
349
						),
350
						'salesforce_field' => $salesforce_field_attributes,
351
						'date-format'      => esc_attr( $posted['date-format'][ $key ] ),
352
						'is_prematch'      => sanitize_text_field( $posted['is_prematch'][ $key ] ),
353
						'is_key'           => sanitize_text_field( $posted['is_key'][ $key ] ),
354
						'direction'        => sanitize_text_field( $posted['direction'][ $key ] ),
355
						'is_delete'        => sanitize_text_field( $posted['is_delete'][ $key ] ),
356
					);
357
358
					// If the WordPress key or the Salesforce key are blank, remove this incomplete mapping.
359
					// This prevents https://github.com/MinnPost/object-sync-for-salesforce/issues/82 .
360
					if (
361
						empty( $setup['fields'][ $key ]['wordpress_field']['label'] )
362
						||
363
						empty( $setup['fields'][ $key ]['salesforce_field']['name'] )
364
					) {
365
						unset( $setup['fields'][ $key ] );
366
					}
367
				}
368
			} // End foreach() on WordPress fields.
369
			$data['fields'] = maybe_serialize( $setup['fields'] );
370
		} elseif ( isset( $posted['fields'] ) && is_array( $posted['fields'] ) ) {
371
			// if $posted['fields'] is already set, use that
372
			$data['fields'] = maybe_serialize( $posted['fields'] );
373
		} // End if() WordPress fields are present.
374
375
		if ( isset( $posted['salesforce_record_types_allowed'] ) ) {
376
			$data['salesforce_record_types_allowed'] = maybe_serialize( $posted['salesforce_record_types_allowed'] );
377
		} else {
378
			$data['salesforce_record_types_allowed'] = maybe_serialize(
379
				array(
380
					$this->salesforce_default_record_type => $this->salesforce_default_record_type,
381
				)
382
			);
383
		}
384
		if ( isset( $posted['salesforce_record_type_default'] ) && '' !== $posted['salesforce_record_type_default'] ) {
385
			$data['salesforce_record_type_default'] = $posted['salesforce_record_type_default'];
386
		} else {
387
			$data['salesforce_record_type_default'] = maybe_serialize( $this->salesforce_default_record_type );
388
		}
389
		if ( isset( $posted['pull_trigger_field'] ) ) {
390
			$data['pull_trigger_field'] = $posted['pull_trigger_field'];
391
		}
392
		if ( isset( $posted['sync_triggers'] ) && is_array( $posted['sync_triggers'] ) ) {
393
			$setup['sync_triggers'] = array();
394
			foreach ( $posted['sync_triggers'] as $key => $value ) {
395
				$setup['sync_triggers'][ $key ] = esc_html( $posted['sync_triggers'][ $key ] );
396
			}
397
		} else {
398
			$setup['sync_triggers'] = array();
399
		}
400
		$data['sync_triggers'] = maybe_serialize( $setup['sync_triggers'] );
401
		if ( isset( $posted['pull_trigger_field'] ) ) {
402
			$data['pull_trigger_field'] = $posted['pull_trigger_field'];
403
		}
404
405
		// invert value of immediately to get value of async
406
		if ( isset( $posted['push_immediately'] ) && true === filter_var( $posted['push_immediately'], FILTER_VALIDATE_BOOLEAN ) ) {
407
			$data['push_async'] = '';
408
		} else {
409
			$data['push_async'] = '1';
410
		}
411
412
		$data['push_drafts']    = isset( $posted['push_drafts'] ) ? $posted['push_drafts'] : '';
413
		$data['pull_to_drafts'] = isset( $posted['pull_to_drafts'] ) ? $posted['pull_to_drafts'] : '';
414
		$data['weight']         = isset( $posted['weight'] ) ? $posted['weight'] : '';
415
		return $data;
416
	}
417
418
	/**
419
	 * Delete a fieldmap row between a WordPress and Salesforce object
420
	 *
421
	 * @param int $id The ID of a field mapping.
422
	 * @return Boolean
423
	 * @throws \Exception
424
	 */
425
	public function delete_fieldmap( $id = '' ) {
426
		$data   = array(
427
			'id' => $id,
428
		);
429
		$delete = $this->wpdb->delete( $this->fieldmap_table, $data );
430
		if ( 1 === $delete ) {
431
			return true;
432
		} else {
433
			return false;
434
		}
435
	}
436
437
	/**
438
	 * Create an object map row between a WordPress and Salesforce object
439
	 *
440
	 * @param array $posted It's $_POST.
441
	 * @return false|Int of field mapping between WordPress and Salesforce objects
442
	 * @throws \Exception
443
	 */
444
	public function create_object_map( $posted = array() ) {
445
		$data            = $this->setup_object_map_data( $posted );
446
		$data['created'] = current_time( 'mysql' );
447
		// Check to see if we don't know the salesforce id and it is not a temporary id, or if this is pending.
448
		// 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.
449
		if ( substr( $data['salesforce_id'], 0, 7 ) !== 'tmp_sf_' || ( isset( $data['action'] ) && 'pending' === $data['action'] ) ) {
450
			unset( $data['action'] );
451
			$insert = $this->wpdb->insert( $this->object_map_table, $data );
452
		} else {
453
			$status = 'error';
454
			if ( isset( $this->logging ) ) {
455
				$logging = $this->logging;
456
			} elseif ( class_exists( 'Object_Sync_Sf_Logging' ) ) {
457
				$logging = new Object_Sync_Sf_Logging( $this->wpdb, $this->version );
458
			}
459
			$logging->setup(
460
				sprintf(
461
					// translators: %1$s is the log status, %2$s is the name of a WordPress object. %3$s is the id of that object.
462
					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' ),
463
					ucfirst( esc_attr( $status ) ),
464
					esc_attr( $data['wordpress_object'] ),
465
					absint( $data['wordpress_id'] )
466
				),
467
				'',
468
				0,
469
				0,
470
				$status
471
			);
472
			return false;
473
		}
474
		if ( 1 === $insert ) {
475
			return $this->wpdb->insert_id;
476
		} elseif ( false !== strpos( $this->wpdb->last_error, 'Duplicate entry' ) ) {
477
			// this error should never happen now, I think. But let's watch and see.
478
			$mapping = $this->load_all_by_salesforce( $data['salesforce_id'] )[0];
479
			$id      = $mapping['id'];
480
			$status  = 'error';
481
			if ( isset( $this->logging ) ) {
482
				$logging = $this->logging;
483
			} elseif ( class_exists( 'Object_Sync_Sf_Logging' ) ) {
484
				$logging = new Object_Sync_Sf_Logging( $this->wpdb, $this->version );
485
			}
486
			$logging->setup(
487
				sprintf(
488
					// 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.
489
					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' ),
490
					ucfirst( esc_attr( $status ) ),
491
					esc_attr( $data['salesforce_id'] ),
492
					absint( $id )
493
				),
494
				print_r( $mapping, true ), // log whatever we have for the mapping object, so print the array
495
				0,
496
				0,
497
				$status
498
			);
499
			return $id;
500
		} else {
501
			return false;
502
		}
503
	}
504
505
	/**
506
	 * Get all object map rows between WordPress and Salesforce objects.
507
	 *
508
	 * This replaces previous functionality that would return a single object map if there was only one, rather than a multi-dimensional array.
509
	 *
510
	 * @param array $conditions Limitations on the SQL query for object mapping rows.
511
	 * @param bool $reset Unused parameter.
512
	 * @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...
513
	 */
514
	public function get_all_object_maps( $conditions = array(), $reset = false ) {
0 ignored issues
show
Unused Code introduced by
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

514
	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...
515
		$table = $this->object_map_table;
516
		$order = ' ORDER BY object_updated, created';
517
		if ( ! empty( $conditions ) ) { // get multiple but with a limitation.
518
			$mappings = array();
519
520
			if ( ! empty( $conditions ) ) {
521
				$where = ' WHERE ';
522
				$i     = 0;
523
				foreach ( $conditions as $key => $value ) {
524
					$i++;
525
					if ( $i > 1 ) {
526
						$where .= ' AND ';
527
					}
528
					$where .= '`' . $key . '` = "' . $value . '"';
529
				}
530
			} else {
531
				$where = '';
532
			}
533
534
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . $order, ARRAY_A );
535
		} else { // get all of the mappings. ALL THE MAPPINGS.
536
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $order, ARRAY_A );
537
		}
538
539
		return $mappings;
540
541
	}
542
543
	/**
544
	 * Get one or more object map rows between WordPress and Salesforce objects
545
	 *
546
	 * @deprecated since 1.8.0
547
	 * @param array $conditions Limitations on the SQL query for object mapping rows.
548
	 * @param bool  $reset Unused parameter.
549
	 * @return array $map or $mappings
550
	 * @throws \Exception
551
	 */
552
	public function get_object_maps( $conditions = array(), $reset = false ) {
0 ignored issues
show
Unused Code introduced by
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

552
	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...
553
		$table = $this->object_map_table;
554
		$order = ' ORDER BY object_updated, created';
555
		if ( ! empty( $conditions ) ) { // get multiple but with a limitation.
556
			$mappings = array();
557
558
			if ( ! empty( $conditions ) ) {
559
				$where = ' WHERE ';
560
				$i     = 0;
561
				foreach ( $conditions as $key => $value ) {
562
					$i++;
563
					if ( $i > 1 ) {
564
						$where .= ' AND ';
565
					}
566
					$where .= '`' . $key . '` = "' . $value . '"';
567
				}
568
			} else {
569
				$where = '';
570
			}
571
572
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $where . $order, ARRAY_A );
573
			if ( ! empty( $mappings ) && 1 === $this->wpdb->num_rows ) {
574
				$mappings = $mappings[0];
575
			}
576
		} else { // get all of the mappings. ALL THE MAPPINGS.
577
			$mappings = $this->wpdb->get_results( 'SELECT * FROM ' . $table . $order, ARRAY_A );
578
			if ( ! empty( $mappings ) && 1 === $this->wpdb->num_rows ) {
579
				$mappings = $mappings[0];
580
			}
581
		}
582
583
		return $mappings;
584
585
	}
586
587
	/**
588
	 * Update an object map row between a WordPress and Salesforce object
589
	 *
590
	 * @param array $posted It's $_POST.
591
	 * @param array $id The ID of the object map row.
592
	 * @return boolean
593
	 * @throws \Exception
594
	 */
595
	public function update_object_map( $posted = array(), $id = '' ) {
596
		$data = $this->setup_object_map_data( $posted );
597
		if ( ! isset( $data['object_updated'] ) ) {
598
			$data['object_updated'] = current_time( 'mysql' );
599
		}
600
		$update = $this->wpdb->update(
601
			$this->object_map_table,
602
			$data,
603
			array(
604
				'id' => $id,
605
			)
606
		);
607
		if ( false === $update ) {
608
			return false;
609
		} else {
610
			return true;
611
		}
612
	}
613
614
	/**
615
	 * Setup the data for the object map
616
	 *
617
	 * @param array $posted It's $_POST.
618
	 * @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.
619
	 */
620
	private function setup_object_map_data( $posted = array() ) {
621
		$allowed_fields   = $this->wpdb->get_col( "DESC {$this->object_map_table}", 0 );
622
		$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
623
624
		$data = array_intersect_key( $posted, array_flip( $allowed_fields ) );
625
		return $data;
626
	}
627
628
	/**
629
	 * Delete an object map row between a WordPress and Salesforce object
630
	 *
631
	 * @param int|array $id The ID or IDs of the object map row(s).
632
	 * @return boolean
633
	 * @throws \Exception
634
	 */
635
	public function delete_object_map( $id = '' ) {
636
		if ( is_string( $id ) || is_int( $id ) ) {
637
			$data   = array(
638
				'id' => $id,
639
			);
640
			$delete = $this->wpdb->delete( $this->object_map_table, $data );
641
			if ( 1 === $delete ) {
642
				return true;
643
			} else {
644
				return false;
645
			}
646
		} elseif ( is_array( $id ) ) {
0 ignored issues
show
introduced by
The condition is_array($id) is always true.
Loading history...
647
			$ids    = implode( ',', array_map( 'absint', $id ) );
648
			$delete = $this->wpdb->query( "DELETE FROM $this->object_map_table WHERE ID IN ($ids)" );
649
			if ( false !== $delete ) {
650
				return true;
651
			} else {
652
				return false;
653
			}
654
		}
655
	}
656
657
	/**
658
	 * Generate a temporary ID to store while waiting for a push or pull to complete, before the record has been assigned a new ID
659
	 *
660
	 * @param string $direction Whether this is part of a push or pull action
661
	 * @return string $id is a temporary string that will be replaced if the modification is successful
662
	 */
663
	public function generate_temporary_id( $direction ) {
664
		if ( 'push' === $direction ) {
665
			$prefix = 'tmp_sf_';
666
		} elseif ( 'pull' === $direction ) {
667
			$prefix = 'tmp_wp_';
668
		}
669
		$id = uniqid( $prefix, true );
670
		return $id;
671
	}
672
673
	/**
674
	 * Returns Salesforce object mappings for a given WordPress object.
675
	 *
676
	 * @param string $object_type Type of object to load.
677
	 * @param int    $object_id Unique identifier of the target object to load.
678
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
679
	 *
680
	 * @return SalesforceMappingObject
0 ignored issues
show
Bug introduced by
The type SalesforceMappingObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
681
	 *   The requested SalesforceMappingObject or FALSE if none was found.
682
	 */
683
	public function load_all_by_wordpress( $object_type, $object_id, $reset = false ) {
684
		$conditions = array(
685
			'wordpress_id'     => $object_id,
686
			'wordpress_object' => $object_type,
687
		);
688
		return $this->get_all_object_maps( $conditions, $reset );
689
	}
690
691
	/**
692
	 * Returns one or more Salesforce object mappings for a given WordPress object.
693
	 *
694
	 * @deprecated since 1.8.0
695
	 * @param string $object_type Type of object to load.
696
	 * @param int    $object_id Unique identifier of the target object to load.
697
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
698
	 *
699
	 * @return SalesforceMappingObject
700
	 *   The requested SalesforceMappingObject or FALSE if none was found.
701
	 */
702
	public function load_by_wordpress( $object_type, $object_id, $reset = false ) {
703
		$conditions = array(
704
			'wordpress_id'     => $object_id,
705
			'wordpress_object' => $object_type,
706
		);
707
		return $this->get_object_maps( $conditions, $reset );
0 ignored issues
show
Deprecated Code introduced by
The function Object_Sync_Sf_Mapping::get_object_maps() has been deprecated: since 1.8.0 ( Ignorable by Annotation )

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

707
		return /** @scrutinizer ignore-deprecated */ $this->get_object_maps( $conditions, $reset );

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
708
	}
709
710
	/**
711
	 * Returns Salesforce object mappings for a given Salesforce object.
712
	 *
713
	 * @param string $salesforce_id Type of object to load.
714
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
715
	 *
716
	 * @return array $maps all the object maps that match the Salesforce Id
717
	 */
718
	public function load_all_by_salesforce( $salesforce_id, $reset = false ) {
719
		$conditions = array(
720
			'salesforce_id' => $salesforce_id,
721
		);
722
723
		$maps = $this->get_all_object_maps( $conditions, $reset );
724
725
		return $maps;
726
	}
727
728
	/**
729
	 * Returns one or more Salesforce object mappings for a given Salesforce object.
730
	 *
731
	 * @deprecated since 1.8.0
732
	 * @param string $salesforce_id Type of object to load.
733
	 * @param bool   $reset Whether or not the cache should be cleared and fetch from current data.
734
	 *
735
	 * @return array $map
736
	 *   The most recent fieldmap
737
	 */
738
	public function load_by_salesforce( $salesforce_id, $reset = false ) {
739
		$conditions = array(
740
			'salesforce_id' => $salesforce_id,
741
		);
742
743
		$map = $this->get_object_maps( $conditions, $reset );
0 ignored issues
show
Deprecated Code introduced by
The function Object_Sync_Sf_Mapping::get_object_maps() has been deprecated: since 1.8.0 ( Ignorable by Annotation )

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

743
		$map = /** @scrutinizer ignore-deprecated */ $this->get_object_maps( $conditions, $reset );

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
744
745
		if ( isset( $map[0] ) && is_array( $map[0] ) && count( $map ) > 1 ) {
746
			$status = 'notice';
747
			$log    = '';
748
			$log   .= 'Mapping: there is more than one mapped WordPress object for the Salesforce object ' . $salesforce_id . '. These WordPress IDs are: ';
749
			$i      = 0;
750
			foreach ( $map as $mapping ) {
751
				$i++;
752
				if ( isset( $mapping['wordpress_id'] ) ) {
753
					$log .= 'object type: ' . $mapping['wordpress_object'] . ', id: ' . $mapping['wordpress_id'];
754
				}
755
				if ( count( $map ) !== $i ) {
756
					$log .= '; ';
757
				} else {
758
					$log .= '.';
759
				}
760
			}
761
			$map = $map[0];
762
			// Create log entry for multiple maps.
763
			if ( isset( $this->logging ) ) {
764
				$logging = $this->logging;
765
			} elseif ( class_exists( 'Object_Sync_Sf_Logging' ) ) {
766
				$logging = new Object_Sync_Sf_Logging( $this->wpdb, $this->version );
767
			}
768
			$logging->setup(
769
				sprintf(
770
					// translators: %1$s is the Id of a Salesforce object.
771
					esc_html__( 'Notice: Mapping: there is more than one mapped WordPress object for the Salesforce object %2$s', 'object-sync-for-salesforce' ),
772
					esc_attr( $salesforce_id )
773
				),
774
				$log,
775
				0,
776
				0,
777
				$status
778
			);
779
		} // End if().
780
781
		return $map;
782
	}
783
784
	/**
785
	 * Map values between WordPress and Salesforce objects.
786
	 *
787
	 * @param array  $mapping The fieldmap that maps these types together.
788
	 * @param array  $object WordPress or Salesforce object data.
789
	 * @param array  $trigger The thing that triggered this mapping.
790
	 * @param bool   $use_soap Flag to enforce use of the SOAP API.
791
	 * @param bool   $is_new Indicates whether a mapping object for this entity already exists.
792
	 * @param string $object_id_field optionally pass the object id field name
793
	 *
794
	 * @return array Associative array of key value pairs.
795
	 */
796
	public function map_params( $mapping, $object, $trigger, $use_soap = false, $is_new = true, $object_id_field = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $is_new 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

796
	public function map_params( $mapping, $object, $trigger, $use_soap = false, /** @scrutinizer ignore-unused */ $is_new = true, $object_id_field = '' ) {

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...
797
798
		$params = array();
799
800
		// these are the triggers that define whether the action was from WordPress or Salesforce.
801
		$wordpress_haystack  = array_values( $this->wordpress_events );
802
		$salesforce_haystack = array_values( $this->salesforce_events );
803
804
		$has_missing_required_salesforce_field = false;
805
		foreach ( $mapping['fields'] as $fieldmap ) {
806
807
			$fieldmap['wordpress_field']['methods'] = maybe_unserialize( $fieldmap['wordpress_field']['methods'] );
808
809
			$wordpress_field = $fieldmap['wordpress_field']['label'];
810
811
			if ( version_compare( $this->user_installed_version, '1.2.0', '>=' ) && isset( $fieldmap['salesforce_field']['name'] ) ) {
812
				$salesforce_field = $fieldmap['salesforce_field']['name'];
813
				// 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.
814
				$salesforce_field_type = $fieldmap['salesforce_field']['type'];
815
			} else {
816
				$salesforce_field = $fieldmap['salesforce_field']['label'];
817
			}
818
819
			// A WordPress event caused this.
820
			if ( in_array( $trigger, array_values( $wordpress_haystack ), true ) ) {
821
822
				// Is the field in WordPress an array, if we unserialize it? Salesforce wants it to be an imploded string.
823
				if ( is_array( maybe_unserialize( $object[ $wordpress_field ] ) ) ) {
824
					// 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.
825
					if ( 'wp_capabilities' === $wordpress_field ) {
826
						$object[ $wordpress_field ] = implode( $this->array_delimiter, array_keys( $object[ $wordpress_field ] ) );
827
					} else {
828
						$object[ $wordpress_field ] = implode( $this->array_delimiter, $object[ $wordpress_field ] );
829
					}
830
				}
831
832
				if ( isset( $salesforce_field_type ) ) {
833
					// Is the Salesforce field a date, and is the WordPress value a valid date?
834
					// According to https://salesforce.stackexchange.com/questions/57032/date-format-with-salesforce-rest-api
835
					if ( in_array( $salesforce_field_type, $this->date_types_from_salesforce, true ) ) {
836
						if ( '' === $object[ $wordpress_field ] ) {
837
							$object[ $wordpress_field ] = null;
838
						} else {
839
							if ( false !== strtotime( $object[ $wordpress_field ] ) ) {
840
								$timestamp = strtotime( $object[ $wordpress_field ] );
841
							} else {
842
								$timestamp = $object[ $wordpress_field ];
843
							}
844
							if ( 'datetime' === $salesforce_field_type ) {
845
								$object[ $wordpress_field ] = date_i18n( 'c', $timestamp );
846
							} else {
847
								$object[ $wordpress_field ] = date_i18n( 'Y-m-d', $timestamp );
848
							}
849
						}
850
					}
851
852
					// Boolean SF fields only want real boolean values. NULL is also not allowed.
853
					if ( 'boolean' === $salesforce_field_type ) {
854
						$object[ $wordpress_field ] = (bool) $object[ $wordpress_field ];
855
					}
856
				}
857
858
				$params[ $salesforce_field ] = $object[ $wordpress_field ];
859
860
				// If the field is a key in Salesforce, remove it from $params to avoid upsert errors from Salesforce,
861
				// but still put its name in the params array so we can check for it later.
862
				if ( '1' === $fieldmap['is_key'] ) {
863
					if ( ! $use_soap ) {
864
						unset( $params[ $salesforce_field ] );
865
					}
866
					$params['key'] = array(
867
						'salesforce_field' => $salesforce_field,
868
						'wordpress_field'  => $wordpress_field,
869
						'value'            => $object[ $wordpress_field ],
870
					);
871
				}
872
873
				// If the field is a prematch in Salesforce, put its name in the params array so we can check for it later.
874
				if ( '1' === $fieldmap['is_prematch'] ) {
875
					$params['prematch'] = array(
876
						'salesforce_field' => $salesforce_field,
877
						'wordpress_field'  => $wordpress_field,
878
						'value'            => $object[ $wordpress_field ],
879
					);
880
				}
881
882
				// Skip fields that aren't being pushed to Salesforce.
883
				if ( ! in_array( $fieldmap['direction'], array_values( $this->direction_wordpress ), true ) ) {
884
					// The trigger is a WordPress trigger, but the fieldmap direction is not a WordPress direction.
885
					unset( $params[ $salesforce_field ] );
886
				}
887
888
				// I think it's good to over-mention that updateable is really how the Salesforce api spells it.
889
				// Skip fields that aren't updateable when mapping params because Salesforce will error otherwise.
890
				// 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.
891
				if ( 1 !== (int) $fieldmap['salesforce_field']['updateable'] ) {
892
					unset( $params[ $salesforce_field ] );
893
				}
894
895
				// This case means the following:
896
				//    this field is expected by the fieldmap
897
				//    Salesforce's api reports that this field is required
898
				//    we do not have a WordPress value for this field, or it's empty
899
				//    it also means the field has not been unset by prematch, updateable, key, or directional flags prior to this check.
900
				// When this happens, we should flag that we're missing a required Salesforce field
901
				if ( in_array( $salesforce_field, $params, true ) && false === filter_var( $fieldmap['salesforce_field']['nillable'], FILTER_VALIDATE_BOOLEAN ) && ( ! isset( $object[ $wordpress_field ] ) || '' === $object[ $wordpress_field ] ) ) {
902
					$has_missing_required_salesforce_field = true;
903
				}
904
905
				// we don't need a continue with the unset methods because there's no array being created down here
906
			} elseif ( in_array( $trigger, $salesforce_haystack, true ) ) {
907
908
				// A Salesforce event caused this.
909
910
				if ( isset( $salesforce_field_type ) && isset( $object[ $salesforce_field ] ) && ! is_null( $object[ $salesforce_field ] ) ) {
911
					// Salesforce provides multipicklist values as a delimited string. If the
912
					// destination field in WordPress accepts multiple values, explode the string into an array and then serialize it.
913
					if ( in_array( $salesforce_field_type, $this->array_types_from_salesforce, true ) ) {
914
						$object[ $salesforce_field ] = explode( $this->array_delimiter, $object[ $salesforce_field ] );
915
						// 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.
916
						if ( 'wp_capabilities' === $wordpress_field ) {
917
							$capabilities = array();
918
							foreach ( $object[ $salesforce_field ] as $capability ) {
919
								$capabilities[ $capability ] = true;
920
							}
921
							$object[ $salesforce_field ] = $capabilities;
922
						}
923
					}
924
925
					// Handle specific data types from Salesforce.
926
					switch ( $salesforce_field_type ) {
927
						case ( in_array( $salesforce_field_type, $this->date_types_from_salesforce, true ) ):
928
							$format = get_option( 'date_format', 'U' );
929
							if ( isset( $fieldmap['wordpress_field']['type'] ) && 'datetime' === $fieldmap['wordpress_field']['type'] ) {
930
								$format = 'Y-m-d H:i:s';
931
							}
932
							if ( 'tribe_events' === $mapping['wordpress_object'] && class_exists( 'Tribe__Events__Main' ) ) {
933
								$format = 'Y-m-d H:i:s';
934
							}
935
							if ( 'datetime' === $salesforce_field_type ) {
936
								// 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.
937
								// 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.
938
								$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.
939
							}
940
							$object[ $salesforce_field ] = date_i18n( $format, strtotime( $object[ $salesforce_field ] ) );
941
							break;
942
						case ( in_array( $salesforce_field_type, $this->int_types_from_salesforce, true ) ):
943
							$object[ $salesforce_field ] = isset( $object[ $salesforce_field ] ) ? (int) $object[ $salesforce_field ] : 0;
944
							break;
945
						case 'text':
946
							$object[ $salesforce_field ] = (string) $object[ $salesforce_field ];
947
							break;
948
						case 'url':
949
							$object[ $salesforce_field ] = esc_url_raw( $object[ $salesforce_field ] );
950
							break;
951
					} // end switch
952
953
					// set a default WordPress status value based on the fieldmap
954
					$params['default_status'] = $mapping['wordpress_object_default_status'];
955
				} // end if for Salesforce event
956
957
				// Make an array because we need to store the methods for each field as well.
958
				if ( isset( $object[ $salesforce_field ] ) ) {
959
					$params[ $wordpress_field ]          = array();
960
					$params[ $wordpress_field ]['value'] = $object[ $salesforce_field ];
961
				} elseif ( is_null( $object[ $salesforce_field ] ) ) {
962
					// Salesforce returns blank fields as null fields; set them to blank
963
					$params[ $wordpress_field ]          = array();
964
					$params[ $wordpress_field ]['value'] = '';
965
				} else {
966
					// prevent fields that don't exist from being passed
967
					continue;
968
				}
969
970
				// 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.
971
972
				// If the field is a prematch in Salesforce, put its name in the params array so we can check for it later.
973
				if ( '1' === $fieldmap['is_prematch'] ) {
974
					$params['prematch'] = array(
975
						'salesforce_field' => $salesforce_field,
976
						'wordpress_field'  => $wordpress_field,
977
						'value'            => $object[ $salesforce_field ],
978
						'method_match'     => isset( $fieldmap['wordpress_field']['methods']['match'] ) ? $fieldmap['wordpress_field']['methods']['match'] : $fieldmap['wordpress_field']['methods']['read'],
979
						'method_read'      => $fieldmap['wordpress_field']['methods']['read'],
980
						'method_create'    => $fieldmap['wordpress_field']['methods']['create'],
981
						'method_update'    => $fieldmap['wordpress_field']['methods']['update'],
982
					);
983
				}
984
985
				// Skip fields that aren't being pulled from Salesforce.
986
				if ( ! in_array( $fieldmap['direction'], array_values( $this->direction_salesforce ), true ) ) {
987
					// The trigger is a Salesforce trigger, but the fieldmap direction is not a Salesforce direction.
988
					unset( $params[ $wordpress_field ] );
989
					// we also need to continue here, so it doesn't create an empty array below for fields that are WordPress -> Salesforce only
990
					continue;
991
				}
992
993
				switch ( $trigger ) {
994
					case $this->sync_sf_create:
995
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['create'];
996
						break;
997
					case $this->sync_sf_update:
998
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['update'];
999
						break;
1000
					case $this->sync_sf_delete:
1001
						$params[ $wordpress_field ]['method_modify'] = $fieldmap['wordpress_field']['methods']['delete'];
1002
						break;
1003
				}
1004
1005
				// always allow for the delete and read methods
1006
				$params[ $wordpress_field ]['method_delete'] = $fieldmap['wordpress_field']['methods']['delete'];
1007
				$params[ $wordpress_field ]['method_read']   = $fieldmap['wordpress_field']['methods']['read'];
1008
1009
			} // End if().
1010
		} // End foreach().
1011
1012
		if ( true === $has_missing_required_salesforce_field ) {
1013
			update_option( $this->option_prefix . 'missing_required_data_id_' . $object[ $object_id_field ], true, false );
1014
			return array();
1015
		}
1016
1017
		return $params;
1018
1019
	}
1020
1021
	/**
1022
	 * Prepare field map data for use
1023
	 *
1024
	 * @param array  $mappings Array of fieldmaps.
1025
	 * @param string $record_type Optional Salesforce record type to see if it is allowed or not.
1026
	 *
1027
	 * @return array $mappings Associative array of field maps ready to use
1028
	 */
1029
	private function prepare_fieldmap_data( $mappings, $record_type = '' ) {
1030
1031
		foreach ( $mappings as $id => $mapping ) {
1032
			$mappings[ $id ]['salesforce_record_types_allowed'] = maybe_unserialize( $mapping['salesforce_record_types_allowed'] );
1033
			$mappings[ $id ]['fields']                          = maybe_unserialize( $mapping['fields'] );
1034
			$mappings[ $id ]['sync_triggers']                   = maybe_unserialize( $mapping['sync_triggers'] );
1035
			// reverse async to get immediately
1036
			if ( true === filter_var( $mappings[ $id ]['push_async'], FILTER_VALIDATE_BOOLEAN ) ) {
1037
				$mappings[ $id ]['push_immediately'] = '';
1038
			} else {
1039
				$mappings[ $id ]['push_immediately'] = '1';
1040
			}
1041
			if ( '' !== $record_type && ! in_array( $record_type, $mappings[ $id ]['salesforce_record_types_allowed'], true ) ) {
1042
				unset( $mappings[ $id ] );
1043
			}
1044
		}
1045
1046
		return $mappings;
1047
1048
	}
1049
1050
	/**
1051
	 * Check object map table to see if there have been any failed object map create attempts
1052
	 *
1053
	 * @return array $errors Associative array of rows that failed to finish from either system
1054
	 */
1055
	public function get_failed_object_maps() {
1056
		$table                 = $this->object_map_table;
1057
		$errors                = array();
1058
		$items_per_page        = (int) get_option( $this->option_prefix . 'errors_per_page', 50 );
1059
		$current_error_page    = isset( $_GET['error_page'] ) ? (int) $_GET['error_page'] : 1;
1060
		$offset                = ( $current_error_page * $items_per_page ) - $items_per_page;
1061
		$all_errors            = $this->wpdb->get_results( "SELECT * FROM ${table} WHERE salesforce_id LIKE 'tmp_sf_%' OR wordpress_id LIKE 'tmp_wp_%' LIMIT ${offset}, ${items_per_page}", ARRAY_A );
1062
		$errors_total          = $this->wpdb->get_var( "SELECT COUNT(`id`) FROM ${table} WHERE salesforce_id LIKE 'tmp_sf_%' OR wordpress_id LIKE 'tmp_wp_%'" );
1063
		$errors['total_pages'] = ceil( $errors_total / $items_per_page );
1064
		$errors['pagination']  = paginate_links(
1065
			array(
1066
				'base'      => add_query_arg( 'error_page', '%#%' ),
1067
				'format'    => '',
1068
				'total'     => $errors['total_pages'],
1069
				'prev_text' => __( '&laquo;', 'text-domain' ),
1070
				'next_text' => __( '&raquo;', 'text-domain' ),
1071
				'current'   => $current_error_page,
1072
			)
1073
		);
1074
		$errors['error_page']  = $current_error_page;
1075
		$errors['all_errors']  = $all_errors;
1076
		$errors['total']       = $errors_total;
1077
		return $errors;
1078
	}
1079
1080
	/**
1081
	 * Check object map table to see if there have been any failed object map create attempts
1082
	 *
1083
	 * @param int   $id The ID of a desired mapping.
1084
	 *
1085
	 * @return array $error Associative array of single row that failed to finish based on id
1086
	 */
1087
	public function get_failed_object_map( $id ) {
1088
		$table     = $this->object_map_table;
1089
		$error     = array();
1090
		$error_row = $this->wpdb->get_row( 'SELECT * FROM ' . $table . ' WHERE id = "' . $id . '"', ARRAY_A );
1091
		if ( ! empty( $error_row ) ) {
1092
			$error = $error_row;
1093
		}
1094
		return $error;
1095
	}
1096
1097
}
1098