Completed
Push — master ( 306226...251949 )
by Jonathan
03:43 queued 10s
created

Object_Sync_Sf_Admin::fields_settings()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 266
Code Lines 208

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 6
eloc 208
c 6
b 0
f 0
nc 6
nop 3
dl 0
loc 266
rs 7.3777

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
			'use_soap'                       => array(
715
				'title'    => __( 'Enable the Salesforce SOAP API?', 'object-sync-for-salesforce' ),
716
				'callback' => $callbacks['text'],
717
				'page'     => $page,
718
				'section'  => $section,
719
				'args'     => array(
720
					'type'     => 'checkbox',
721
					'validate' => 'sanitize_text_field',
722
					'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' ),
723
					'constant' => '',
724
				),
725
			),
726
			'soap_wsdl_path'                 => array(
727
				'title'    => __( 'Path to SOAP WSDL file', 'object-sync-for-salesforce' ),
728
				'callback' => $callbacks['text'],
729
				'page'     => $page,
730
				'section'  => $section,
731
				'args'     => array(
732
					'type'     => 'text',
733
					'validate' => 'sanitize_text_field',
734
					'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' ),
735
					'constant' => '',
736
				),
737
			),
738
			'debug_mode'                     => array(
739
				'title'    => __( 'Debug mode?', 'object-sync-for-salesforce' ),
740
				'callback' => $callbacks['text'],
741
				'page'     => $page,
742
				'section'  => $section,
743
				'args'     => array(
744
					'type'     => 'checkbox',
745
					'validate' => 'sanitize_text_field',
746
					'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' ),
747
					'constant' => '',
748
				),
749
			),
750
			'delete_data_on_uninstall'       => array(
751
				'title'    => __( 'Delete plugin data on uninstall?', 'object-sync-for-salesforce' ),
752
				'callback' => $callbacks['text'],
753
				'page'     => $page,
754
				'section'  => $section,
755
				'args'     => array(
756
					'type'     => 'checkbox',
757
					'validate' => 'sanitize_text_field',
758
					'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' ),
759
					'constant' => '',
760
				),
761
			),
762
		);
763
764
		if ( true === is_object( $this->salesforce['sfapi'] ) && true === $this->salesforce['sfapi']->is_authorized() ) {
765
			$salesforce_settings['api_version'] = array(
766
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
767
				'callback' => $callbacks['select'],
768
				'page'     => $page,
769
				'section'  => $section,
770
				'args'     => array(
771
					'type'     => 'select',
772
					'validate' => 'sanitize_text_field',
773
					'desc'     => '',
774
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
775
					'items'    => $this->version_options(),
776
				),
777
			);
778
		}
779
780
		foreach ( $salesforce_settings as $key => $attributes ) {
781
			$id       = $this->option_prefix . $key;
782
			$name     = $this->option_prefix . $key;
783
			$title    = $attributes['title'];
784
			$callback = $attributes['callback'];
785
			$validate = $attributes['args']['validate'];
786
			$page     = $attributes['page'];
787
			$section  = $attributes['section'];
788
			$args     = array_merge(
789
				$attributes['args'],
790
				array(
791
					'title'     => $title,
792
					'id'        => $id,
793
					'label_for' => $id,
794
					'name'      => $name,
795
				)
796
			);
797
798
			// if there is a constant and it is defined, don't run a validate function
799
			if ( isset( $attributes['args']['constant'] ) && defined( $attributes['args']['constant'] ) ) {
800
				$validate = '';
801
			}
802
803
			add_settings_field( $id, $title, $callback, $page, $section, $args );
804
			register_setting( $page, $id, array( $this, $validate ) );
805
		}
806
	}
807
808
	/**
809
	* Fields for the Fieldmaps tab
810
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
811
	*
812
	* @param string $page
813
	* @param string $section
814
	* @param string $input_callback
815
	*/
816
	private function fields_fieldmaps( $page, $section, $input_callback = '' ) {
817
		add_settings_section( $page, ucwords( $page ), null, $page );
818
	}
819
820
	/**
821
	* Fields for the Scheduling tab
822
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
823
	*
824
	* @param string $page
825
	* @param string $section
826
	* @param string $input_callback
827
	*/
828
	private function fields_scheduling( $page, $section, $callbacks ) {
829
830
		add_settings_section( 'batch', __( 'Batch Settings', 'object-sync-for-salesforce' ), null, $page );
831
		$section           = 'batch';
832
		$schedule_settings = array(
833
			'action_scheduler_batch_size'         => array(
834
				'title'    => __( 'Batch size', 'object-sync-for-salesforce' ),
835
				'callback' => $callbacks['text'],
836
				'page'     => $page,
837
				'section'  => $section,
838
				'args'     => array(
839
					'type'     => 'number',
840
					'validate' => 'absint',
841
					'default'  => 5,
842
					'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' ),
843
					'constant' => '',
844
				),
845
846
			),
847
			'action_scheduler_concurrent_batches' => array(
848
				'title'    => __( 'Concurrent batches', 'object-sync-for-salesforce' ),
849
				'callback' => $callbacks['text'],
850
				'page'     => $page,
851
				'section'  => $section,
852
				'args'     => array(
853
					'type'     => 'number',
854
					'validate' => 'absint',
855
					'default'  => 3,
856
					'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' ),
857
					'constant' => '',
858
				),
859
			),
860
		);
861
862
		foreach ( $this->schedulable_classes as $key => $value ) {
863
			add_settings_section( $key, $value['label'], null, $page );
864
			if ( isset( $value['initializer'] ) ) {
865
				$schedule_settings[ $key . '_schedule_number' ] = array(
866
					'title'    => __( 'Run schedule every', 'object-sync-for-salesforce' ),
867
					'callback' => $callbacks['text'],
868
					'page'     => $page,
869
					'section'  => $key,
870
					'args'     => array(
871
						'type'     => 'number',
872
						'validate' => 'absint',
873
						'desc'     => '',
874
						'constant' => '',
875
					),
876
				);
877
				$schedule_settings[ $key . '_schedule_unit' ]   = array(
878
					'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
879
					'callback' => $callbacks['select'],
880
					'page'     => $page,
881
					'section'  => $key,
882
					'args'     => array(
883
						'type'     => 'select',
884
						'validate' => 'sanitize_validate_text',
885
						'desc'     => '',
886
						'items'    => array(
887
							'minutes' => array(
888
								'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
889
								'value' => 'minutes',
890
							),
891
							'hours'   => array(
892
								'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
893
								'value' => 'hours',
894
							),
895
							'days'    => array(
896
								'text'  => __( 'Days', 'object-sync-for-salesforce' ),
897
								'value' => 'days',
898
							),
899
						),
900
					),
901
				);
902
			}
903
			$schedule_settings[ $key . '_clear_button' ] = array(
904
				// translators: $this->get_schedule_count is an integer showing how many items are in the current queue
905
				'title'    => sprintf( 'This queue has ' . _n( '%s item', '%s items', $this->get_schedule_count( $key ), 'object-sync-for-salesforce' ), $this->get_schedule_count( $key ) ),
906
				'callback' => $callbacks['link'],
907
				'page'     => $page,
908
				'section'  => $key,
909
				'args'     => array(
910
					'label'      => __( 'Clear this queue', 'object-sync-for-salesforce' ),
911
					'desc'       => '',
912
					'url'        => esc_url( '?page=object-sync-salesforce-admin&amp;tab=clear_schedule&amp;schedule_name=' . $key ),
913
					'link_class' => 'button button-secondary',
914
				),
915
			);
916
			foreach ( $schedule_settings as $key => $attributes ) {
917
				$id       = $this->option_prefix . $key;
918
				$name     = $this->option_prefix . $key;
919
				$title    = $attributes['title'];
920
				$callback = $attributes['callback'];
921
				$page     = $attributes['page'];
922
				$section  = $attributes['section'];
923
				$args     = array_merge(
924
					$attributes['args'],
925
					array(
926
						'title'     => $title,
927
						'id'        => $id,
928
						'label_for' => $id,
929
						'name'      => $name,
930
					)
931
				);
932
				add_settings_field( $id, $title, $callback, $page, $section, $args );
933
				register_setting( $page, $id );
934
			}
935
		} // End foreach().
936
	}
937
938
	/**
939
	* Fields for the Log Settings tab
940
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
941
	*
942
	* @param string $page
943
	* @param string $section
944
	* @param array $callbacks
945
	*/
946
	private function fields_log_settings( $page, $section, $callbacks ) {
947
		add_settings_section( $page, ucwords( str_replace( '_', ' ', $page ) ), null, $page );
948
		$log_settings = array(
949
			'enable_logging'        => array(
950
				'title'    => __( 'Enable Logging?', 'object-sync-for-salesforce' ),
951
				'callback' => $callbacks['text'],
952
				'page'     => $page,
953
				'section'  => $section,
954
				'args'     => array(
955
					'type'     => 'checkbox',
956
					'validate' => 'absint',
957
					'desc'     => '',
958
					'constant' => '',
959
				),
960
			),
961
			'statuses_to_log'       => array(
962
				'title'    => __( 'Statuses to log', 'object-sync-for-salesforce' ),
963
				'callback' => $callbacks['checkboxes'],
964
				'page'     => $page,
965
				'section'  => $section,
966
				'args'     => array(
967
					'type'     => 'checkboxes',
968
					'validate' => 'sanitize_validate_text',
969
					'desc'     => __( 'these are the statuses to log', 'object-sync-for-salesforce' ),
970
					'items'    => array(
971
						'error'   => array(
972
							'text' => __( 'Error', 'object-sync-for-salesforce' ),
973
							'id'   => 'error',
974
							'desc' => '',
975
						),
976
						'success' => array(
977
							'text' => __( 'Success', 'object-sync-for-salesforce' ),
978
							'id'   => 'success',
979
							'desc' => '',
980
						),
981
						'notice'  => array(
982
							'text' => __( 'Notice', 'object-sync-for-salesforce' ),
983
							'id'   => 'notice',
984
							'desc' => '',
985
						),
986
						'debug'   => array(
987
							'text' => __( 'Debug', 'object-sync-for-salesforce' ),
988
							'id'   => 'debug',
989
							'desc' => '',
990
						),
991
					),
992
				),
993
			),
994
			'prune_logs'            => array(
995
				'title'    => __( 'Automatically delete old log entries?', 'object-sync-for-salesforce' ),
996
				'callback' => $callbacks['text'],
997
				'page'     => $page,
998
				'section'  => $section,
999
				'args'     => array(
1000
					'type'     => 'checkbox',
1001
					'validate' => 'absint',
1002
					'desc'     => '',
1003
					'constant' => '',
1004
				),
1005
			),
1006
			'logs_how_old'          => array(
1007
				'title'    => __( 'Age to delete log entries', 'object-sync-for-salesforce' ),
1008
				'callback' => $callbacks['text'],
1009
				'page'     => $page,
1010
				'section'  => $section,
1011
				'args'     => array(
1012
					'type'     => 'text',
1013
					'validate' => 'sanitize_validate_text',
1014
					'desc'     => __( 'If automatic deleting is enabled, it will affect logs this old.', 'object-sync-for-salesforce' ),
1015
					'default'  => '2 weeks',
1016
					'constant' => '',
1017
				),
1018
			),
1019
			'logs_how_often_number' => array(
1020
				'title'    => __( 'Check for old logs every', 'object-sync-for-salesforce' ),
1021
				'callback' => $callbacks['text'],
1022
				'page'     => $page,
1023
				'section'  => $section,
1024
				'args'     => array(
1025
					'type'     => 'number',
1026
					'validate' => 'absint',
1027
					'desc'     => '',
1028
					'default'  => '1',
1029
					'constant' => '',
1030
				),
1031
			),
1032
			'logs_how_often_unit'   => array(
1033
				'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
1034
				'callback' => $callbacks['select'],
1035
				'page'     => $page,
1036
				'section'  => $section,
1037
				'args'     => array(
1038
					'type'     => 'select',
1039
					'validate' => 'sanitize_validate_text',
1040
					'desc'     => __( 'These two fields are how often the site will check for logs to delete.', 'object-sync-for-salesforce' ),
1041
					'items'    => array(
1042
						'minutes' => array(
1043
							'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
1044
							'value' => 'minutes',
1045
						),
1046
						'hours'   => array(
1047
							'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
1048
							'value' => 'hours',
1049
						),
1050
						'days'    => array(
1051
							'text'  => __( 'Days', 'object-sync-for-salesforce' ),
1052
							'value' => 'days',
1053
						),
1054
					),
1055
				),
1056
			),
1057
			'triggers_to_log'       => array(
1058
				'title'    => __( 'Triggers to log', 'object-sync-for-salesforce' ),
1059
				'callback' => $callbacks['checkboxes'],
1060
				'page'     => $page,
1061
				'section'  => $section,
1062
				'args'     => array(
1063
					'type'     => 'checkboxes',
1064
					'validate' => 'sanitize_validate_text',
1065
					'desc'     => __( 'these are the triggers to log', 'object-sync-for-salesforce' ),
1066
					'items'    => array(
1067
						$this->mappings->sync_wordpress_create => array(
1068
							'text' => __( 'WordPress create', 'object-sync-for-salesforce' ),
1069
							'id'   => 'wordpress_create',
1070
							'desc' => '',
1071
						),
1072
						$this->mappings->sync_wordpress_update => array(
1073
							'text' => __( 'WordPress update', 'object-sync-for-salesforce' ),
1074
							'id'   => 'wordpress_update',
1075
							'desc' => '',
1076
						),
1077
						$this->mappings->sync_wordpress_delete => array(
1078
							'text' => __( 'WordPress delete', 'object-sync-for-salesforce' ),
1079
							'id'   => 'wordpress_delete',
1080
							'desc' => '',
1081
						),
1082
						$this->mappings->sync_sf_create => array(
1083
							'text' => __( 'Salesforce create', 'object-sync-for-salesforce' ),
1084
							'id'   => 'sf_create',
1085
							'desc' => '',
1086
						),
1087
						$this->mappings->sync_sf_update => array(
1088
							'text' => __( 'Salesforce update', 'object-sync-for-salesforce' ),
1089
							'id'   => 'sf_update',
1090
							'desc' => '',
1091
						),
1092
						$this->mappings->sync_sf_delete => array(
1093
							'text' => __( 'Salesforce delete', 'object-sync-for-salesforce' ),
1094
							'id'   => 'sf_delete',
1095
							'desc' => '',
1096
						),
1097
					),
1098
				),
1099
			),
1100
		);
1101
		foreach ( $log_settings as $key => $attributes ) {
1102
			$id       = $this->option_prefix . $key;
1103
			$name     = $this->option_prefix . $key;
1104
			$title    = $attributes['title'];
1105
			$callback = $attributes['callback'];
1106
			$page     = $attributes['page'];
1107
			$section  = $attributes['section'];
1108
			$args     = array_merge(
1109
				$attributes['args'],
1110
				array(
1111
					'title'     => $title,
1112
					'id'        => $id,
1113
					'label_for' => $id,
1114
					'name'      => $name,
1115
				)
1116
			);
1117
			add_settings_field( $id, $title, $callback, $page, $section, $args );
1118
			register_setting( $page, $id );
1119
		}
1120
	}
1121
1122
	/**
1123
	* Create the notices, settings, and conditions by which admin notices should appear
1124
	*
1125
	*/
1126
	public function notices() {
1127
1128
		// before a notice is displayed, we should make sure we are on a page related to this plugin
1129
		if ( ! isset( $_GET['page'] ) || 'object-sync-salesforce-admin' !== $_GET['page'] ) {
1130
			return;
1131
		}
1132
1133
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
1134
		require_once plugin_dir_path( __FILE__ ) . '../classes/admin-notice.php';
1135
1136
		$notices = array(
1137
			'permission'              => array(
1138
				'condition'   => false === $this->check_wordpress_admin_permissions(),
1139
				'message'     => __( "Your account does not have permission to edit the Salesforce REST API plugin's settings.", 'object-sync-for-salesforce' ),
1140
				'type'        => 'error',
1141
				'dismissible' => false,
1142
			),
1143
			'fieldmap'                => array(
1144
				'condition'   => isset( $get_data['transient'] ),
1145
				'message'     => __( 'Errors kept this fieldmap from being saved.', 'object-sync-for-salesforce' ),
1146
				'type'        => 'error',
1147
				'dismissible' => true,
1148
			),
1149
			'object_map'              => array(
1150
				'condition'   => isset( $get_data['map_transient'] ),
1151
				'message'     => __( 'Errors kept this object map from being saved.', 'object-sync-for-salesforce' ),
1152
				'type'        => 'error',
1153
				'dismissible' => true,
1154
			),
1155
			'data_saved'              => array(
1156
				'condition'   => isset( $get_data['data_saved'] ) && 'true' === $get_data['data_saved'],
1157
				'message'     => __( 'This data was successfully saved.', 'object-sync-for-salesforce' ),
1158
				'type'        => 'success',
1159
				'dismissible' => true,
1160
			),
1161
			'data_save_error'         => array(
1162
				'condition'   => isset( $get_data['data_saved'] ) && 'false' === $get_data['data_saved'],
1163
				'message'     => __( 'This data was not successfully saved. Try again.', 'object-sync-for-salesforce' ),
1164
				'type'        => 'error',
1165
				'dismissible' => true,
1166
			),
1167
			'mapping_error_transient' => array(
1168
				'condition'   => isset( $get_data['mapping_error_transient'] ),
1169
				'message'     => __( 'Errors kept these mapping errors from being deleted.', 'object-sync-for-salesforce' ),
1170
				'type'        => 'error',
1171
				'dismissible' => true,
1172
			),
1173
		);
1174
1175
		foreach ( $notices as $key => $value ) {
1176
1177
			$condition = $value['condition'];
1178
			$message   = $value['message'];
1179
1180
			if ( isset( $value['dismissible'] ) ) {
1181
				$dismissible = $value['dismissible'];
1182
			} else {
1183
				$dismissible = false;
1184
			}
1185
1186
			if ( isset( $value['type'] ) ) {
1187
				$type = $value['type'];
1188
			} else {
1189
				$type = '';
1190
			}
1191
1192
			if ( ! isset( $value['template'] ) ) {
1193
				$template = '';
1194
			}
1195
1196
			if ( $condition ) {
1197
				new Object_Sync_Sf_Admin_Notice( $condition, $message, $dismissible, $type, $template );
1198
			}
1199
		}
1200
1201
	}
1202
1203
	/**
1204
	* Get all the Salesforce object settings for fieldmapping
1205
	* This takes either the $_POST array via ajax, or can be directly called with a $data array
1206
	*
1207
	* @param array $data
1208
	* data must contain a salesforce_object
1209
	* can optionally contain a type
1210
	* @return array $object_settings
1211
	*/
1212
	public function get_salesforce_object_description( $data = array() ) {
1213
		$ajax = false;
1214
		if ( empty( $data ) ) {
1215
			$data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1216
			$ajax = true;
1217
		}
1218
1219
		$object_description = array();
1220
1221
		if ( ! empty( $data['salesforce_object'] ) ) {
1222
			$object = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1223
1224
			$object_fields        = array();
1225
			$include_record_types = array();
1226
1227
			// these can come from ajax
1228
			$include = isset( $data['include'] ) ? (array) $data['include'] : array();
1229
			$include = array_map( 'esc_attr', $include );
1230
1231
			if ( in_array( 'fields', $include, true ) || empty( $include ) ) {
1232
				$type = isset( $data['field_type'] ) ? esc_attr( $data['field_type'] ) : ''; // can come from ajax
1233
				foreach ( $object['data']['fields'] as $key => $value ) {
1234
					if ( '' === $type || $type === $value['type'] ) {
1235
						$object_fields[ $key ] = $value;
1236
					}
1237
				}
1238
				$object_description['fields'] = $object_fields;
1239
			}
1240
1241
			if ( in_array( 'recordTypeInfos', $include, true ) ) {
1242
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1243
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1244
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1245
					}
1246
					$object_description['recordTypeInfos'] = $object_record_types;
1247
				}
1248
			}
1249
		}
1250
1251
		if ( true === $ajax ) {
1252
			wp_send_json_success( $object_description );
1253
		} else {
1254
			return $object_description;
1255
		}
1256
	}
1257
1258
	/**
1259
	* Get Salesforce object fields for fieldmapping
1260
	*
1261
	* @param array $data
1262
	* data must contain a salesforce_object
1263
	* can optionally contain a type for the field
1264
	* @return array $object_fields
1265
	*/
1266
	public function get_salesforce_object_fields( $data = array() ) {
1267
1268
		if ( ! empty( $data['salesforce_object'] ) ) {
1269
			$object               = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1270
			$object_fields        = array();
1271
			$type                 = isset( $data['type'] ) ? esc_attr( $data['type'] ) : '';
1272
			$include_record_types = isset( $data['include_record_types'] ) ? esc_attr( $data['include_record_types'] ) : false;
1273
			foreach ( $object['data']['fields'] as $key => $value ) {
1274
				if ( '' === $type || $type === $value['type'] ) {
1275
					$object_fields[ $key ] = $value;
1276
				}
1277
			}
1278
			if ( true === $include_record_types ) {
0 ignored issues
show
introduced by
The condition true === $include_record_types is always false.
Loading history...
1279
				$object_record_types = array();
1280
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1281
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1282
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1283
					}
1284
				}
1285
			}
1286
		}
1287
1288
		return $object_fields;
1289
1290
	}
1291
1292
	/**
1293
	* Get WordPress object fields for fieldmapping
1294
	* This takes either the $_POST array via ajax, or can be directly called with a $wordpress_object field
1295
	*
1296
	* @param string $wordpress_object
1297
	* @return array $object_fields
1298
	*/
1299
	public function get_wordpress_object_fields( $wordpress_object = '' ) {
1300
		$ajax      = false;
1301
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1302
		if ( empty( $wordpress_object ) ) {
1303
			$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

1303
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1304
			$ajax             = true;
1305
		}
1306
1307
		$object_fields = $this->wordpress->get_wordpress_object_fields( $wordpress_object );
1308
1309
		if ( true === $ajax ) {
1310
			wp_send_json_success( $object_fields );
1311
		} else {
1312
			return $object_fields;
1313
		}
1314
	}
1315
1316
	/**
1317
	* Get WordPress and Salesforce object fields together for fieldmapping
1318
	* This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $salesforce_object fields
1319
	*
1320
	* @param string $wordpress_object
1321
	* @param string $salesforce_object
1322
	* @return array $object_fields
1323
	*/
1324
	public function get_wp_sf_object_fields( $wordpress_object = '', $salesforce = '' ) {
1325
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1326
		if ( empty( $wordpress_object ) ) {
1327
			$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

1327
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1328
		}
1329
		if ( empty( $salesforce_object ) ) {
1330
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_object'] ) ) : '';
1331
		}
1332
1333
		$object_fields['wordpress']  = $this->get_wordpress_object_fields( $wordpress_object );
1334
		$object_fields['salesforce'] = $this->get_salesforce_object_fields(
1335
			array(
1336
				'salesforce_object' => $salesforce_object,
1337
			)
1338
		);
1339
1340
		if ( ! empty( $post_data ) ) {
1341
			wp_send_json_success( $object_fields );
1342
		} else {
1343
			return $object_fields;
1344
		}
1345
	}
1346
1347
	/**
1348
	* Manually push the WordPress object to Salesforce
1349
	* This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $wordpress_id fields
1350
	*
1351
	* @param string $wordpress_object
1352
	* @param int $wordpress_id
1353
	*/
1354
	public function push_to_salesforce( $wordpress_object = '', $wordpress_id = '' ) {
1355
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1356
		if ( empty( $wordpress_object ) && empty( $wordpress_id ) ) {
1357
			$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

1357
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1358
			$wordpress_id     = isset( $post_data['wordpress_id'] ) ? absint( $post_data['wordpress_id'] ) : '';
1359
		}
1360
1361
		// clarify what that variable is in this context.
1362
		$object_type = $wordpress_object;
1363
1364
		// When objects are already mapped, there is a Salesforce id as well. Otherwise, it's blank.
1365
		$salesforce_id = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( $post_data['salesforce_id'] ) : '';
1366
		if ( '' === $salesforce_id ) {
1367
			$method = 'POST';
1368
		} else {
1369
			$method = 'PUT';
1370
		}
1371
1372
		$result = $this->push->manual_push( $object_type, $wordpress_id, $method );
1373
1374
		if ( ! empty( $post_data['wordpress_object'] ) && ! empty( $post_data['wordpress_id'] ) ) {
1375
			wp_send_json_success( $result );
1376
		} else {
1377
			return $result;
1378
		}
1379
1380
	}
1381
1382
	/**
1383
	* Manually pull the Salesforce object into WordPress
1384
	* This takes either the $_POST array via ajax, or can be directly called with $salesforce_id fields
1385
	*
1386
	* @param string $salesforce_id
1387
	* @param string $wordpress_object
1388
	*/
1389
	public function pull_from_salesforce( $salesforce_id = '', $wordpress_object = '' ) {
1390
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1391
		if ( empty( $wordpress_object ) && empty( $salesforce_id ) ) {
1392
			$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

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

1449
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1450
			}
1451
		} else { // there are no errors
1452
			// send the row to the fieldmap class
1453
			// if it is add or clone, use the create method
1454
			$method            = esc_attr( $post_data['method'] );
1455
			$salesforce_fields = $this->get_salesforce_object_fields(
1456
				array(
1457
					'salesforce_object' => $post_data['salesforce_object'],
1458
				)
1459
			);
1460
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1461
			if ( 'add' === $method || 'clone' === $method ) {
1462
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1463
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method
1464
				$id     = esc_attr( $post_data['id'] );
1465
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
1466
			}
1467
			if ( false === $result ) { // if the database didn't save, it's still an error
1468
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1469
				if ( '' !== $cachekey ) {
1470
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1471
				}
1472
			} else {
1473
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1474
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1475
				}
1476
				// then send the user to the list of fieldmaps
1477
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1478
			}
1479
		}
1480
		wp_safe_redirect( $url );
1481
		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...
1482
	}
1483
1484
	/**
1485
	* Delete fieldmap data and redirect after processing
1486
	* This runs when the delete link is clicked, after the user confirms
1487
	* It is public because it depends on an admin hook
1488
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1489
	*
1490
	*/
1491
	public function delete_fieldmap() {
1492
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1493
		if ( $post_data['id'] ) {
1494
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1495
			if ( true === $result ) {
1496
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1497
			} else {
1498
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1499
			}
1500
			wp_safe_redirect( $url );
1501
			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...
1502
		}
1503
	}
1504
1505
	/**
1506
	* Prepare object data and redirect after processing
1507
	* This runs when the update form is submitted
1508
	* It is public because it depends on an admin hook
1509
	* It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1510
	* This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1511
	*
1512
	*/
1513
	public function prepare_object_map_data() {
1514
		$error     = false;
1515
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1516
		$cachekey  = wp_json_encode( $post_data );
1517
		if ( false !== $cachekey ) {
1518
			$cachekey = md5( $cachekey );
1519
		}
1520
1521
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1522
			$error = true;
1523
		}
1524
		if ( true === $error ) {
1525
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1526
			if ( '' !== $cachekey ) {
1527
				$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

1527
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1528
			}
1529
		} else { // there are no errors
1530
			// send the row to the object map class
1531
			$method = esc_attr( $post_data['method'] );
1532
			if ( 'edit' === $method ) { // if it is edit, use the update method
1533
				$id     = esc_attr( $post_data['id'] );
1534
				$result = $this->mappings->update_object_map( $post_data, $id );
1535
			}
1536
			if ( false === $result ) { // if the database didn't save, it's still an error
1537
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1538
				if ( '' !== $cachekey ) {
1539
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1540
				}
1541
			} else {
1542
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1543
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1544
				}
1545
				// then send the user to the success redirect url
1546
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1547
			}
1548
		}
1549
		wp_safe_redirect( $url );
1550
		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...
1551
	}
1552
1553
	/**
1554
	* Delete object map data and redirect after processing
1555
	* This runs when the delete link is clicked on an error row, after the user confirms
1556
	* It is public because it depends on an admin hook
1557
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1558
	*
1559
	*/
1560
	public function delete_object_map() {
1561
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1562
		if ( isset( $post_data['id'] ) ) {
1563
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1564
			if ( true === $result ) {
1565
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1566
			} else {
1567
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1568
			}
1569
			wp_safe_redirect( $url );
1570
			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...
1571
		} elseif ( $post_data['delete'] ) {
1572
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1573
			$cachekey  = wp_json_encode( $post_data );
1574
			if ( false !== $cachekey ) {
1575
				$cachekey = md5( $cachekey );
1576
			}
1577
			$error = false;
1578
			if ( ! isset( $post_data['delete'] ) ) {
1579
				$error = true;
1580
			}
1581
			if ( true === $error ) {
1582
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1583
				if ( '' !== $cachekey ) {
1584
					$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

1584
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1585
				}
1586
			} else { // there are no errors
1587
				$result = $this->mappings->delete_object_map( array_keys( $post_data['delete'] ) );
1588
				if ( true === $result ) {
1589
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1590
				}
1591
1592
				if ( false === $result ) { // if the database didn't save, it's still an error
1593
					$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1594
					if ( '' !== $cachekey ) {
1595
						$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
1596
					}
1597
				} else {
1598
					if ( isset( $post_data['mapping_error_transient'] ) ) { // there was previously an error saved. can delete it now.
1599
						$this->sfwp_transients->delete( esc_attr( $post_data['mapping_error_transient'] ) );
1600
					}
1601
					// then send the user to the list of fieldmaps
1602
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1603
				}
1604
			}
1605
			wp_safe_redirect( $url );
1606
			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...
1607
		}
1608
	}
1609
1610
	/**
1611
	* Import a json file and use it for plugin data
1612
	*
1613
	*/
1614
	public function import_json_file() {
1615
1616
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_import'], 'object_sync_for_salesforce_nonce_import' ) ) {
1617
			return;
1618
		}
1619
		if ( ! current_user_can( 'manage_options' ) ) {
1620
			return;
1621
		}
1622
		$path      = $_FILES['import_file']['name'];
1623
		$extension = pathinfo( $path, PATHINFO_EXTENSION );
1624
		if ( 'json' !== $extension ) {
1625
			wp_die( __( 'Please upload a valid .json file' ) );
1626
		}
1627
1628
		$import_file = $_FILES['import_file']['tmp_name'];
1629
		if ( empty( $import_file ) ) {
1630
			wp_die( __( 'Please upload a file to import' ) );
1631
		}
1632
1633
		// Retrieve the data from the file and convert the json object to an array.
1634
		$data = (array) json_decode( file_get_contents( $import_file ), true );
1635
1636
		// if there is only one object map, fix the array
1637
		if ( isset( $data['object_maps'] ) ) {
1638
			if ( count( $data['object_maps'] ) === count( $data['object_maps'], COUNT_RECURSIVE ) ) {
1639
				$data['object_maps'] = array( 0 => $data['object_maps'] );
1640
			}
1641
		}
1642
1643
		$overwrite = isset( $_POST['overwrite'] ) ? esc_attr( $_POST['overwrite'] ) : '';
1644
		if ( true === filter_var( $overwrite, FILTER_VALIDATE_BOOLEAN ) ) {
1645
			if ( isset( $data['fieldmaps'] ) ) {
1646
				$fieldmaps = $this->mappings->get_fieldmaps();
1647
				foreach ( $fieldmaps as $fieldmap ) {
1648
					$id     = $fieldmap['id'];
1649
					$delete = $this->mappings->delete_fieldmap( $id );
1650
				}
1651
			}
1652
			if ( isset( $data['object_maps'] ) ) {
1653
				$object_maps = $this->mappings->get_object_maps();
1654
1655
				// if there is only one existing object map, fix the array
1656
				if ( count( $object_maps ) === count( $object_maps, COUNT_RECURSIVE ) ) {
1657
					$object_maps = array( 0 => $object_maps );
1658
				}
1659
1660
				foreach ( $object_maps as $object_map ) {
1661
					$id     = $object_map['id'];
1662
					$delete = $this->mappings->delete_object_map( $id );
1663
				}
1664
			}
1665
			if ( isset( $data['plugin_settings'] ) ) {
1666
				foreach ( $data['plugin_settings'] as $key => $value ) {
1667
					delete_option( $value['option_name'] );
1668
				}
1669
			}
1670
		}
1671
1672
		$success = true;
1673
1674
		if ( isset( $data['fieldmaps'] ) ) {
1675
			foreach ( $data['fieldmaps'] as $fieldmap ) {
1676
				unset( $fieldmap['id'] );
1677
				$create = $this->mappings->create_fieldmap( $fieldmap );
1678
				if ( false === $create ) {
1679
					$success = false;
1680
				}
1681
			}
1682
		}
1683
1684
		if ( isset( $data['object_maps'] ) ) {
1685
			foreach ( $data['object_maps'] as $object_map ) {
1686
				unset( $object_map['id'] );
1687
				$create = $this->mappings->create_object_map( $object_map );
1688
				if ( false === $create ) {
1689
					$success = false;
1690
				}
1691
			}
1692
		}
1693
1694
		if ( isset( $data['plugin_settings'] ) ) {
1695
			foreach ( $data['plugin_settings'] as $key => $value ) {
1696
				update_option( $value['option_name'], maybe_unserialize( $value['option_value'] ), $value['autoload'] );
1697
			}
1698
		}
1699
1700
		if ( true === $success ) {
1701
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=import-export&data_saved=true' ) );
1702
			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...
1703
		} else {
1704
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=import-export&data_saved=false' ) );
1705
			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...
1706
		}
1707
1708
	}
1709
1710
	/**
1711
	* Create a json file for exporting
1712
	*
1713
	*/
1714
	public function export_json_file() {
1715
1716
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_export'], 'object_sync_for_salesforce_nonce_export' ) ) {
1717
			return;
1718
		}
1719
		if ( ! current_user_can( 'manage_options' ) ) {
1720
			return;
1721
		}
1722
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1723
		$export    = array();
1724
		if ( in_array( 'fieldmaps', $post_data['export'] ) ) {
1725
			$export['fieldmaps'] = $this->mappings->get_fieldmaps();
1726
		}
1727
		if ( in_array( 'object_maps', $post_data['export'] ) ) {
1728
			$export['object_maps'] = $this->mappings->get_object_maps();
1729
		}
1730
		if ( in_array( 'plugin_settings', $post_data['export'] ) ) {
1731
			$export['plugin_settings'] = $this->wpdb->get_results( 'SELECT * FROM ' . $this->wpdb->prefix . 'options' . ' WHERE option_name like "' . $this->option_prefix . '%"', ARRAY_A );
1732
		}
1733
		nocache_headers();
1734
		header( 'Content-Type: application/json; charset=utf-8' );
1735
		header( 'Content-Disposition: attachment; filename=object-sync-for-salesforce-data-export-' . date( 'm-d-Y' ) . '.json' );
1736
		header( 'Expires: 0' );
1737
		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

1737
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
1738
		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...
1739
	}
1740
1741
	/**
1742
	* Default display for <input> fields
1743
	*
1744
	* @param array $args
1745
	*/
1746
	public function display_input_field( $args ) {
1747
		$type    = $args['type'];
1748
		$id      = $args['label_for'];
1749
		$name    = $args['name'];
1750
		$desc    = $args['desc'];
1751
		$checked = '';
1752
1753
		$class = 'regular-text';
1754
1755
		if ( 'checkbox' === $type ) {
1756
			$class = 'checkbox';
1757
		}
1758
1759
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
1760
			$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

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