Passed
Push — 273-when-soap-is-missing ( f058f6...47fe74 )
by Jonathan
03:37
created

Object_Sync_Sf_Admin::delete_fieldmap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 0
dl 0
loc 11
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * Class file for the Object_Sync_Sf_Admin class.
4
 *
5
 * @file
6
 */
7
8
if ( ! class_exists( 'Object_Sync_Salesforce' ) ) {
9
	die();
10
}
11
12
/**
13
 * Create default WordPress admin functionality to configure the plugin.
14
 */
15
class Object_Sync_Sf_Admin {
16
17
	protected $wpdb;
18
	protected $version;
19
	protected $login_credentials;
20
	protected $slug;
21
	protected $salesforce;
22
	protected $wordpress;
23
	protected $mappings;
24
	protected $push;
25
	protected $pull;
26
	protected $logging;
27
	protected $schedulable_classes;
28
	protected $queue;
29
	protected $option_prefix;
30
31
	private $sfwp_transients;
32
33
	private $access_token;
34
	private $instance_url;
35
	private $refresh_token;
36
37
	/**
38
	* @var string
39
	* Default path for the Salesforce authorize URL
40
	*/
41
	public $default_authorize_url_path;
42
43
	/**
44
	* @var string
45
	* Default path for the Salesforce token URL
46
	*/
47
	public $default_token_url_path;
48
49
	/**
50
	* @var string
51
	* What version of the Salesforce API should be the default on the settings screen.
52
	* Users can edit this, but they won't see a correct list of all their available versions until WordPress has
53
	* been authenticated with Salesforce.
54
	*/
55
	public $default_api_version;
56
57
	/**
58
	* @var int
59
	* Default max number of pull records
60
	* Users can edit this
61
	*/
62
	public $default_pull_limit;
63
64
	/**
65
	* @var int
66
	* Default pull throttle for how often Salesforce can pull
67
	* Users can edit this
68
	*/
69
	public $default_pull_throttle;
70
71
	/**
72
	* @var bool
73
	* Default for whether to limit to triggerable items
74
	* Users can edit this
75
	*/
76
	public $default_triggerable;
77
78
	/**
79
	* @var bool
80
	* Default for whether to limit to updateable items
81
	* Users can edit this
82
	*/
83
	public $default_updateable;
84
85
	/**
86
	* @var string
87
	* Suffix for action group name
88
	*/
89
	public $action_group_suffix;
90
91
	/**
92
	* Constructor which sets up admin pages
93
	*
94
	* @param object $wpdb
95
	* @param string $version
96
	* @param array $login_credentials
97
	* @param string $slug
98
	* @param object $wordpress
99
	* @param object $salesforce
100
	* @param object $mappings
101
	* @param object $push
102
	* @param object $pull
103
	* @param object $logging
104
	* @param array $schedulable_classes
105
	* @param object $queue
106
	* @throws \Exception
107
	*/
108
	public function __construct( $wpdb, $version, $login_credentials, $slug, $wordpress, $salesforce, $mappings, $push, $pull, $logging, $schedulable_classes, $queue = '', $option_prefix = '' ) {
109
		$this->wpdb                = $wpdb;
110
		$this->version             = $version;
111
		$this->login_credentials   = $login_credentials;
112
		$this->slug                = $slug;
113
		$this->option_prefix       = isset( $option_prefix ) ? $option_prefix : 'object_sync_for_salesforce_';
114
		$this->wordpress           = $wordpress;
115
		$this->salesforce          = $salesforce;
116
		$this->mappings            = $mappings;
117
		$this->push                = $push;
118
		$this->pull                = $pull;
119
		$this->logging             = $logging;
120
		$this->schedulable_classes = $schedulable_classes;
121
		$this->queue               = $queue;
122
123
		$this->sfwp_transients = $this->wordpress->sfwp_transients;
124
125
		// default authorize url path
126
		$this->default_authorize_url_path = '/services/oauth2/authorize';
127
		// default token url path
128
		$this->default_token_url_path = '/services/oauth2/token';
129
		// what Salesforce API version to start the settings with. This is only used in the settings form
130
		$this->default_api_version = '46.0';
131
		// default pull record limit
132
		$this->default_pull_limit = 25;
133
		// default pull throttle for avoiding going over api limits
134
		$this->default_pull_throttle = 5;
135
		// default setting for triggerable items
136
		$this->default_triggerable = true;
137
		// default setting for updateable items
138
		$this->default_updateable = true;
139
		// suffix for action groups
140
		$this->action_group_suffix = '_check_records';
141
142
		$this->add_actions();
143
144
	}
145
146
	/**
147
	* Create the action hooks to create the admin page(s)
148
	*
149
	*/
150
	public function add_actions() {
151
		add_action( 'admin_init', array( $this, 'salesforce_settings_forms' ) );
152
		add_action( 'admin_init', array( $this, 'notices' ) );
153
		add_action( 'admin_post_post_fieldmap', array( $this, 'prepare_fieldmap_data' ) );
154
155
		add_action( 'admin_post_delete_fieldmap', array( $this, 'delete_fieldmap' ) );
156
		add_action( 'wp_ajax_get_salesforce_object_description', array( $this, 'get_salesforce_object_description' ) );
157
		add_action( 'wp_ajax_get_wordpress_object_description', array( $this, 'get_wordpress_object_fields' ) );
158
		add_action( 'wp_ajax_get_wp_sf_object_fields', array( $this, 'get_wp_sf_object_fields' ) );
159
		add_action( 'wp_ajax_push_to_salesforce', array( $this, 'push_to_salesforce' ) );
160
		add_action( 'wp_ajax_pull_from_salesforce', array( $this, 'pull_from_salesforce' ) );
161
		add_action( 'wp_ajax_refresh_mapped_data', array( $this, 'refresh_mapped_data' ) );
162
		add_action( 'wp_ajax_clear_sfwp_cache', array( $this, 'clear_sfwp_cache' ) );
163
164
		add_action( 'edit_user_profile', array( $this, 'show_salesforce_user_fields' ) );
165
		add_action( 'personal_options_update', array( $this, 'save_salesforce_user_fields' ) );
166
		add_action( 'edit_user_profile_update', array( $this, 'save_salesforce_user_fields' ) );
167
168
		// when either field for schedule settings changes
169
		foreach ( $this->schedulable_classes as $key => $value ) {
170
			// if the user doesn't have any action schedule tasks, let's not leave them empty
171
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'initial_action_schedule' ), 10, 3 );
172
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'initial_action_schedule' ), 10, 3 );
173
174
			// this is if the user is changing their tasks
175
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'change_action_schedule' ), 10, 3 );
176
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'change_action_schedule' ), 10, 3 );
177
		}
178
179
		add_action( 'admin_post_delete_object_map', array( $this, 'delete_object_map' ) );
180
		add_action( 'admin_post_post_object_map', array( $this, 'prepare_object_map_data' ) );
181
182
		// import and export plugin data
183
		add_action( 'admin_post_object_sync_for_salesforce_import', array( $this, 'import_json_file' ) );
184
		add_action( 'admin_post_object_sync_for_salesforce_export', array( $this, 'export_json_file' ) );
185
186
	}
187
188
	/**
189
	* Set up recurring tasks if there are none
190
	*
191
	* @param string $new_schedule
192
	* @param string $old_schedule
193
	* @param string $option_name
194
	* @return string $new_schedule
195
	*
196
	*/
197
	public function initial_action_schedule( $new_schedule, $old_schedule, $option_name ) {
198
199
		// get the current schedule name from the task, based on pattern in the foreach
200
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
201
		$schedule_name     = $matches[1];
202
		$action_group_name = $schedule_name . $this->action_group_suffix;
203
204
		// make sure there are no tasks already
205
		$current_tasks = as_get_scheduled_actions(
206
			array(
207
				'hook'  => $this->schedulable_classes[ $schedule_name ]['initializer'],
208
				'group' => $action_group_name,
209
			),
210
			ARRAY_A
211
		);
212
213
		// exit if there are already tasks; they'll be saved if the option data changed
214
		if ( ! empty( $current_tasks ) ) {
215
			return $new_schedule;
216
		}
217
218
		$this->set_action_schedule( $schedule_name, $action_group_name );
219
220
		return $new_schedule;
221
222
	}
223
224
	/**
225
	* Change recurring tasks if options change
226
	*
227
	* @param string $old_schedule
228
	* @param string $new_schedule
229
	* @param string $option_name
230
	*
231
	*/
232
	public function change_action_schedule( $old_schedule, $new_schedule, $option_name ) {
233
234
		// this method does not run if the option's data is unchanged
235
236
		// get the current schedule name from the task, based on pattern in the foreach
237
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
238
		$schedule_name     = $matches[1];
239
		$action_group_name = $schedule_name . $this->action_group_suffix;
240
241
		$this->set_action_schedule( $schedule_name, $action_group_name );
242
243
	}
244
245
	/**
246
	* Set up recurring tasks
247
	*
248
	* @param string $schedule_name
249
	* @param string $action_group_name
250
	*
251
	*/
252
	private function set_action_schedule( $schedule_name, $action_group_name ) {
253
		// exit if there is no initializer property on this schedule
254
		if ( ! isset( $this->schedulable_classes[ $schedule_name ]['initializer'] ) ) {
255
			return;
256
		}
257
258
		// cancel previous task
259
		$this->queue->cancel(
260
			$this->schedulable_classes[ $schedule_name ]['initializer'],
261
			array(),
262
			$action_group_name
263
		);
264
265
		// create new recurring task for action-scheduler to check for data to pull from salesforce
266
		$this->queue->schedule_recurring(
267
			current_time( 'timestamp', true ), // plugin seems to expect UTC
268
			$this->queue->get_frequency( $schedule_name, 'seconds' ),
269
			$this->schedulable_classes[ $schedule_name ]['initializer'],
270
			array(),
271
			$action_group_name
272
		);
273
	}
274
275
	/**
276
	* Create WordPress admin options page
277
	*
278
	*/
279
	public function create_admin_menu() {
280
		$title = __( 'Salesforce', 'object-sync-for-salesforce' );
281
		add_options_page( $title, $title, 'configure_salesforce', 'object-sync-salesforce-admin', array( $this, 'show_admin_page' ) );
282
	}
283
284
	/**
285
	* Render full admin pages in WordPress
286
	* This allows other plugins to add tabs to the Salesforce settings screen
287
	*
288
	* todo: better front end: html, organization of html into templates, css, js
289
	*
290
	*/
291
	public function show_admin_page() {
292
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
293
		echo '<div class="wrap">';
294
		echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
295
		$allowed = $this->check_wordpress_admin_permissions();
296
		if ( false === $allowed ) {
297
			return;
298
		}
299
		$tabs = array(
300
			'settings'      => __( 'Settings', 'object-sync-for-salesforce' ),
301
			'authorize'     => __( 'Authorize', 'object-sync-for-salesforce' ),
302
			'fieldmaps'     => __( 'Fieldmaps', 'object-sync-for-salesforce' ),
303
			'schedule'      => __( 'Scheduling', 'object-sync-for-salesforce' ),
304
			'import-export' => __( 'Import &amp; Export', 'object-sync-for-salesforce' ),
305
		); // this creates the tabs for the admin
306
307
		// optionally make tab(s) for logging and log settings
308
		$logging_enabled      = get_option( $this->option_prefix . 'enable_logging', false );
309
		$tabs['log_settings'] = __( 'Log Settings', 'object-sync-for-salesforce' );
310
311
		$mapping_errors = $this->mappings->get_failed_object_maps();
312
		if ( ! empty( $mapping_errors ) ) {
313
			$tabs['mapping_errors'] = __( 'Mapping Errors', 'object-sync-for-salesforce' );
314
		}
315
316
		// filter for extending the tabs available on the page
317
		// currently it will go into the default switch case for $tab
318
		$tabs = apply_filters( $this->option_prefix . 'settings_tabs', $tabs );
319
320
		$tab = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
321
		$this->tabs( $tabs, $tab );
322
323
		$consumer_key    = $this->login_credentials['consumer_key'];
324
		$consumer_secret = $this->login_credentials['consumer_secret'];
325
		$callback_url    = $this->login_credentials['callback_url'];
326
327
		if ( true !== $this->salesforce['is_authorized'] ) {
328
			$url     = esc_url( $callback_url );
329
			$anchor  = esc_html__( 'Authorize tab', 'object-sync-for-salesforce' );
330
			$message = sprintf( 'Salesforce needs to be authorized to connect to this website. Use the <a href="%s">%s</a> to connect.', $url, $anchor );
331
			require( plugin_dir_path( __FILE__ ) . '/../templates/admin/error.php' );
332
		}
333
334
		if ( 0 === count( $this->mappings->get_fieldmaps() ) ) {
335
			$url     = esc_url( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=fieldmaps' ) );
336
			$anchor  = esc_html__( 'Fieldmaps tab', 'object-sync-for-salesforce' );
337
			$message = sprintf( 'No fieldmaps exist yet. Use the <a href="%s">%s</a> to map WordPress and Salesforce objects to each other.', $url, $anchor );
338
			require( plugin_dir_path( __FILE__ ) . '/../templates/admin/error.php' );
339
		}
340
341
		try {
342
			switch ( $tab ) {
343
				case 'authorize':
344
					if ( isset( $get_data['code'] ) ) {
345
						// this string is an oauth token
346
						$data          = esc_html( wp_unslash( $get_data['code'] ) );
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($get_data['code']) can also be of type array; however, parameter $text of esc_html() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

346
						$data          = esc_html( /** @scrutinizer ignore-type */ wp_unslash( $get_data['code'] ) );
Loading history...
347
						$is_authorized = $this->salesforce['sfapi']->request_token( $data );
348
						?>
349
						<script>window.location = '<?php echo esc_url_raw( $callback_url ); ?>'</script>
350
						<?php
351
					} elseif ( true === $this->salesforce['is_authorized'] ) {
352
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/authorized.php' );
353
							$this->status( $this->salesforce['sfapi'] );
354
					} elseif ( true === is_object( $this->salesforce['sfapi'] ) && isset( $consumer_key ) && isset( $consumer_secret ) ) {
355
						?>
356
						<p><a class="button button-primary" href="<?php echo esc_url( $this->salesforce['sfapi']->get_authorization_code() ); ?>"><?php echo esc_html__( 'Connect to Salesforce', 'object-sync-for-salesforce' ); ?></a></p>
357
						<?php
358
					} else {
359
						$url    = esc_url( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=settings' ) );
360
						$anchor = esc_html__( 'Settings', 'object-sync-for-salesforce' );
361
						// translators: placeholders are for the settings tab link: 1) the url, and 2) the anchor text
362
						$message = sprintf( esc_html__( 'Salesforce needs to be authorized to connect to this website but the credentials are missing. Use the <a href="%1$s">%2$s</a> tab to add them.', 'object-sync-salesforce' ), $url, $anchor );
363
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/error.php' );
364
					}
365
					break;
366
				case 'fieldmaps':
367
					if ( isset( $get_data['method'] ) ) {
368
369
						$method      = sanitize_key( $get_data['method'] );
370
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=fieldmaps&method=' . $method );
371
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=fieldmaps' );
372
373
						if ( isset( $get_data['transient'] ) ) {
374
							$transient = sanitize_key( $get_data['transient'] );
375
							$posted    = $this->sfwp_transients->get( $transient );
376
						}
377
378
						if ( isset( $posted ) && is_array( $posted ) ) {
379
							$map = $posted;
380
						} elseif ( 'edit' === $method || 'clone' === $method || 'delete' === $method ) {
381
							$map = $this->mappings->get_fieldmaps( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
382
						}
383
384
						if ( isset( $map ) && is_array( $map ) ) {
385
							$label                           = $map['label'];
386
							$salesforce_object               = $map['salesforce_object'];
387
							$salesforce_record_types_allowed = maybe_unserialize( $map['salesforce_record_types_allowed'] );
388
							$salesforce_record_type_default  = $map['salesforce_record_type_default'];
389
							$wordpress_object                = $map['wordpress_object'];
390
							$pull_trigger_field              = $map['pull_trigger_field'];
391
							$fieldmap_fields                 = $map['fields'];
392
							$sync_triggers                   = $map['sync_triggers'];
393
							$push_async                      = $map['push_async'];
394
							$push_drafts                     = $map['push_drafts'];
395
							$pull_to_drafts                  = $map['pull_to_drafts'];
396
							$weight                          = $map['weight'];
397
						}
398
399
						if ( 'add' === $method || 'edit' === $method || 'clone' === $method ) {
400
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-add-edit-clone.php' );
401
						} elseif ( 'delete' === $method ) {
402
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-delete.php' );
403
						}
404
					} else {
405
						$fieldmaps = $this->mappings->get_fieldmaps();
406
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-list.php' );
407
					} // End if().
408
					break;
409
				case 'logout':
410
					$this->logout();
411
					break;
412
				case 'clear_cache':
413
					$this->clear_cache();
414
					break;
415
				case 'clear_schedule':
416
					if ( isset( $get_data['schedule_name'] ) ) {
417
						$schedule_name = sanitize_key( $get_data['schedule_name'] );
418
					}
419
					$this->clear_schedule( $schedule_name );
420
					break;
421
				case 'settings':
422
					require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/settings.php' );
423
					break;
424
				case 'mapping_errors':
425
					if ( isset( $get_data['method'] ) ) {
426
427
						$method      = sanitize_key( $get_data['method'] );
428
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors&method=' . $method );
429
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors' );
430
431
						if ( isset( $get_data['map_transient'] ) ) {
432
							$transient = sanitize_key( $get_data['map_transient'] );
433
							$posted    = $this->sfwp_transients->get( $transient );
434
						}
435
436
						if ( isset( $posted ) && is_array( $posted ) ) {
437
							$map_row = $posted;
438
						} elseif ( 'edit' === $method || 'delete' === $method ) {
439
							$map_row = $this->mappings->get_failed_object_map( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
440
						}
441
442
						if ( isset( $map_row ) && is_array( $map_row ) ) {
443
							$salesforce_id = $map_row['salesforce_id'];
444
							$wordpress_id  = $map_row['wordpress_id'];
445
						}
446
447
						if ( 'edit' === $method ) {
448
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors-edit.php' );
449
						} elseif ( 'delete' === $method ) {
450
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors-delete.php' );
451
						}
452
					} else {
453
454
						if ( isset( $get_data['mapping_error_transient'] ) ) {
455
							$transient = sanitize_key( $get_data['mapping_error_transient'] );
456
							$posted    = $this->sfwp_transients->get( $transient );
457
						}
458
459
						$ids_string = '';
460
						$ids        = array();
461
						if ( isset( $posted['delete'] ) ) {
462
							$ids_string = maybe_serialize( $posted['delete'] );
463
							$ids        = $posted['delete'];
464
						}
465
466
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors&ids=' . $ids_string );
467
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors' );
468
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors.php' );
469
					}
470
					break;
471
				case 'import-export':
472
					require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/import-export.php' );
473
					break;
474
				default:
475
					$include_settings = apply_filters( $this->option_prefix . 'settings_tab_include_settings', true, $tab );
476
					$content_before   = apply_filters( $this->option_prefix . 'settings_tab_content_before', null, $tab );
477
					$content_after    = apply_filters( $this->option_prefix . 'settings_tab_content_after', null, $tab );
478
					if ( null !== $content_before ) {
479
						echo esc_html( $content_before );
480
					}
481
					if ( true === $include_settings ) {
482
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/settings.php' );
483
					}
484
					if ( null !== $content_after ) {
485
						echo esc_html( $content_after );
486
					}
487
					break;
488
			} // End switch().
489
		} catch ( SalesforceApiException $ex ) {
0 ignored issues
show
Bug introduced by
The type SalesforceApiException 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...
490
			echo sprintf( '<p>Error <strong>%1$s</strong>: %2$s</p>',
491
				absint( $ex->getCode() ),
492
				esc_html( $ex->getMessage() )
493
			);
494
		} catch ( Exception $ex ) {
495
			echo sprintf( '<p>Error <strong>%1$s</strong>: %2$s</p>',
496
				absint( $ex->getCode() ),
497
				esc_html( $ex->getMessage() )
498
			);
499
		} // End try().
500
		echo '</div>';
501
	}
502
503
	/**
504
	* Create default WordPress admin settings form for salesforce
505
	* This is for the Settings page/tab
506
	*
507
	*/
508
	public function salesforce_settings_forms() {
509
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
510
		$page     = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
511
		$section  = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
512
513
		$input_callback_default   = array( $this, 'display_input_field' );
514
		$input_checkboxes_default = array( $this, 'display_checkboxes' );
515
		$input_select_default     = array( $this, 'display_select' );
516
		$link_default             = array( $this, 'display_link' );
517
518
		$all_field_callbacks = array(
519
			'text'       => $input_callback_default,
520
			'checkboxes' => $input_checkboxes_default,
521
			'select'     => $input_select_default,
522
			'link'       => $link_default,
523
		);
524
525
		$this->fields_settings( 'settings', 'settings', $all_field_callbacks );
526
		$this->fields_fieldmaps( 'fieldmaps', 'objects' );
527
		$this->fields_scheduling( 'schedule', 'schedule', $all_field_callbacks );
528
		$this->fields_log_settings( 'log_settings', 'log_settings', $all_field_callbacks );
529
	}
530
531
	/**
532
	* Fields for the Settings tab
533
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
534
	*
535
	* @param string $page
536
	* @param string $section
537
	* @param string $input_callback
538
	*/
539
	private function fields_settings( $page, $section, $callbacks ) {
540
		add_settings_section( $page, ucwords( $page ), null, $page );
541
		$salesforce_settings = array(
542
			'consumer_key'                   => array(
543
				'title'    => __( 'Consumer Key', 'object-sync-for-salesforce' ),
544
				'callback' => $callbacks['text'],
545
				'page'     => $page,
546
				'section'  => $section,
547
				'args'     => array(
548
					'type'     => 'text',
549
					'validate' => 'sanitize_validate_text',
550
					'desc'     => '',
551
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_KEY',
552
				),
553
554
			),
555
			'consumer_secret'                => array(
556
				'title'    => __( 'Consumer Secret', 'object-sync-for-salesforce' ),
557
				'callback' => $callbacks['text'],
558
				'page'     => $page,
559
				'section'  => $section,
560
				'args'     => array(
561
					'type'     => 'text',
562
					'validate' => 'sanitize_validate_text',
563
					'desc'     => '',
564
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_SECRET',
565
				),
566
			),
567
			'callback_url'                   => array(
568
				'title'    => __( 'Callback URL', 'object-sync-for-salesforce' ),
569
				'callback' => $callbacks['text'],
570
				'page'     => $page,
571
				'section'  => $section,
572
				'args'     => array(
573
					'type'     => 'url',
574
					'validate' => 'sanitize_validate_text',
575
					// translators: %1$s is the admin URL for the Authorize tab
576
					'desc'     => sprintf( __( 'In most cases, you will want to use %1$s for this value.', 'object-sync-for-salesforce' ),
577
						get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=authorize' )
578
					),
579
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CALLBACK_URL',
580
				),
581
			),
582
			'login_base_url'                 => array(
583
				'title'    => __( 'Login Base URL', 'object-sync-for-salesforce' ),
584
				'callback' => $callbacks['text'],
585
				'page'     => $page,
586
				'section'  => $section,
587
				'args'     => array(
588
					'type'     => 'url',
589
					'validate' => 'sanitize_validate_text',
590
					// translators: 1) production salesforce login, 2) sandbox salesforce login
591
					'desc'     => sprintf( __( 'For most Salesforce setups, you should use %1$s for production and %2$s for sandbox. If you try to use an instance name as the URL, you may encounter Salesforce errors.', 'object-sync-for-salesforce' ),
592
						esc_url( 'https://login.salesforce.com' ),
593
						esc_url( 'https://test.salesforce.com' )
594
					),
595
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_LOGIN_BASE_URL',
596
				),
597
			),
598
			'authorize_url_path'             => array(
599
				'title'    => __( 'Authorize URL Path', 'object-sync-for-salesforce' ),
600
				'callback' => $callbacks['text'],
601
				'page'     => $page,
602
				'section'  => $section,
603
				'args'     => array(
604
					'type'     => 'text',
605
					'validate' => 'sanitize_validate_text',
606
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
607
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_AUTHORIZE_URL_PATH',
608
					'default'  => $this->default_authorize_url_path,
609
				),
610
			),
611
			'token_url_path'                 => array(
612
				'title'    => __( 'Token URL Path', 'object-sync-for-salesforce' ),
613
				'callback' => $callbacks['text'],
614
				'page'     => $page,
615
				'section'  => $section,
616
				'args'     => array(
617
					'type'     => 'text',
618
					'validate' => 'sanitize_validate_text',
619
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
620
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_TOKEN_URL_PATH',
621
					'default'  => $this->default_token_url_path,
622
				),
623
			),
624
			'api_version'                    => array(
625
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
626
				'callback' => $callbacks['text'],
627
				'page'     => $page,
628
				'section'  => $section,
629
				'args'     => array(
630
					'type'     => 'text',
631
					'validate' => 'sanitize_validate_text',
632
					'desc'     => '',
633
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
634
					'default'  => $this->default_api_version,
635
				),
636
			),
637
			'object_filters'                 => array(
638
				'title'    => __( 'Limit Salesforce Objects', 'object-sync-for-salesforce' ),
639
				'callback' => $callbacks['checkboxes'],
640
				'page'     => $page,
641
				'section'  => $section,
642
				'args'     => array(
643
					'type'     => 'checkboxes',
644
					'validate' => 'sanitize_validate_text',
645
					'desc'     => __( 'Allows you to limit which Salesforce objects can be mapped', 'object-sync-for-salesforce' ),
646
					'items'    => array(
647
						'triggerable' => array(
648
							'text'    => __( 'Only Triggerable objects', 'object-sync-for-salesforce' ),
649
							'id'      => 'triggerable',
650
							'desc'    => '',
651
							'default' => $this->default_triggerable,
652
						),
653
						'updateable'  => array(
654
							'text'    => __( 'Only Updateable objects', 'object-sync-for-salesforce' ),
655
							'id'      => 'updateable',
656
							'desc'    => '',
657
							'default' => $this->default_updateable,
658
						),
659
					),
660
				),
661
			),
662
			'salesforce_field_display_value' => array(
663
				'title'    => __( 'Salesforce Field Display Value', 'object-sync-for-salesforce' ),
664
				'callback' => $callbacks['select'],
665
				'page'     => $page,
666
				'section'  => $section,
667
				'args'     => array(
668
					'type'     => 'select',
669
					'validate' => 'sanitize_validate_text',
670
					'desc'     => __( 'When choosing Salesforce fields to map, this value determines how the dropdown will identify Salesforce fields.', 'object-sync-for-salesforce' ),
671
					'constant' => '',
672
					'items'    => array(
673
						'field_label' => array(
674
							'text'  => __( 'Field Label', 'object-sync-for-salesforce' ),
675
							'value' => 'field_label',
676
						),
677
						/*'field_name'  => array(
678
							'text'  => __( 'Field Name', 'object-sync-for-salesforce' ),
679
							'value' => 'field_name',
680
						),*/
681
						'api_name'    => array(
682
							'text'  => __( 'API Name', 'object-sync-for-salesforce' ),
683
							'value' => 'api_name',
684
						),
685
					),
686
				),
687
			),
688
			'pull_query_limit'               => array(
689
				'title'    => __( 'Pull query record limit', 'object-sync-for-salesforce' ),
690
				'callback' => $callbacks['text'],
691
				'page'     => $page,
692
				'section'  => $section,
693
				'args'     => array(
694
					'type'     => 'number',
695
					'validate' => 'absint',
696
					'desc'     => __( 'Limit the number of records that can be pulled from Salesforce in a single query.', 'object-sync-for-salesforce' ),
697
					'constant' => '',
698
					'default'  => $this->default_pull_limit,
699
				),
700
			),
701
			'pull_throttle'                  => array(
702
				'title'    => __( 'Pull throttle (seconds)', 'object-sync-for-salesforce' ),
703
				'callback' => $callbacks['text'],
704
				'page'     => $page,
705
				'section'  => $section,
706
				'args'     => array(
707
					'type'     => 'number',
708
					'validate' => 'sanitize_validate_text',
709
					'desc'     => __( 'Number of seconds to wait between repeated salesforce pulls. Prevents the webserver from becoming overloaded in case of too many cron runs, or webhook usage.', 'object-sync-for-salesforce' ),
710
					'constant' => '',
711
					'default'  => $this->default_pull_throttle,
712
				),
713
			),
714
		);
715
716
		// only show soap settings if the soap extension is enabled on the server
717
		if ( true === $this->salesforce['soap_available'] ) {
718
			$salesforce_settings['use_soap']       = array(
719
				'title'    => __( 'Enable the Salesforce SOAP API?', 'object-sync-for-salesforce' ),
720
				'callback' => $callbacks['text'],
721
				'page'     => $page,
722
				'section'  => $section,
723
				'class'    => 'object-sync-for-salesforce-enable-soap',
724
				'args'     => array(
725
					'type'     => 'checkbox',
726
					'validate' => 'sanitize_text_field',
727
					'desc'     => __( 'Check this to enable the SOAP API and use it instead of the REST API when the plugin supports it. https://developer.salesforce.com/blogs/tech-pubs/2011/10/salesforce-apis-what-they-are-when-to-use-them.html to compare the two. Note: if you need to detect Salesforce merges in this plugin, you will need to enable SOAP.', 'object-sync-for-salesforce' ),
728
					'constant' => '',
729
				),
730
			);
731
			$salesforce_settings['soap_wsdl_path'] = array(
732
				'title'    => __( 'Path to SOAP WSDL file', 'object-sync-for-salesforce' ),
733
				'callback' => $callbacks['text'],
734
				'page'     => $page,
735
				'section'  => $section,
736
				'class'    => 'object-sync-for-salesforce-soap-wsdl-path',
737
				'args'     => array(
738
					'type'     => 'text',
739
					'validate' => 'sanitize_text_field',
740
					'desc'     => __( 'Optionally add a path to your own WSDL file. If you do not, the plugin will use the default partner.wsdl.xml from the Force.com toolkit.', 'object-sync-for-salesforce' ),
741
					'constant' => '',
742
				),
743
			);
744
		}
745
746
		$salesforce_settings['debug_mode']               = array(
747
			'title'    => __( 'Debug mode?', 'object-sync-for-salesforce' ),
748
			'callback' => $callbacks['text'],
749
			'page'     => $page,
750
			'section'  => $section,
751
			'args'     => array(
752
				'type'     => 'checkbox',
753
				'validate' => 'sanitize_text_field',
754
				'desc'     => __( 'Debug mode can, combined with the Log Settings, log things like Salesforce API requests. It can create a lot of entries if enabled; it is not recommended to use it in a production environment.', 'object-sync-for-salesforce' ),
755
				'constant' => '',
756
			),
757
		);
758
		$salesforce_settings['delete_data_on_uninstall'] = array(
759
			'title'    => __( 'Delete plugin data on uninstall?', 'object-sync-for-salesforce' ),
760
			'callback' => $callbacks['text'],
761
			'page'     => $page,
762
			'section'  => $section,
763
			'args'     => array(
764
				'type'     => 'checkbox',
765
				'validate' => 'sanitize_text_field',
766
				'desc'     => __( 'If checked, the plugin will delete the tables and other data it creates when you uninstall it. Unchecking this field can be useful if you need to reactivate the plugin for any reason without losing data.', 'object-sync-for-salesforce' ),
767
				'constant' => '',
768
			),
769
		);
770
771
		if ( true === is_object( $this->salesforce['sfapi'] ) && true === $this->salesforce['sfapi']->is_authorized() ) {
772
			$salesforce_settings['api_version'] = array(
773
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
774
				'callback' => $callbacks['select'],
775
				'page'     => $page,
776
				'section'  => $section,
777
				'args'     => array(
778
					'type'     => 'select',
779
					'validate' => 'sanitize_text_field',
780
					'desc'     => '',
781
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
782
					'items'    => $this->version_options(),
783
				),
784
			);
785
		}
786
787
		foreach ( $salesforce_settings as $key => $attributes ) {
788
			$id       = $this->option_prefix . $key;
789
			$name     = $this->option_prefix . $key;
790
			$title    = $attributes['title'];
791
			$callback = $attributes['callback'];
792
			$validate = $attributes['args']['validate'];
793
			$page     = $attributes['page'];
794
			$section  = $attributes['section'];
795
			$class    = isset( $attributes['class'] ) ? $attributes['class'] : '';
796
			$args     = array_merge(
797
				$attributes['args'],
798
				array(
799
					'title'     => $title,
800
					'id'        => $id,
801
					'label_for' => $id,
802
					'name'      => $name,
803
					'class'     => $class,
804
				)
805
			);
806
807
			// if there is a constant and it is defined, don't run a validate function
808
			if ( isset( $attributes['args']['constant'] ) && defined( $attributes['args']['constant'] ) ) {
809
				$validate = '';
810
			}
811
812
			add_settings_field( $id, $title, $callback, $page, $section, $args );
813
			register_setting( $page, $id, array( $this, $validate ) );
814
		}
815
	}
816
817
	/**
818
	* Fields for the Fieldmaps tab
819
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
820
	*
821
	* @param string $page
822
	* @param string $section
823
	* @param string $input_callback
824
	*/
825
	private function fields_fieldmaps( $page, $section, $input_callback = '' ) {
826
		add_settings_section( $page, ucwords( $page ), null, $page );
827
	}
828
829
	/**
830
	* Fields for the Scheduling tab
831
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
832
	*
833
	* @param string $page
834
	* @param string $section
835
	* @param string $input_callback
836
	*/
837
	private function fields_scheduling( $page, $section, $callbacks ) {
838
839
		add_settings_section( 'batch', __( 'Batch Settings', 'object-sync-for-salesforce' ), null, $page );
840
		$section           = 'batch';
841
		$schedule_settings = array(
842
			'action_scheduler_batch_size'         => array(
843
				'title'    => __( 'Batch size', 'object-sync-for-salesforce' ),
844
				'callback' => $callbacks['text'],
845
				'page'     => $page,
846
				'section'  => $section,
847
				'args'     => array(
848
					'type'     => 'number',
849
					'validate' => 'absint',
850
					'default'  => 5,
851
					'desc'     => __( 'Set how many actions (checking for data changes, syncing a record, etc. all count as individual actions) can be run in a batch. Start with a low number here, like 5, if you are unsure.', 'object-sync-for-salesforce' ),
852
					'constant' => '',
853
				),
854
855
			),
856
			'action_scheduler_concurrent_batches' => array(
857
				'title'    => __( 'Concurrent batches', 'object-sync-for-salesforce' ),
858
				'callback' => $callbacks['text'],
859
				'page'     => $page,
860
				'section'  => $section,
861
				'args'     => array(
862
					'type'     => 'number',
863
					'validate' => 'absint',
864
					'default'  => 3,
865
					'desc'     => __( 'Set how many batches of actions can be run at once. Start with a low number here, like 3, if you are unsure.', 'object-sync-for-salesforce' ),
866
					'constant' => '',
867
				),
868
			),
869
		);
870
871
		foreach ( $this->schedulable_classes as $key => $value ) {
872
			add_settings_section( $key, $value['label'], null, $page );
873
			if ( isset( $value['initializer'] ) ) {
874
				$schedule_settings[ $key . '_schedule_number' ] = array(
875
					'title'    => __( 'Run schedule every', 'object-sync-for-salesforce' ),
876
					'callback' => $callbacks['text'],
877
					'page'     => $page,
878
					'section'  => $key,
879
					'args'     => array(
880
						'type'     => 'number',
881
						'validate' => 'absint',
882
						'desc'     => '',
883
						'constant' => '',
884
					),
885
				);
886
				$schedule_settings[ $key . '_schedule_unit' ]   = array(
887
					'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
888
					'callback' => $callbacks['select'],
889
					'page'     => $page,
890
					'section'  => $key,
891
					'args'     => array(
892
						'type'     => 'select',
893
						'validate' => 'sanitize_validate_text',
894
						'desc'     => '',
895
						'items'    => array(
896
							'minutes' => array(
897
								'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
898
								'value' => 'minutes',
899
							),
900
							'hours'   => array(
901
								'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
902
								'value' => 'hours',
903
							),
904
							'days'    => array(
905
								'text'  => __( 'Days', 'object-sync-for-salesforce' ),
906
								'value' => 'days',
907
							),
908
						),
909
					),
910
				);
911
			}
912
			$schedule_settings[ $key . '_clear_button' ] = array(
913
				// translators: $this->get_schedule_count is an integer showing how many items are in the current queue
914
				'title'    => sprintf( 'This queue has ' . _n( '%s item', '%s items', $this->get_schedule_count( $key ), 'object-sync-for-salesforce' ), $this->get_schedule_count( $key ) ),
915
				'callback' => $callbacks['link'],
916
				'page'     => $page,
917
				'section'  => $key,
918
				'args'     => array(
919
					'label'      => __( 'Clear this queue', 'object-sync-for-salesforce' ),
920
					'desc'       => '',
921
					'url'        => esc_url( '?page=object-sync-salesforce-admin&amp;tab=clear_schedule&amp;schedule_name=' . $key ),
922
					'link_class' => 'button button-secondary',
923
				),
924
			);
925
			foreach ( $schedule_settings as $key => $attributes ) {
926
				$id       = $this->option_prefix . $key;
927
				$name     = $this->option_prefix . $key;
928
				$title    = $attributes['title'];
929
				$callback = $attributes['callback'];
930
				$page     = $attributes['page'];
931
				$section  = $attributes['section'];
932
				$args     = array_merge(
933
					$attributes['args'],
934
					array(
935
						'title'     => $title,
936
						'id'        => $id,
937
						'label_for' => $id,
938
						'name'      => $name,
939
					)
940
				);
941
				add_settings_field( $id, $title, $callback, $page, $section, $args );
942
				register_setting( $page, $id );
943
			}
944
		} // End foreach().
945
	}
946
947
	/**
948
	* Fields for the Log Settings tab
949
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
950
	*
951
	* @param string $page
952
	* @param string $section
953
	* @param array $callbacks
954
	*/
955
	private function fields_log_settings( $page, $section, $callbacks ) {
956
		add_settings_section( $page, ucwords( str_replace( '_', ' ', $page ) ), null, $page );
957
		$log_settings = array(
958
			'enable_logging'        => array(
959
				'title'    => __( 'Enable Logging?', 'object-sync-for-salesforce' ),
960
				'callback' => $callbacks['text'],
961
				'page'     => $page,
962
				'section'  => $section,
963
				'args'     => array(
964
					'type'     => 'checkbox',
965
					'validate' => 'absint',
966
					'desc'     => '',
967
					'constant' => '',
968
				),
969
			),
970
			'statuses_to_log'       => array(
971
				'title'    => __( 'Statuses to log', 'object-sync-for-salesforce' ),
972
				'callback' => $callbacks['checkboxes'],
973
				'page'     => $page,
974
				'section'  => $section,
975
				'args'     => array(
976
					'type'     => 'checkboxes',
977
					'validate' => 'sanitize_validate_text',
978
					'desc'     => __( 'these are the statuses to log', 'object-sync-for-salesforce' ),
979
					'items'    => array(
980
						'error'   => array(
981
							'text' => __( 'Error', 'object-sync-for-salesforce' ),
982
							'id'   => 'error',
983
							'desc' => '',
984
						),
985
						'success' => array(
986
							'text' => __( 'Success', 'object-sync-for-salesforce' ),
987
							'id'   => 'success',
988
							'desc' => '',
989
						),
990
						'notice'  => array(
991
							'text' => __( 'Notice', 'object-sync-for-salesforce' ),
992
							'id'   => 'notice',
993
							'desc' => '',
994
						),
995
						'debug'   => array(
996
							'text' => __( 'Debug', 'object-sync-for-salesforce' ),
997
							'id'   => 'debug',
998
							'desc' => '',
999
						),
1000
					),
1001
				),
1002
			),
1003
			'prune_logs'            => array(
1004
				'title'    => __( 'Automatically delete old log entries?', 'object-sync-for-salesforce' ),
1005
				'callback' => $callbacks['text'],
1006
				'page'     => $page,
1007
				'section'  => $section,
1008
				'args'     => array(
1009
					'type'     => 'checkbox',
1010
					'validate' => 'absint',
1011
					'desc'     => '',
1012
					'constant' => '',
1013
				),
1014
			),
1015
			'logs_how_old'          => array(
1016
				'title'    => __( 'Age to delete log entries', 'object-sync-for-salesforce' ),
1017
				'callback' => $callbacks['text'],
1018
				'page'     => $page,
1019
				'section'  => $section,
1020
				'args'     => array(
1021
					'type'     => 'text',
1022
					'validate' => 'sanitize_validate_text',
1023
					'desc'     => __( 'If automatic deleting is enabled, it will affect logs this old.', 'object-sync-for-salesforce' ),
1024
					'default'  => '2 weeks',
1025
					'constant' => '',
1026
				),
1027
			),
1028
			'logs_how_often_number' => array(
1029
				'title'    => __( 'Check for old logs every', 'object-sync-for-salesforce' ),
1030
				'callback' => $callbacks['text'],
1031
				'page'     => $page,
1032
				'section'  => $section,
1033
				'args'     => array(
1034
					'type'     => 'number',
1035
					'validate' => 'absint',
1036
					'desc'     => '',
1037
					'default'  => '1',
1038
					'constant' => '',
1039
				),
1040
			),
1041
			'logs_how_often_unit'   => array(
1042
				'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
1043
				'callback' => $callbacks['select'],
1044
				'page'     => $page,
1045
				'section'  => $section,
1046
				'args'     => array(
1047
					'type'     => 'select',
1048
					'validate' => 'sanitize_validate_text',
1049
					'desc'     => __( 'These two fields are how often the site will check for logs to delete.', 'object-sync-for-salesforce' ),
1050
					'items'    => array(
1051
						'minutes' => array(
1052
							'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
1053
							'value' => 'minutes',
1054
						),
1055
						'hours'   => array(
1056
							'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
1057
							'value' => 'hours',
1058
						),
1059
						'days'    => array(
1060
							'text'  => __( 'Days', 'object-sync-for-salesforce' ),
1061
							'value' => 'days',
1062
						),
1063
					),
1064
				),
1065
			),
1066
			'triggers_to_log'       => array(
1067
				'title'    => __( 'Triggers to log', 'object-sync-for-salesforce' ),
1068
				'callback' => $callbacks['checkboxes'],
1069
				'page'     => $page,
1070
				'section'  => $section,
1071
				'args'     => array(
1072
					'type'     => 'checkboxes',
1073
					'validate' => 'sanitize_validate_text',
1074
					'desc'     => __( 'these are the triggers to log', 'object-sync-for-salesforce' ),
1075
					'items'    => array(
1076
						$this->mappings->sync_wordpress_create => array(
1077
							'text' => __( 'WordPress create', 'object-sync-for-salesforce' ),
1078
							'id'   => 'wordpress_create',
1079
							'desc' => '',
1080
						),
1081
						$this->mappings->sync_wordpress_update => array(
1082
							'text' => __( 'WordPress update', 'object-sync-for-salesforce' ),
1083
							'id'   => 'wordpress_update',
1084
							'desc' => '',
1085
						),
1086
						$this->mappings->sync_wordpress_delete => array(
1087
							'text' => __( 'WordPress delete', 'object-sync-for-salesforce' ),
1088
							'id'   => 'wordpress_delete',
1089
							'desc' => '',
1090
						),
1091
						$this->mappings->sync_sf_create => array(
1092
							'text' => __( 'Salesforce create', 'object-sync-for-salesforce' ),
1093
							'id'   => 'sf_create',
1094
							'desc' => '',
1095
						),
1096
						$this->mappings->sync_sf_update => array(
1097
							'text' => __( 'Salesforce update', 'object-sync-for-salesforce' ),
1098
							'id'   => 'sf_update',
1099
							'desc' => '',
1100
						),
1101
						$this->mappings->sync_sf_delete => array(
1102
							'text' => __( 'Salesforce delete', 'object-sync-for-salesforce' ),
1103
							'id'   => 'sf_delete',
1104
							'desc' => '',
1105
						),
1106
					),
1107
				),
1108
			),
1109
		);
1110
		foreach ( $log_settings as $key => $attributes ) {
1111
			$id       = $this->option_prefix . $key;
1112
			$name     = $this->option_prefix . $key;
1113
			$title    = $attributes['title'];
1114
			$callback = $attributes['callback'];
1115
			$page     = $attributes['page'];
1116
			$section  = $attributes['section'];
1117
			$args     = array_merge(
1118
				$attributes['args'],
1119
				array(
1120
					'title'     => $title,
1121
					'id'        => $id,
1122
					'label_for' => $id,
1123
					'name'      => $name,
1124
				)
1125
			);
1126
			add_settings_field( $id, $title, $callback, $page, $section, $args );
1127
			register_setting( $page, $id );
1128
		}
1129
	}
1130
1131
	/**
1132
	* Create the notices, settings, and conditions by which admin notices should appear
1133
	*
1134
	*/
1135
	public function notices() {
1136
1137
		// before a notice is displayed, we should make sure we are on a page related to this plugin
1138
		if ( ! isset( $_GET['page'] ) || 'object-sync-salesforce-admin' !== $_GET['page'] ) {
1139
			return;
1140
		}
1141
1142
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
1143
		require_once plugin_dir_path( __FILE__ ) . '../classes/admin-notice.php';
1144
1145
		$notices = array(
1146
			'permission'              => array(
1147
				'condition'   => false === $this->check_wordpress_admin_permissions(),
1148
				'message'     => __( "Your account does not have permission to edit the Salesforce REST API plugin's settings.", 'object-sync-for-salesforce' ),
1149
				'type'        => 'error',
1150
				'dismissible' => false,
1151
			),
1152
			'fieldmap'                => array(
1153
				'condition'   => isset( $get_data['transient'] ),
1154
				'message'     => __( 'Errors kept this fieldmap from being saved.', 'object-sync-for-salesforce' ),
1155
				'type'        => 'error',
1156
				'dismissible' => true,
1157
			),
1158
			'object_map'              => array(
1159
				'condition'   => isset( $get_data['map_transient'] ),
1160
				'message'     => __( 'Errors kept this object map from being saved.', 'object-sync-for-salesforce' ),
1161
				'type'        => 'error',
1162
				'dismissible' => true,
1163
			),
1164
			'data_saved'              => array(
1165
				'condition'   => isset( $get_data['data_saved'] ) && 'true' === $get_data['data_saved'],
1166
				'message'     => __( 'This data was successfully saved.', 'object-sync-for-salesforce' ),
1167
				'type'        => 'success',
1168
				'dismissible' => true,
1169
			),
1170
			'data_save_error'         => array(
1171
				'condition'   => isset( $get_data['data_saved'] ) && 'false' === $get_data['data_saved'],
1172
				'message'     => __( 'This data was not successfully saved. Try again.', 'object-sync-for-salesforce' ),
1173
				'type'        => 'error',
1174
				'dismissible' => true,
1175
			),
1176
			'mapping_error_transient' => array(
1177
				'condition'   => isset( $get_data['mapping_error_transient'] ),
1178
				'message'     => __( 'Errors kept these mapping errors from being deleted.', 'object-sync-for-salesforce' ),
1179
				'type'        => 'error',
1180
				'dismissible' => true,
1181
			),
1182
		);
1183
1184
		foreach ( $notices as $key => $value ) {
1185
1186
			$condition = $value['condition'];
1187
			$message   = $value['message'];
1188
1189
			if ( isset( $value['dismissible'] ) ) {
1190
				$dismissible = $value['dismissible'];
1191
			} else {
1192
				$dismissible = false;
1193
			}
1194
1195
			if ( isset( $value['type'] ) ) {
1196
				$type = $value['type'];
1197
			} else {
1198
				$type = '';
1199
			}
1200
1201
			if ( ! isset( $value['template'] ) ) {
1202
				$template = '';
1203
			}
1204
1205
			if ( $condition ) {
1206
				new Object_Sync_Sf_Admin_Notice( $condition, $message, $dismissible, $type, $template );
1207
			}
1208
		}
1209
1210
	}
1211
1212
	/**
1213
	* Get all the Salesforce object settings for fieldmapping
1214
	* This takes either the $_POST array via ajax, or can be directly called with a $data array
1215
	*
1216
	* @param array $data
1217
	* data must contain a salesforce_object
1218
	* can optionally contain a type
1219
	* @return array $object_settings
1220
	*/
1221
	public function get_salesforce_object_description( $data = array() ) {
1222
		$ajax = false;
1223
		if ( empty( $data ) ) {
1224
			$data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1225
			$ajax = true;
1226
		}
1227
1228
		$object_description = array();
1229
1230
		if ( ! empty( $data['salesforce_object'] ) ) {
1231
			$object = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1232
1233
			$object_fields        = array();
1234
			$include_record_types = array();
1235
1236
			// these can come from ajax
1237
			$include = isset( $data['include'] ) ? (array) $data['include'] : array();
1238
			$include = array_map( 'esc_attr', $include );
1239
1240
			if ( in_array( 'fields', $include, true ) || empty( $include ) ) {
1241
				$type = isset( $data['field_type'] ) ? esc_attr( $data['field_type'] ) : ''; // can come from ajax
1242
				foreach ( $object['data']['fields'] as $key => $value ) {
1243
					if ( '' === $type || $type === $value['type'] ) {
1244
						$object_fields[ $key ] = $value;
1245
					}
1246
				}
1247
				$object_description['fields'] = $object_fields;
1248
			}
1249
1250
			if ( in_array( 'recordTypeInfos', $include, true ) ) {
1251
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1252
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1253
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1254
					}
1255
					$object_description['recordTypeInfos'] = $object_record_types;
1256
				}
1257
			}
1258
		}
1259
1260
		if ( true === $ajax ) {
1261
			wp_send_json_success( $object_description );
1262
		} else {
1263
			return $object_description;
1264
		}
1265
	}
1266
1267
	/**
1268
	* Get Salesforce object fields for fieldmapping
1269
	*
1270
	* @param array $data
1271
	* data must contain a salesforce_object
1272
	* can optionally contain a type for the field
1273
	* @return array $object_fields
1274
	*/
1275
	public function get_salesforce_object_fields( $data = array() ) {
1276
1277
		if ( ! empty( $data['salesforce_object'] ) ) {
1278
			$object               = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1279
			$object_fields        = array();
1280
			$type                 = isset( $data['type'] ) ? esc_attr( $data['type'] ) : '';
1281
			$include_record_types = isset( $data['include_record_types'] ) ? esc_attr( $data['include_record_types'] ) : false;
1282
			foreach ( $object['data']['fields'] as $key => $value ) {
1283
				if ( '' === $type || $type === $value['type'] ) {
1284
					$object_fields[ $key ] = $value;
1285
				}
1286
			}
1287
			if ( true === $include_record_types ) {
0 ignored issues
show
introduced by
The condition true === $include_record_types is always false.
Loading history...
1288
				$object_record_types = array();
1289
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1290
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1291
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1292
					}
1293
				}
1294
			}
1295
		}
1296
1297
		return $object_fields;
1298
1299
	}
1300
1301
	/**
1302
	* Get WordPress object fields for fieldmapping
1303
	* This takes either the $_POST array via ajax, or can be directly called with a $wordpress_object field
1304
	*
1305
	* @param string $wordpress_object
1306
	* @return array $object_fields
1307
	*/
1308
	public function get_wordpress_object_fields( $wordpress_object = '' ) {
1309
		$ajax      = false;
1310
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1311
		if ( empty( $wordpress_object ) ) {
1312
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( wp_unslash( $post_data['wordpress_object'] ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($post_data['wordpress_object']) can also be of type array; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1312
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1313
			$ajax             = true;
1314
		}
1315
1316
		$object_fields = $this->wordpress->get_wordpress_object_fields( $wordpress_object );
1317
1318
		if ( true === $ajax ) {
1319
			wp_send_json_success( $object_fields );
1320
		} else {
1321
			return $object_fields;
1322
		}
1323
	}
1324
1325
	/**
1326
	* Get WordPress and Salesforce object fields together for fieldmapping
1327
	* This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $salesforce_object fields
1328
	*
1329
	* @param string $wordpress_object
1330
	* @param string $salesforce_object
1331
	* @return array $object_fields
1332
	*/
1333
	public function get_wp_sf_object_fields( $wordpress_object = '', $salesforce = '' ) {
1334
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1335
		if ( empty( $wordpress_object ) ) {
1336
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( wp_unslash( $post_data['wordpress_object'] ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($post_data['wordpress_object']) can also be of type array; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1336
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1337
		}
1338
		if ( empty( $salesforce_object ) ) {
1339
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_object'] ) ) : '';
1340
		}
1341
1342
		$object_fields['wordpress']  = $this->get_wordpress_object_fields( $wordpress_object );
1343
		$object_fields['salesforce'] = $this->get_salesforce_object_fields(
1344
			array(
1345
				'salesforce_object' => $salesforce_object,
1346
			)
1347
		);
1348
1349
		if ( ! empty( $post_data ) ) {
1350
			wp_send_json_success( $object_fields );
1351
		} else {
1352
			return $object_fields;
1353
		}
1354
	}
1355
1356
	/**
1357
	* Manually push the WordPress object to Salesforce
1358
	* This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $wordpress_id fields
1359
	*
1360
	* @param string $wordpress_object
1361
	* @param int $wordpress_id
1362
	*/
1363
	public function push_to_salesforce( $wordpress_object = '', $wordpress_id = '' ) {
1364
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1365
		if ( empty( $wordpress_object ) && empty( $wordpress_id ) ) {
1366
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( wp_unslash( $post_data['wordpress_object'] ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($post_data['wordpress_object']) can also be of type array; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1366
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1367
			$wordpress_id     = isset( $post_data['wordpress_id'] ) ? absint( $post_data['wordpress_id'] ) : '';
1368
		}
1369
1370
		// clarify what that variable is in this context.
1371
		$object_type = $wordpress_object;
1372
1373
		// When objects are already mapped, there is a Salesforce id as well. Otherwise, it's blank.
1374
		$salesforce_id = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( $post_data['salesforce_id'] ) : '';
1375
		if ( '' === $salesforce_id ) {
1376
			$method = 'POST';
1377
		} else {
1378
			$method = 'PUT';
1379
		}
1380
1381
		$result = $this->push->manual_push( $object_type, $wordpress_id, $method );
1382
1383
		if ( ! empty( $post_data['wordpress_object'] ) && ! empty( $post_data['wordpress_id'] ) ) {
1384
			wp_send_json_success( $result );
1385
		} else {
1386
			return $result;
1387
		}
1388
1389
	}
1390
1391
	/**
1392
	* Manually pull the Salesforce object into WordPress
1393
	* This takes either the $_POST array via ajax, or can be directly called with $salesforce_id fields
1394
	*
1395
	* @param string $salesforce_id
1396
	* @param string $wordpress_object
1397
	*/
1398
	public function pull_from_salesforce( $salesforce_id = '', $wordpress_object = '' ) {
1399
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1400
		if ( empty( $wordpress_object ) && empty( $salesforce_id ) ) {
1401
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( wp_unslash( $post_data['wordpress_object'] ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($post_data['wordpress_object']) can also be of type array; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1401
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1402
			$salesforce_id    = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_id'] ) ) : '';
1403
		}
1404
		$type   = $this->salesforce['sfapi']->get_sobject_type( $salesforce_id );
1405
		$result = $this->pull->manual_pull( $type, $salesforce_id, $wordpress_object ); // we want the wp object to make sure we get the right fieldmap
1406
		if ( ! empty( $post_data ) ) {
1407
			wp_send_json_success( $result );
1408
		} else {
1409
			return $result;
1410
		}
1411
	}
1412
1413
	/**
1414
	* Manually pull the Salesforce object into WordPress
1415
	* This takes an id for a mapping object row
1416
	*
1417
	* @param int $mapping_id
1418
	*/
1419
	public function refresh_mapped_data( $mapping_id = '' ) {
1420
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1421
		if ( empty( $mapping_id ) ) {
1422
			$mapping_id = isset( $post_data['mapping_id'] ) ? absint( $post_data['mapping_id'] ) : '';
1423
		}
1424
		$result = $this->mappings->get_object_maps(
1425
			array(
1426
				'id' => $mapping_id,
1427
			)
1428
		);
1429
		if ( ! empty( $post_data ) ) {
1430
			wp_send_json_success( $result );
1431
		} else {
1432
			return $result;
1433
		}
1434
	}
1435
1436
	/**
1437
	* Prepare fieldmap data and redirect after processing
1438
	* This runs when the create or update forms are submitted
1439
	* It is public because it depends on an admin hook
1440
	* It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1441
	* This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1442
	*
1443
	*/
1444
	public function prepare_fieldmap_data() {
1445
		$error     = false;
1446
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1447
		$cachekey  = wp_json_encode( $post_data );
1448
		if ( false !== $cachekey ) {
1449
			$cachekey = md5( $cachekey );
1450
		}
1451
1452
		if ( ! isset( $post_data['label'] ) || ! isset( $post_data['salesforce_object'] ) || ! isset( $post_data['wordpress_object'] ) ) {
1453
			$error = true;
1454
		}
1455
		if ( true === $error ) {
1456
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1457
			if ( '' !== $cachekey ) {
1458
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
0 ignored issues
show
Bug introduced by
Are you sure $cachekey of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

1458
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1459
			}
1460
		} else { // there are no errors
1461
			// send the row to the fieldmap class
1462
			// if it is add or clone, use the create method
1463
			$method            = esc_attr( $post_data['method'] );
1464
			$salesforce_fields = $this->get_salesforce_object_fields(
1465
				array(
1466
					'salesforce_object' => $post_data['salesforce_object'],
1467
				)
1468
			);
1469
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1470
			if ( 'add' === $method || 'clone' === $method ) {
1471
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1472
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method
1473
				$id     = esc_attr( $post_data['id'] );
1474
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
1475
			}
1476
			if ( false === $result ) { // if the database didn't save, it's still an error
1477
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1478
				if ( '' !== $cachekey ) {
1479
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1480
				}
1481
			} else {
1482
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1483
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1484
				}
1485
				// then send the user to the list of fieldmaps
1486
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1487
			}
1488
		}
1489
		wp_safe_redirect( $url );
1490
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1491
	}
1492
1493
	/**
1494
	* Delete fieldmap data and redirect after processing
1495
	* This runs when the delete link is clicked, after the user confirms
1496
	* It is public because it depends on an admin hook
1497
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1498
	*
1499
	*/
1500
	public function delete_fieldmap() {
1501
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1502
		if ( $post_data['id'] ) {
1503
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1504
			if ( true === $result ) {
1505
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1506
			} else {
1507
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1508
			}
1509
			wp_safe_redirect( $url );
1510
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1511
		}
1512
	}
1513
1514
	/**
1515
	* Prepare object data and redirect after processing
1516
	* This runs when the update form is submitted
1517
	* It is public because it depends on an admin hook
1518
	* It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1519
	* This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1520
	*
1521
	*/
1522
	public function prepare_object_map_data() {
1523
		$error     = false;
1524
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1525
		$cachekey  = wp_json_encode( $post_data );
1526
		if ( false !== $cachekey ) {
1527
			$cachekey = md5( $cachekey );
1528
		}
1529
1530
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1531
			$error = true;
1532
		}
1533
		if ( true === $error ) {
1534
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1535
			if ( '' !== $cachekey ) {
1536
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
0 ignored issues
show
Bug introduced by
Are you sure $cachekey of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

1536
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1537
			}
1538
		} else { // there are no errors
1539
			// send the row to the object map class
1540
			$method = esc_attr( $post_data['method'] );
1541
			if ( 'edit' === $method ) { // if it is edit, use the update method
1542
				$id     = esc_attr( $post_data['id'] );
1543
				$result = $this->mappings->update_object_map( $post_data, $id );
1544
			}
1545
			if ( false === $result ) { // if the database didn't save, it's still an error
1546
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1547
				if ( '' !== $cachekey ) {
1548
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1549
				}
1550
			} else {
1551
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1552
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1553
				}
1554
				// then send the user to the success redirect url
1555
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1556
			}
1557
		}
1558
		wp_safe_redirect( $url );
1559
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1560
	}
1561
1562
	/**
1563
	* Delete object map data and redirect after processing
1564
	* This runs when the delete link is clicked on an error row, after the user confirms
1565
	* It is public because it depends on an admin hook
1566
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1567
	*
1568
	*/
1569
	public function delete_object_map() {
1570
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1571
		if ( isset( $post_data['id'] ) ) {
1572
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1573
			if ( true === $result ) {
1574
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1575
			} else {
1576
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1577
			}
1578
			wp_safe_redirect( $url );
1579
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1580
		} elseif ( $post_data['delete'] ) {
1581
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1582
			$cachekey  = wp_json_encode( $post_data );
1583
			if ( false !== $cachekey ) {
1584
				$cachekey = md5( $cachekey );
1585
			}
1586
			$error = false;
1587
			if ( ! isset( $post_data['delete'] ) ) {
1588
				$error = true;
1589
			}
1590
			if ( true === $error ) {
1591
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1592
				if ( '' !== $cachekey ) {
1593
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
0 ignored issues
show
Bug introduced by
Are you sure $cachekey of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

1593
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1594
				}
1595
			} else { // there are no errors
1596
				$result = $this->mappings->delete_object_map( array_keys( $post_data['delete'] ) );
1597
				if ( true === $result ) {
1598
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1599
				}
1600
1601
				if ( false === $result ) { // if the database didn't save, it's still an error
1602
					$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1603
					if ( '' !== $cachekey ) {
1604
						$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
1605
					}
1606
				} else {
1607
					if ( isset( $post_data['mapping_error_transient'] ) ) { // there was previously an error saved. can delete it now.
1608
						$this->sfwp_transients->delete( esc_attr( $post_data['mapping_error_transient'] ) );
1609
					}
1610
					// then send the user to the list of fieldmaps
1611
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1612
				}
1613
			}
1614
			wp_safe_redirect( $url );
1615
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1616
		}
1617
	}
1618
1619
	/**
1620
	* Import a json file and use it for plugin data
1621
	*
1622
	*/
1623
	public function import_json_file() {
1624
1625
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_import'], 'object_sync_for_salesforce_nonce_import' ) ) {
1626
			return;
1627
		}
1628
		if ( ! current_user_can( 'manage_options' ) ) {
1629
			return;
1630
		}
1631
		$path      = $_FILES['import_file']['name'];
1632
		$extension = pathinfo( $path, PATHINFO_EXTENSION );
1633
		if ( 'json' !== $extension ) {
1634
			wp_die( __( 'Please upload a valid .json file' ) );
1635
		}
1636
1637
		$import_file = $_FILES['import_file']['tmp_name'];
1638
		if ( empty( $import_file ) ) {
1639
			wp_die( __( 'Please upload a file to import' ) );
1640
		}
1641
1642
		// Retrieve the data from the file and convert the json object to an array.
1643
		$data = (array) json_decode( file_get_contents( $import_file ), true );
1644
1645
		// if there is only one object map, fix the array
1646
		if ( isset( $data['object_maps'] ) ) {
1647
			if ( count( $data['object_maps'] ) === count( $data['object_maps'], COUNT_RECURSIVE ) ) {
1648
				$data['object_maps'] = array( 0 => $data['object_maps'] );
1649
			}
1650
		}
1651
1652
		$overwrite = isset( $_POST['overwrite'] ) ? esc_attr( $_POST['overwrite'] ) : '';
1653
		if ( true === filter_var( $overwrite, FILTER_VALIDATE_BOOLEAN ) ) {
1654
			if ( isset( $data['fieldmaps'] ) ) {
1655
				$fieldmaps = $this->mappings->get_fieldmaps();
1656
				foreach ( $fieldmaps as $fieldmap ) {
1657
					$id     = $fieldmap['id'];
1658
					$delete = $this->mappings->delete_fieldmap( $id );
1659
				}
1660
			}
1661
			if ( isset( $data['object_maps'] ) ) {
1662
				$object_maps = $this->mappings->get_object_maps();
1663
1664
				// if there is only one existing object map, fix the array
1665
				if ( count( $object_maps ) === count( $object_maps, COUNT_RECURSIVE ) ) {
1666
					$object_maps = array( 0 => $object_maps );
1667
				}
1668
1669
				foreach ( $object_maps as $object_map ) {
1670
					$id     = $object_map['id'];
1671
					$delete = $this->mappings->delete_object_map( $id );
1672
				}
1673
			}
1674
			if ( isset( $data['plugin_settings'] ) ) {
1675
				foreach ( $data['plugin_settings'] as $key => $value ) {
1676
					delete_option( $value['option_name'] );
1677
				}
1678
			}
1679
		}
1680
1681
		$success = true;
1682
1683
		if ( isset( $data['fieldmaps'] ) ) {
1684
			foreach ( $data['fieldmaps'] as $fieldmap ) {
1685
				unset( $fieldmap['id'] );
1686
				$create = $this->mappings->create_fieldmap( $fieldmap );
1687
				if ( false === $create ) {
1688
					$success = false;
1689
				}
1690
			}
1691
		}
1692
1693
		if ( isset( $data['object_maps'] ) ) {
1694
			foreach ( $data['object_maps'] as $object_map ) {
1695
				unset( $object_map['id'] );
1696
				$create = $this->mappings->create_object_map( $object_map );
1697
				if ( false === $create ) {
1698
					$success = false;
1699
				}
1700
			}
1701
		}
1702
1703
		if ( isset( $data['plugin_settings'] ) ) {
1704
			foreach ( $data['plugin_settings'] as $key => $value ) {
1705
				update_option( $value['option_name'], maybe_unserialize( $value['option_value'] ), $value['autoload'] );
1706
			}
1707
		}
1708
1709
		if ( true === $success ) {
1710
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=import-export&data_saved=true' ) );
1711
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1712
		} else {
1713
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=import-export&data_saved=false' ) );
1714
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1715
		}
1716
1717
	}
1718
1719
	/**
1720
	* Create a json file for exporting
1721
	*
1722
	*/
1723
	public function export_json_file() {
1724
1725
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_export'], 'object_sync_for_salesforce_nonce_export' ) ) {
1726
			return;
1727
		}
1728
		if ( ! current_user_can( 'manage_options' ) ) {
1729
			return;
1730
		}
1731
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1732
		$export    = array();
1733
		if ( in_array( 'fieldmaps', $post_data['export'] ) ) {
1734
			$export['fieldmaps'] = $this->mappings->get_fieldmaps();
1735
		}
1736
		if ( in_array( 'object_maps', $post_data['export'] ) ) {
1737
			$export['object_maps'] = $this->mappings->get_object_maps();
1738
		}
1739
		if ( in_array( 'plugin_settings', $post_data['export'] ) ) {
1740
			$export['plugin_settings'] = $this->wpdb->get_results( 'SELECT * FROM ' . $this->wpdb->prefix . 'options' . ' WHERE option_name like "' . $this->option_prefix . '%"', ARRAY_A );
1741
		}
1742
		nocache_headers();
1743
		header( 'Content-Type: application/json; charset=utf-8' );
1744
		header( 'Content-Disposition: attachment; filename=object-sync-for-salesforce-data-export-' . date( 'm-d-Y' ) . '.json' );
1745
		header( 'Expires: 0' );
1746
		echo wp_json_encode( $export );
0 ignored issues
show
Bug introduced by
Are you sure wp_json_encode($export) of type false|string can be used in echo? ( Ignorable by Annotation )

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

1746
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
1747
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1748
	}
1749
1750
	/**
1751
	* Default display for <input> fields
1752
	*
1753
	* @param array $args
1754
	*/
1755
	public function display_input_field( $args ) {
1756
		$type    = $args['type'];
1757
		$id      = $args['label_for'];
1758
		$name    = $args['name'];
1759
		$desc    = $args['desc'];
1760
		$checked = '';
1761
1762
		$class = 'regular-text';
1763
1764
		if ( 'checkbox' === $type ) {
1765
			$class = 'checkbox';
1766
		}
1767
1768
		if ( isset( $args['class'] ) ) {
1769
			$class = $args['class'];
1770
		}
1771
1772
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
1773
			$value = esc_attr( get_option( $id, '' ) );
0 ignored issues
show
Bug introduced by
It seems like get_option($id, '') can also be of type false; however, parameter $text of esc_attr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1773
			$value = esc_attr( /** @scrutinizer ignore-type */ get_option( $id, '' ) );
Loading history...
1774
			if ( 'checkbox' === $type ) {
1775
				$value = filter_var( get_option( $id, false ), FILTER_VALIDATE_BOOLEAN );
1776
				if ( true === $value ) {
1777
					$checked = 'checked ';
1778
				}
1779
				$value = 1;
1780
			}
1781
			if ( '' === $value && isset( $args['default'] ) && '' !== $args['default'] ) {
1782
				$value = $args['default'];
1783
			}
1784
1785
			echo sprintf( '<input type="%1$s" value="%2$s" name="%3$s" id="%4$s" class="%5$s"%6$s>',
1786
				esc_attr( $type ),
1787
				esc_attr( $value ),
1788
				esc_attr( $name ),
1789
				esc_attr( $id ),
1790
				sanitize_html_class( $class . esc_html( ' code' ) ),
1791
				esc_html( $checked )
1792
			);
1793
			if ( '' !== $desc ) {
1794
				echo sprintf( '<p class="description">%1$s</p>',
1795
					esc_html( $desc )
1796
				);
1797
			}
1798
		} else {
1799
			echo sprintf( '<p><code>%1$s</code></p>',
1800
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
1801
			);
1802
		}
1803
	}
1804
1805
	/**
1806
	* Display for multiple checkboxes
1807
	* Above method can handle a single checkbox as it is
1808
	*
1809
	* @param array $args
1810
	*/
1811
	public function display_checkboxes( $args ) {
1812
		$type    = 'checkbox';
1813
		$name    = $args['name'];
1814
		$options = get_option( $name, array() );
1815
		foreach ( $args['items'] as $key => $value ) {
1816
			$text    = $value['text'];
1817
			$id      = $value['id'];
1818
			$desc    = $value['desc'];
1819
			$checked = '';
1820
			if ( is_array( $options ) && in_array( (string) $key, $options, true ) ) {
1821
				$checked = 'checked';
1822
			} elseif ( is_array( $options ) && empty( $options ) ) {
1823
				if ( isset( $value['default'] ) && true === $value['default'] ) {
1824
					$checked = 'checked';
1825
				}
1826
			}
1827
			echo sprintf( '<div class="checkbox"><label><input type="%1$s" value="%2$s" name="%3$s[]" id="%4$s"%5$s>%6$s</label></div>',
1828
				esc_attr( $type ),
1829
				esc_attr( $key ),
1830
				esc_attr( $name ),
1831
				esc_attr( $id ),
1832
				esc_html( $checked ),
1833
				esc_html( $text )
1834
			);
1835
			if ( '' !== $desc ) {
1836
				echo sprintf( '<p class="description">%1$s</p>',
1837
					esc_html( $desc )
1838
				);
1839
			}
1840
		}
1841
	}
1842
1843
	/**
1844
	* Display for a dropdown
1845
	*
1846
	* @param array $args
1847
	*/
1848
	public function display_select( $args ) {
1849
		$type = $args['type'];
1850
		$id   = $args['label_for'];
1851
		$name = $args['name'];
1852
		$desc = $args['desc'];
1853
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
1854
			$current_value = get_option( $name );
1855
1856
			echo sprintf( '<div class="select"><select id="%1$s" name="%2$s"><option value="">- ' . __( 'Select one', 'object-sync-for-salesforce' ) . ' -</option>',
1857
				esc_attr( $id ),
1858
				esc_attr( $name )
1859
			);
1860
1861
			foreach ( $args['items'] as $key => $value ) {
1862
				$text     = $value['text'];
1863
				$value    = $value['value'];
1864
				$selected = '';
1865
				if ( $key === $current_value || $value === $current_value ) {
1866
					$selected = ' selected';
1867
				}
1868
1869
				echo sprintf( '<option value="%1$s"%2$s>%3$s</option>',
1870
					esc_attr( $value ),
1871
					esc_attr( $selected ),
1872
					esc_html( $text )
1873
				);
1874
1875
			}
1876
			echo '</select>';
1877
			if ( '' !== $desc ) {
1878
				echo sprintf( '<p class="description">%1$s</p>',
1879
					esc_html( $desc )
1880
				);
1881
			}
1882
			echo '</div>';
1883
		} else {
1884
			echo sprintf( '<p><code>%1$s</code></p>',
1885
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
1886
			);
1887
		}
1888
	}
1889
1890
	/**
1891
	* Dropdown formatted list of Salesforce API versions
1892
	*
1893
	* @return array $args
1894
	*/
1895
	private function version_options() {
1896
		$args = array();
1897
		if ( defined( 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION' ) || ! isset( $_GET['page'] ) || 'object-sync-salesforce-admin' !== $_GET['page'] ) {
1898
			return $args;
1899
		}
1900
		$versions = $this->salesforce['sfapi']->get_api_versions();
1901
		foreach ( $versions['data'] as $key => $value ) {
1902
			$args[] = array(
1903
				'value' => $value['version'],
1904
				'text'  => $value['label'] . ' (' . $value['version'] . ')',
1905
			);
1906
		}
1907
		return $args;
1908
	}
1909
1910
	/**
1911
	* Default display for <a href> links
1912
	*
1913
	* @param array $args
1914
	*/
1915
	public function display_link( $args ) {
1916
		$label = $args['label'];
1917
		$desc  = $args['desc'];
1918
		$url   = $args['url'];
1919
		if ( isset( $args['link_class'] ) ) {
1920
			echo sprintf( '<p><a class="%1$s" href="%2$s">%3$s</a></p>',
1921
				esc_attr( $args['link_class'] ),
1922
				esc_url( $url ),
1923
				esc_html( $label )
1924
			);
1925
		} else {
1926
			echo sprintf( '<p><a href="%1$s">%2$s</a></p>',
1927
				esc_url( $url ),
1928
				esc_html( $label )
1929
			);
1930
		}
1931
1932
		if ( '' !== $desc ) {
1933
			echo sprintf( '<p class="description">%1$s</p>',
1934
				esc_html( $desc )
1935
			);
1936
		}
1937
1938
	}
1939
1940
	/**
1941
	* Allow for a standard sanitize/validate method. We could use more specific ones if need be, but this one provides a baseline.
1942
	*
1943
	* @param string $option
1944
	* @return string $option
1945
	*/
1946
	public function sanitize_validate_text( $option ) {
1947
		if ( is_array( $option ) ) {
0 ignored issues
show
introduced by
The condition is_array($option) is always false.
Loading history...
1948
			$options = array();
1949
			foreach ( $option as $key => $value ) {
1950
				$options[ $key ] = sanitize_text_field( $value );
1951
			}
1952
			return $options;
1953
		}
1954
		$option = sanitize_text_field( $option );
1955
		return $option;
1956
	}
1957
1958
	/**
1959
	* Run a demo of Salesforce API call on the authenticate tab after WordPress has authenticated with it
1960
	*
1961
	* @param object $sfapi
1962
	*/
1963
	private function status( $sfapi ) {
1964
1965
		$versions = $sfapi->get_api_versions();
1966
1967
		// format this array into text so users can see the versions
1968
		if ( true === $versions['cached'] ) {
1969
			$versions_is_cached = esc_html__( 'This list is cached, and', 'object-sync-salesforce' );
1970
		} else {
1971
			$versions_is_cached = esc_html__( 'This list is not cached, but', 'object-sync-salesforce' );
1972
		}
1973
1974
		if ( true === $versions['from_cache'] ) {
1975
			$versions_from_cache = esc_html__( 'items were loaded from the cache', 'object-sync-salesforce' );
1976
		} else {
1977
			$versions_from_cache = esc_html__( 'items were not loaded from the cache', 'object-sync-salesforce' );
1978
		}
1979
1980
		// translators: 1) $versions_is_cached is the "This list is/is not cached, and/but" line, 2) $versions_from_cache is the "items were/were not loaded from the cache" line
1981
		$versions_apicall_summary = sprintf( esc_html__( 'Available Salesforce API versions. %1$s %2$s. This is not an authenticated request, so it does not touch the Salesforce token.', 'object-sync-for-salesforce' ),
1982
			$versions_is_cached,
1983
			$versions_from_cache
1984
		);
1985
1986
		$contacts = $sfapi->query( 'SELECT Name, Id from Contact LIMIT 100' );
1987
1988
		// format this array into html so users can see the contacts
1989
		if ( true === $contacts['cached'] ) {
1990
			$contacts_is_cached = esc_html__( 'They are cached, and', 'object-sync-salesforce' );
1991
		} else {
1992
			$contacts_is_cached = esc_html__( 'They are not cached, but', 'object-sync-salesforce' );
1993
		}
1994
1995
		if ( true === $contacts['from_cache'] ) {
1996
			$contacts_from_cache = esc_html__( 'they were loaded from the cache', 'object-sync-salesforce' );
1997
		} else {
1998
			$contacts_from_cache = esc_html__( 'they were not loaded from the cache', 'object-sync-salesforce' );
1999
		}
2000
2001
		if ( true === $contacts['is_redo'] ) {
2002
			$contacts_refreshed_token = esc_html__( 'This request did require refreshing the Salesforce token', 'object-sync-salesforce' );
2003
		} else {
2004
			$contacts_refreshed_token = esc_html__( 'This request did not require refreshing the Salesforce token', 'object-sync-salesforce' );
2005
		}
2006
2007
		// translators: 1) $contacts['data']['totalSize'] is the number of items loaded, 2) $contacts['data']['records'][0]['attributes']['type'] is the name of the Salesforce object, 3) $contacts_is_cached is the "They are/are not cached, and/but" line, 4) $contacts_from_cache is the "they were/were not loaded from the cache" line, 5) is the "this request did/did not require refreshing the Salesforce token" line
2008
		$contacts_apicall_summary = sprintf( esc_html__( 'Salesforce successfully returned %1$s %2$s records. %3$s %4$s. %5$s.', 'object-sync-for-salesforce' ),
2009
			absint( $contacts['data']['totalSize'] ),
2010
			esc_html( $contacts['data']['records'][0]['attributes']['type'] ),
2011
			$contacts_is_cached,
2012
			$contacts_from_cache,
2013
			$contacts_refreshed_token
2014
		);
2015
2016
		require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/status.php' );
2017
2018
	}
2019
2020
	/**
2021
	* Deauthorize WordPress from Salesforce.
2022
	* This deletes the tokens from the database; it does not currently do anything in Salesforce
2023
	* For this plugin at this time, that is the decision we are making: don't do any kind of authorization stuff inside Salesforce
2024
	*/
2025
	private function logout() {
2026
		$this->access_token  = delete_option( $this->option_prefix . 'access_token' );
2027
		$this->instance_url  = delete_option( $this->option_prefix . 'instance_url' );
2028
		$this->refresh_token = delete_option( $this->option_prefix . 'refresh_token' );
2029
		echo sprintf( '<p>You have been logged out. You can use the <a href="%1$s">%2$s</a> tab to log in again.</p>',
2030
			esc_url( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=authorize' ) ),
2031
			esc_html__( 'Authorize', 'object-sync-for-salesforce' )
2032
		);
2033
	}
2034
2035
	/**
2036
	* Ajax call to clear the plugin cache.
2037
	*/
2038
	public function clear_sfwp_cache() {
2039
		$result   = $this->clear_cache( true );
2040
		$response = array(
2041
			'message' => $result['message'],
2042
			'success' => $result['success'],
2043
		);
2044
		wp_send_json_success( $response );
2045
	}
2046
2047
	/**
2048
	* Clear the plugin's cache.
2049
	* This uses the flush method contained in the WordPress cache to clear all of this plugin's cached data.
2050
	*/
2051
	private function clear_cache( $ajax = false ) {
2052
		$result = (bool) $this->wordpress->sfwp_transients->flush();
2053
		if ( true === $result ) {
2054
			$message = __( 'The plugin cache has been cleared.', 'object-sync-for-salesforce' );
2055
		} else {
2056
			$message = __( 'There was an error clearing the plugin cache. Try refreshing this page.', 'object-sync-for-salesforce' );
2057
		}
2058
		if ( false === $ajax ) {
2059
			// translators: parameter 1 is the result message
2060
			echo sprintf( '<p>%1$s</p>', $message );
2061
		} else {
2062
			return array(
2063
				'message' => $message,
2064
				'success' => $result,
2065
			);
2066
		}
2067
	}
2068
2069
	/**
2070
	* Check WordPress Admin permissions
2071
	* Check if the current user is allowed to access the Salesforce plugin options
2072
	*/
2073
	private function check_wordpress_admin_permissions() {
2074
2075
		// one programmatic way to give this capability to additional user roles is the
2076
		// object_sync_for_salesforce_roles_configure_salesforce hook
2077
		// it runs on activation of this plugin, and will assign the below capability to any role
2078
		// coming from the hook
2079
2080
		// alternatively, other roles can get this capability in whatever other way you like
2081
		// point is: to administer this plugin, you need this capability
2082
2083
		if ( ! current_user_can( 'configure_salesforce' ) ) {
2084
			return false;
2085
		} else {
2086
			return true;
2087
		}
2088
2089
	}
2090
2091
	/**
2092
	* Show what we know about this user's relationship to a Salesforce object, if any
2093
	* @param object $user
2094
	*
2095
	*/
2096
	public function show_salesforce_user_fields( $user ) {
2097
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2098
		if ( true === $this->check_wordpress_admin_permissions() ) {
2099
			$mappings = $this->mappings->load_all_by_wordpress( 'user', $user->ID );
2100
			$fieldmap = $this->mappings->get_fieldmaps(
2101
				null, // id field must be null for multiples
2102
				array(
2103
					'wordpress_object' => 'user',
2104
				)
2105
			);
2106
			if ( count( $mappings ) > 0 ) {
2107
				foreach ( $mappings as $mapping ) {
2108
					if ( isset( $mapping['id'] ) && ! isset( $get_data['edit_salesforce_mapping'] ) && ! isset( $get_data['delete_salesforce_mapping'] ) ) {
2109
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/user-profile-salesforce.php' );
2110
					} elseif ( ! empty( $fieldmap ) ) { // is the user mapped to something already?
2111
						if ( isset( $get_data['edit_salesforce_mapping'] ) && true === filter_var( $get_data['edit_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2112
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/user-profile-salesforce-change.php' );
2113
						} elseif ( isset( $get_data['delete_salesforce_mapping'] ) && true === filter_var( $get_data['delete_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2114
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/user-profile-salesforce-delete.php' );
2115
						} else {
2116
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/user-profile-salesforce-map.php' );
2117
						}
2118
					}
2119
				}
2120
			} else {
2121
				require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/user-profile-salesforce-map.php' );
2122
			}
2123
		}
2124
	}
2125
2126
	/**
2127
	* If the user profile has been mapped to Salesforce, do it
2128
	* @param int $user_id
2129
	*
2130
	*/
2131
	public function save_salesforce_user_fields( $user_id ) {
2132
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
2133
		if ( isset( $post_data['salesforce_update_mapped_user'] ) && true === filter_var( $post_data['salesforce_update_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2134
			$mapping_object                  = $this->mappings->get_object_maps(
2135
				array(
2136
					'wordpress_id'     => $user_id,
2137
					'wordpress_object' => 'user',
2138
				)
2139
			);
2140
			$mapping_object['salesforce_id'] = $post_data['salesforce_id'];
2141
2142
			$result = $this->mappings->update_object_map( $mapping_object, $mapping_object['id'] );
2143
		} elseif ( isset( $post_data['salesforce_create_mapped_user'] ) && true === filter_var( $post_data['salesforce_create_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2144
			// if a Salesforce ID was entered
2145
			if ( isset( $post_data['salesforce_id'] ) && ! empty( $post_data['salesforce_id'] ) ) {
2146
				$mapping_object = $this->create_object_map( $user_id, 'user', $post_data['salesforce_id'] );
2147
			} elseif ( isset( $post_data['push_new_user_to_salesforce'] ) ) {
2148
				// otherwise, create a new record in Salesforce
2149
				$result = $this->push_to_salesforce( 'user', $user_id );
2150
			}
2151
		} elseif ( isset( $post_data['salesforce_delete_mapped_user'] ) && true === filter_var( $post_data['salesforce_delete_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2152
			// if a Salesforce ID was entered
2153
			if ( isset( $post_data['mapping_id'] ) && ! empty( $post_data['mapping_id'] ) ) {
2154
				$delete = $this->mappings->delete_object_map( $post_data['mapping_id'] );
2155
			}
2156
		}
2157
	}
2158
2159
	/**
2160
	* Render tabs for settings pages in admin
2161
	* @param array $tabs
2162
	* @param string $tab
2163
	*/
2164
	private function tabs( $tabs, $tab = '' ) {
2165
2166
		$get_data        = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2167
		$consumer_key    = $this->login_credentials['consumer_key'];
2168
		$consumer_secret = $this->login_credentials['consumer_secret'];
2169
		$callback_url    = $this->login_credentials['callback_url'];
2170
2171
		$current_tab = $tab;
2172
		echo '<h2 class="nav-tab-wrapper">';
2173
		foreach ( $tabs as $tab_key => $tab_caption ) {
2174
			$active = $current_tab === $tab_key ? ' nav-tab-active' : '';
2175
			if ( 'settings' === $tab_key || ( isset( $consumer_key ) && isset( $consumer_secret ) && ! empty( $consumer_key ) && ! empty( $consumer_secret ) ) ) {
2176
				echo sprintf( '<a class="nav-tab%1$s" href="%2$s">%3$s</a>',
2177
					esc_attr( $active ),
2178
					esc_url( '?page=object-sync-salesforce-admin&tab=' . $tab_key ),
2179
					esc_html( $tab_caption )
2180
				);
2181
			}
2182
		}
2183
		echo '</h2>';
2184
2185
		if ( isset( $get_data['tab'] ) ) {
2186
			$tab = sanitize_key( $get_data['tab'] );
2187
		} else {
2188
			$tab = '';
2189
		}
2190
	}
2191
2192
	/**
2193
	* Clear schedule
2194
	* This clears the schedule if the user clicks the button
2195
	* @param string $schedule_name
2196
	*/
2197
	private function clear_schedule( $schedule_name = '' ) {
2198
		if ( '' !== $schedule_name ) {
2199
			$this->queue->cancel( $schedule_name );
2200
			// translators: $schedule_name is the name of the current queue. Defaults: salesforce_pull, salesforce_push, salesforce
2201
			echo sprintf( esc_html__( 'You have cleared the %s schedule.', 'object-sync-for-salesforce' ), esc_html( $schedule_name ) );
2202
		} else {
2203
			echo esc_html__( 'You need to specify the name of the schedule you want to clear.', 'object-sync-for-salesforce' );
2204
		}
2205
	}
2206
2207
	/**
2208
	* Get count of schedule items
2209
	* @param string $schedule_name
2210
	* @return int $count
2211
	*/
2212
	private function get_schedule_count( $schedule_name = '' ) {
2213
		if ( '' !== $schedule_name ) {
2214
			$count       = count( $this->queue->search(
2215
				array(
2216
					'group'  => $schedule_name,
2217
					'status' => ActionScheduler_Store::STATUS_PENDING,
2218
				),
2219
				'ARRAY_A'
2220
			) );
2221
			$group_count = count( $this->queue->search(
2222
				array(
2223
					'group'  => $schedule_name . $this->action_group_suffix,
2224
					'status' => ActionScheduler_Store::STATUS_PENDING,
2225
				),
2226
				'ARRAY_A'
2227
			) );
2228
			return $count + $group_count;
2229
		} else {
2230
			return 0;
2231
		}
2232
	}
2233
2234
	/**
2235
	* Create an object map between a WordPress object and a Salesforce object
2236
	*
2237
	* @param int $wordpress_id
2238
	*   Unique identifier for the WordPress object
2239
	* @param string $wordpress_object
2240
	*   What kind of object is it?
2241
	* @param string $salesforce_id
2242
	*   Unique identifier for the Salesforce object
2243
	* @param string $action
2244
	*   Did we push or pull?
2245
	*
2246
	* @return int $wpdb->insert_id
2247
	*   This is the database row for the map object
2248
	*
2249
	*/
2250
	private function create_object_map( $wordpress_id, $wordpress_object, $salesforce_id, $action = '' ) {
2251
		// Create object map and save it
2252
		$mapping_object = $this->mappings->create_object_map(
2253
			array(
2254
				'wordpress_id'      => $wordpress_id, // wordpress unique id
2255
				'salesforce_id'     => $salesforce_id, // salesforce unique id. we don't care what kind of object it is at this point
2256
				'wordpress_object'  => $wordpress_object, // keep track of what kind of wp object this is
2257
				'last_sync'         => current_time( 'mysql' ),
2258
				'last_sync_action'  => $action,
2259
				'last_sync_status'  => $this->mappings->status_success,
2260
				'last_sync_message' => __( 'Mapping object updated via function: ', 'object-sync-for-salesforce' ) . __FUNCTION__,
2261
			)
2262
		);
2263
2264
		return $mapping_object;
2265
2266
	}
2267
2268
}
2269