Passed
Push — 336-remove-deprecated-methods ( 44ba17...55f5b2 )
by Jonathan
03:42
created

Object_Sync_Sf_Admin::display_link()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 18
nc 4
nop 1
dl 0
loc 23
rs 9.6666
c 0
b 0
f 0
1
<?php
2
/**
3
 * Create default WordPress admin functionality to configure the plugin.
4
 *
5
 * @class   Object_Sync_Sf_Admin
6
 * @package Object_Sync_Salesforce
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Object_Sync_Sf_Admin class.
13
 */
14
class Object_Sync_Sf_Admin {
15
16
	/**
17
	 * Current version of the plugin
18
	 *
19
	 * @var string
20
	 */
21
	public $version;
22
23
	/**
24
	 * The main plugin file
25
	 *
26
	 * @var string
27
	 */
28
	public $file;
29
30
	/**
31
	 * Global object of `$wpdb`, the WordPress database
32
	 *
33
	 * @var object
34
	 */
35
	public $wpdb;
36
37
	/**
38
	 * The plugin's slug so we can include it when necessary
39
	 *
40
	 * @var string
41
	 */
42
	public $slug;
43
44
	/**
45
	 * The plugin's prefix when saving options to the database
46
	 *
47
	 * @var string
48
	 */
49
	public $option_prefix;
50
51
	/**
52
	 * Suffix for group name in ActionScheduler
53
	 *
54
	 * @var string
55
	 */
56
	public $action_group_suffix;
57
58
	/**
59
	 * Login credentials for the Salesforce API; comes from wp-config or from the plugin settings
60
	 *
61
	 * @var array
62
	 */
63
	public $login_credentials;
64
65
	/**
66
	 * Array of what classes in the plugin can be scheduled to occur with `wp_cron` events
67
	 *
68
	 * @var array
69
	 */
70
	public $schedulable_classes;
71
72
	/**
73
	 * Object_Sync_Sf_Queue class
74
	 *
75
	 * @var object
76
	 */
77
	public $queue;
78
79
	/**
80
	 * Object_Sync_Sf_Logging class
81
	 *
82
	 * @var object
83
	 */
84
	public $logging;
85
86
	/**
87
	 * Object_Sync_Sf_Mapping class
88
	 *
89
	 * @var object
90
	 */
91
	public $mappings;
92
93
	/**
94
	 * Object_Sync_Sf_WordPress class
95
	 *
96
	 * @var object
97
	 */
98
	public $wordpress;
99
100
	/**
101
	 * Object_Sync_Sf_Salesforce class
102
	 * This contains Salesforce API methods
103
	 *
104
	 * @var array
105
	 */
106
	public $salesforce;
107
108
	/**
109
	 * Object_Sync_Sf_Salesforce_Push class
110
	 *
111
	 * @var object
112
	 */
113
	public $push;
114
115
	/**
116
	 * Object_Sync_Sf_Salesforce_Pull class
117
	 *
118
	 * @var object
119
	 */
120
	public $pull;
121
122
	/**
123
	 * Object_Sync_Sf_WordPress_Transient class
124
	 *
125
	 * @var object
126
	 */
127
	private $sfwp_transients;
128
129
	/**
130
	 * URL fragment for the plugin's settings page
131
	 *
132
	 * @var string
133
	 */
134
	private $admin_settings_url_param;
135
136
	/**
137
	 * Salesforce access token
138
	 *
139
	 * @var string
140
	 */
141
	private $access_token;
142
143
	/**
144
	 * Salesforce instance URL
145
	 *
146
	 * @var string
147
	 */
148
	private $instance_url;
149
150
	/**
151
	 * Salesforce refresh token
152
	 *
153
	 * @var string
154
	 */
155
	private $refresh_token;
156
157
	/**
158
	 * Default path for the Salesforce authorize URL
159
	 *
160
	 * @var string
161
	 */
162
	public $default_authorize_url_path;
163
164
	/**
165
	 * Default path for the Salesforce token URL
166
	 *
167
	 * @var string
168
	 */
169
	public $default_token_url_path;
170
171
	/**
172
	 * What version of the Salesforce API should be the default on the settings screen.
173
	 * Users can edit what version is used, but they won't see a correct list of all their available versions until WordPress has
174
	 * been authenticated with Salesforce.
175
	 *
176
	 * @var string
177
	 */
178
	public $default_api_version;
179
180
	/**
181
	 * Default max number of pull records. Users can edit this.
182
	 *
183
	 * @var int
184
	 */
185
	public $default_pull_limit;
186
187
	/**
188
	 * Default throttle for how often to pull from Salesforce. Users can edit this.
189
	 *
190
	 * @var int
191
	 */
192
	public $default_pull_throttle;
193
194
	/**
195
	 * Default for whether to limit to triggerable items. Users can edit this.
196
	 *
197
	 * @var bool
198
	 */
199
	public $default_triggerable;
200
201
	/**
202
	 * Default for whether to limit to items that can be updated. Users can edit this.
203
	 *
204
	 * @var bool
205
	 */
206
	public $default_updateable;
207
208
	/**
209
	 * Constructor for admin class
210
	 */
211
	public function __construct() {
212
		$this->version             = object_sync_for_salesforce()->version;
213
		$this->file                = object_sync_for_salesforce()->file;
214
		$this->wpdb                = object_sync_for_salesforce()->wpdb;
215
		$this->slug                = object_sync_for_salesforce()->slug;
216
		$this->option_prefix       = object_sync_for_salesforce()->option_prefix;
217
		$this->action_group_suffix = object_sync_for_salesforce()->action_group_suffix;
218
219
		$this->login_credentials   = object_sync_for_salesforce()->login_credentials;
220
		$this->wordpress           = object_sync_for_salesforce()->wordpress;
221
		$this->salesforce          = object_sync_for_salesforce()->salesforce;
222
		$this->mappings            = object_sync_for_salesforce()->mappings;
223
		$this->push                = object_sync_for_salesforce()->push;
224
		$this->pull                = object_sync_for_salesforce()->pull;
225
		$this->logging             = object_sync_for_salesforce()->logging;
226
		$this->schedulable_classes = object_sync_for_salesforce()->schedulable_classes;
227
		$this->queue               = object_sync_for_salesforce()->queue;
228
229
		$this->sfwp_transients          = object_sync_for_salesforce()->wordpress->sfwp_transients;
230
		$this->admin_settings_url_param = 'object-sync-salesforce-admin';
231
232
		// default authorize url path.
233
		$this->default_authorize_url_path = '/services/oauth2/authorize';
234
		// default token url path.
235
		$this->default_token_url_path = '/services/oauth2/token';
236
		// what Salesforce API version to start the settings with. This is only used in the settings form.
237
		$this->default_api_version = defined( 'OBJECT_SYNC_SF_DEFAULT_API_VERSION' ) ? OBJECT_SYNC_SF_DEFAULT_API_VERSION : '52.0';
238
		// default pull record limit.
239
		$this->default_pull_limit = 25;
240
		// default pull throttle for avoiding going over api limits.
241
		$this->default_pull_throttle = 5;
242
		// default setting for triggerable items.
243
		$this->default_triggerable = true;
244
		// default setting for updateable items.
245
		$this->default_updateable = true;
246
247
		$this->add_actions();
248
249
		$this->add_deprecated_actions();
250
	}
251
252
	/**
253
	 * Create the action hooks to create the admin pages.
254
	 */
255
	public function add_actions() {
256
257
		// settings link.
258
		add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 5 );
259
260
		// CSS and Javascript.
261
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_and_styles' ) );
262
263
		// Settings API forms and notices.
264
		add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
265
		add_action( 'admin_init', array( $this, 'salesforce_settings_forms' ) );
266
		add_action( 'admin_init', array( $this, 'notices' ) );
267
		add_action( 'admin_post_post_fieldmap', array( $this, 'prepare_fieldmap_data' ) );
268
		add_action( 'admin_post_delete_fieldmap', array( $this, 'delete_fieldmap' ) );
269
270
		// Ajax for fieldmap forms.
271
		add_action( 'wp_ajax_get_salesforce_object_description', array( $this, 'get_salesforce_object_description' ), 10, 1 );
272
		add_action( 'wp_ajax_get_salesforce_object_fields', array( $this, 'get_salesforce_object_fields' ), 10, 1 );
273
		add_action( 'wp_ajax_get_wordpress_object_fields', array( $this, 'get_wordpress_object_fields' ), 10, 1 );
274
275
		// Ajax events that can be manually called.
276
		add_action( 'wp_ajax_push_to_salesforce', array( $this, 'push_to_salesforce' ), 10, 3 );
277
		add_action( 'wp_ajax_pull_from_salesforce', array( $this, 'pull_from_salesforce' ), 10, 2 );
278
		add_action( 'wp_ajax_refresh_mapped_data', array( $this, 'refresh_mapped_data' ), 10, 1 );
279
		add_action( 'wp_ajax_clear_sfwp_cache', array( $this, 'clear_sfwp_cache' ) );
280
281
		// we add a Salesforce box on user profiles.
282
		add_action( 'edit_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
283
		add_action( 'show_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
284
285
		// and we can update Salesforce fields on the user profile box.
286
		add_action( 'personal_options_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
287
		add_action( 'edit_user_profile_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
288
289
		// when either field for schedule settings changes.
290
		foreach ( $this->schedulable_classes as $key => $value ) {
291
			// if the user doesn't have any action schedule tasks, let's not leave them empty.
292
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'initial_action_schedule' ), 10, 3 );
293
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'initial_action_schedule' ), 10, 3 );
294
295
			// this is if the user is changing their tasks.
296
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'change_action_schedule' ), 10, 3 );
297
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'change_action_schedule' ), 10, 3 );
298
		}
299
300
		// handle post requests for object maps.
301
		add_action( 'admin_post_delete_object_map', array( $this, 'delete_object_map' ) );
302
		add_action( 'admin_post_post_object_map', array( $this, 'prepare_object_map_data' ) );
303
304
		// import and export plugin data.
305
		add_action( 'admin_post_object_sync_for_salesforce_import', array( $this, 'import_json_file' ) );
306
		add_action( 'admin_post_object_sync_for_salesforce_export', array( $this, 'export_json_file' ) );
307
308
	}
309
310
	/**
311
	 * Deprecated action hooks for admin pages
312
	 */
313
	private function add_deprecated_actions() {
314
		/**
315
		 * Method: get_wordpress_object_description
316
		 *
317
		 * @deprecated since 1.9.0
318
		 */
319
		add_action( 'wp_ajax_get_wordpress_object_description', array( $this, 'get_wordpress_object_fields' ), 10, 1 );
320
		/**
321
		 * Method: get_wp_sf_object_fields
322
		 *
323
		 * @deprecated since 1.9.0
324
		 */
325
		add_action( 'wp_ajax_get_wp_sf_object_fields', array( $this, 'get_wp_sf_object_fields' ), 10, 2 );
326
	}
327
328
	/**
329
	 * Display a Settings link on the main Plugins page
330
	 *
331
	 * @param array  $links the array of links for the main plugins page.
332
	 * @param string $file the filename.
333
	 * @return array $links the array of links for the main plugins page
334
	 */
335
	public function plugin_action_links( $links, $file ) {
336
		if ( plugin_basename( $this->file ) === $file ) {
337
			$settings = '<a href="' . get_admin_url() . 'options-general.php?page=' . $this->admin_settings_url_param . '">' . __( 'Settings', 'object-sync-for-salesforce' ) . '</a>';
338
			array_unshift( $links, $settings );
339
		}
340
		return $links;
341
	}
342
343
	/**
344
	 * Admin styles. Load the CSS and JavaScript for the plugin's settings
345
	 */
346
	public function admin_scripts_and_styles() {
347
348
		// Developers might not want to bother with select2 or selectwoo, so we allow that to be changeable.
349
		$select_library = apply_filters( $this->option_prefix . 'select_library', 'selectwoo' );
350
351
		/*
352
		 * example to modify the select library
353
		 * add_filter( 'object_sync_for_salesforce_select_library', 'select_library', 10, 1 );
354
		 * function select_library( $select_library ) {
355
		 * 	$select_library = 'select2';
356
		 *  // this could also be empty; in that case we would just use default browser select
357
		 * 	return $select_library;
358
		 * }
359
		*/
360
361
		$javascript_dependencies = array( 'jquery' );
362
		$css_dependencies        = array();
363
		if ( '' !== $select_library ) {
364
			wp_enqueue_script( $select_library . 'js', plugins_url( 'assets/js/vendor/' . $select_library . '.min.js', $this->file ), array( 'jquery' ), filemtime( plugin_dir_path( $this->file ) . 'assets/js/vendor/' . $select_library . '.min.js' ), true );
365
			$javascript_dependencies[] = $select_library . 'js';
366
			wp_enqueue_style( $select_library . 'css', plugins_url( 'assets/css/vendor/' . $select_library . '.min.css', $this->file ), array(), filemtime( plugin_dir_path( $this->file ) . 'assets/css/vendor/' . $select_library . '.min.css' ), 'all' );
367
			$css_dependencies[] = $select_library . 'css';
368
		}
369
370
		wp_enqueue_script( $this->slug . '-admin', plugins_url( 'assets/js/object-sync-for-salesforce-admin.min.js', $this->file ), $javascript_dependencies, filemtime( plugin_dir_path( $this->file ) . 'assets/js/object-sync-for-salesforce-admin.min.js' ), true );
371
		wp_enqueue_style( $this->slug . '-admin', plugins_url( 'assets/css/object-sync-for-salesforce-admin.css', $this->file ), $css_dependencies, filemtime( plugin_dir_path( $this->file ) . 'assets/css/object-sync-for-salesforce-admin.css' ), 'all' );
372
	}
373
374
	/**
375
	 * Initial recurring tasks for ActionScheduler
376
	 *
377
	 * @param string $new_schedule the new, unserialized option value.
378
	 * @param string $old_schedule the old option value.
379
	 * @param string $option_name option name.
380
	 * @return string $new_schedule
381
	 */
382
	public function initial_action_schedule( $new_schedule, $old_schedule, $option_name ) {
383
384
		// get the current schedule name from the task, based on pattern in the foreach.
385
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
386
		$schedule_name     = $matches[1];
387
		$action_group_name = $schedule_name . $this->action_group_suffix;
388
389
		// make sure there are no tasks already.
390
		$current_tasks = as_get_scheduled_actions(
391
			array(
392
				'hook'  => $this->schedulable_classes[ $schedule_name ]['initializer'],
393
				'group' => $action_group_name,
394
			),
395
			ARRAY_A
396
		);
397
398
		// exit if there are already tasks; they'll be saved if the option data changed.
399
		if ( ! empty( $current_tasks ) ) {
400
			return $new_schedule;
401
		}
402
403
		$this->set_action_schedule( $schedule_name, $action_group_name );
404
405
		return $new_schedule;
406
407
	}
408
409
	/**
410
	 * Update recurring tasks for ActionScheduler if options change
411
	 *
412
	 * @param string $old_schedule the old option value.
413
	 * @param string $new_schedule the new, unserialized option value.
414
	 * @param string $option_name option name.
415
	 */
416
	public function change_action_schedule( $old_schedule, $new_schedule, $option_name ) {
417
418
		// this method does not run if the option's data is unchanged.
419
420
		// get the current schedule name from the task, based on pattern in the foreach.
421
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
422
		$schedule_name     = $matches[1];
423
		$action_group_name = $schedule_name . $this->action_group_suffix;
424
425
		$this->set_action_schedule( $schedule_name, $action_group_name );
426
427
	}
428
429
	/**
430
	 * Set up recurring tasks for ActionScheduler
431
	 *
432
	 * @param string $schedule_name the name of the schedule.
433
	 * @param string $action_group_name the group's name.
434
	 */
435
	private function set_action_schedule( $schedule_name, $action_group_name ) {
436
		// exit if there is no initializer property on this schedule.
437
		if ( ! isset( $this->schedulable_classes[ $schedule_name ]['initializer'] ) ) {
438
			return;
439
		}
440
441
		// cancel previous task.
442
		$this->queue->cancel(
443
			$this->schedulable_classes[ $schedule_name ]['initializer'],
444
			array(),
445
			$action_group_name
446
		);
447
448
		// create new recurring task for ActionScheduler to check for data to pull from Salesforce.
449
		$this->queue->schedule_recurring(
450
			time(), // plugin seems to expect UTC.
451
			$this->queue->get_frequency( $schedule_name, 'seconds' ),
452
			$this->schedulable_classes[ $schedule_name ]['initializer'],
453
			array(),
454
			$action_group_name
455
		);
456
	}
457
458
	/**
459
	 * Create the WordPress admin options page
460
	 */
461
	public function create_admin_menu() {
462
		$title = __( 'Salesforce', 'object-sync-for-salesforce' );
463
		add_options_page( $title, $title, 'configure_salesforce', $this->admin_settings_url_param, array( $this, 'show_admin_page' ) );
464
	}
465
466
	/**
467
	 * Render the admin pages in WordPress. This also allows other plugins to add tabs to this plugin's settings screen
468
	 */
469
	public function show_admin_page() {
470
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
471
		echo '<div class="wrap">';
472
		echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
473
		$allowed = $this->check_wordpress_admin_permissions();
474
		if ( false === $allowed ) {
475
			return;
476
		}
477
		$tabs = array(
478
			'settings'      => __( 'Settings', 'object-sync-for-salesforce' ),
479
			'authorize'     => __( 'Authorize', 'object-sync-for-salesforce' ),
480
			'fieldmaps'     => __( 'Fieldmaps', 'object-sync-for-salesforce' ),
481
			'schedule'      => __( 'Scheduling', 'object-sync-for-salesforce' ),
482
			'import-export' => __( 'Import &amp; Export', 'object-sync-for-salesforce' ),
483
		); // this creates the tabs for the admin.
484
485
		// optionally make tab(s) for logging and log settings.
486
		$logging_enabled      = get_option( $this->option_prefix . 'enable_logging', false );
487
		$tabs['log_settings'] = __( 'Log Settings', 'object-sync-for-salesforce' );
488
489
		$mapping_errors       = $this->mappings->get_failed_object_maps();
490
		$mapping_errors_total = isset( $mapping_errors['total'] ) ? $mapping_errors['total'] : 0;
491
		if ( 0 < $mapping_errors_total ) {
492
			$tabs['mapping_errors'] = __( 'Mapping Errors', 'object-sync-for-salesforce' );
493
		}
494
495
		// filter for extending the tabs available on the page
496
		// currently it will go into the default switch case for $tab.
497
		$tabs = apply_filters( $this->option_prefix . 'settings_tabs', $tabs );
498
499
		$tab = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
500
		$this->tabs( $tabs, $tab );
501
502
		$consumer_key    = $this->login_credentials['consumer_key'];
503
		$consumer_secret = $this->login_credentials['consumer_secret'];
504
		$callback_url    = $this->login_credentials['callback_url'];
505
506
		if ( true !== $this->salesforce['is_authorized'] ) {
507
			$url     = esc_url( $callback_url );
508
			$anchor  = esc_html__( 'Authorize tab', 'object-sync-for-salesforce' );
509
			$message = sprintf( 'Salesforce needs to be authorized to connect to this website. Use the <a href="%s">%s</a> to connect.', $url, $anchor );
510
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
511
		}
512
513
		if ( 0 === count( $this->mappings->get_fieldmaps() ) ) {
514
			$url     = esc_url( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=fieldmaps' ) );
515
			$anchor  = esc_html__( 'Fieldmaps tab', 'object-sync-for-salesforce' );
516
			$message = sprintf( 'No fieldmaps exist yet. Use the <a href="%s">%s</a> to map WordPress and Salesforce objects to each other.', $url, $anchor );
517
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
518
		}
519
520
		try {
521
			switch ( $tab ) {
522
				case 'authorize':
523
					if ( isset( $get_data['code'] ) ) {
524
						// this string is an oauth token.
525
						$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

525
						$data          = esc_html( /** @scrutinizer ignore-type */ wp_unslash( $get_data['code'] ) );
Loading history...
526
						$is_authorized = $this->salesforce['sfapi']->request_token( $data );
527
						?>
528
						<script>window.location = '<?php echo esc_url_raw( $callback_url ); ?>'</script>
529
						<?php
530
					} elseif ( true === $this->salesforce['is_authorized'] ) {
531
							require_once plugin_dir_path( $this->file ) . '/templates/admin/authorized.php';
532
							$this->status( $this->salesforce['sfapi'] );
533
					} elseif ( true === is_object( $this->salesforce['sfapi'] ) && isset( $consumer_key ) && isset( $consumer_secret ) ) {
534
						?>
535
						<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>
536
						<?php
537
					} else {
538
						$url    = esc_url( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=settings' ) );
539
						$anchor = esc_html__( 'Settings', 'object-sync-for-salesforce' );
540
						// translators: placeholders are for the settings tab link: 1) the url, and 2) the anchor text.
541
						$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-for-salesforce' ), $url, $anchor );
542
						require_once plugin_dir_path( $this->file ) . '/templates/admin/error.php';
543
					}
544
					break;
545
				case 'fieldmaps':
546
					if ( isset( $get_data['method'] ) ) {
547
548
						$method      = sanitize_key( $get_data['method'] );
549
						$error_url   = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=fieldmaps&method=' . $method );
550
						$success_url = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=fieldmaps' );
551
552
						if ( isset( $get_data['transient'] ) ) {
553
							$transient = sanitize_key( $get_data['transient'] );
554
							$posted    = $this->sfwp_transients->get( $transient );
555
						}
556
557
						if ( isset( $posted ) && is_array( $posted ) ) {
558
							$map = $posted;
559
						} elseif ( 'edit' === $method || 'clone' === $method || 'delete' === $method ) {
560
							$map = $this->mappings->get_fieldmaps( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
561
						}
562
563
						if ( isset( $map ) && is_array( $map ) ) {
564
							$label                           = $map['label'];
565
							$salesforce_object               = $map['salesforce_object'];
566
							$salesforce_record_types_allowed = maybe_unserialize( $map['salesforce_record_types_allowed'] );
567
							$salesforce_record_type_default  = $map['salesforce_record_type_default'];
568
							$wordpress_object                = $map['wordpress_object'];
569
							$pull_trigger_field              = $map['pull_trigger_field'];
570
							$fieldmap_fields                 = $map['fields'];
571
							$sync_triggers                   = $map['sync_triggers'];
572
							$push_async                      = $map['push_async'];
573
							$push_drafts                     = $map['push_drafts'];
574
							$pull_to_drafts                  = $map['pull_to_drafts'];
575
							$weight                          = $map['weight'];
576
						}
577
578
						if ( 'add' === $method || 'edit' === $method || 'clone' === $method ) {
579
							require_once plugin_dir_path( $this->file ) . '/templates/admin/fieldmaps-add-edit-clone.php';
580
						} elseif ( 'delete' === $method ) {
581
							require_once plugin_dir_path( $this->file ) . '/templates/admin/fieldmaps-delete.php';
582
						}
583
					} else {
584
						$fieldmaps = $this->mappings->get_fieldmaps();
585
						require_once plugin_dir_path( $this->file ) . '/templates/admin/fieldmaps-list.php';
586
					} // End if statement.
587
					break;
588
				case 'logout':
589
					$this->logout();
590
					break;
591
				case 'clear_cache':
592
					$this->clear_cache();
593
					break;
594
				case 'clear_schedule':
595
					if ( isset( $get_data['schedule_name'] ) ) {
596
						$schedule_name = sanitize_key( $get_data['schedule_name'] );
597
					}
598
					$this->clear_schedule( $schedule_name );
599
					break;
600
				case 'settings':
601
					require_once plugin_dir_path( $this->file ) . '/templates/admin/settings.php';
602
					break;
603
				case 'mapping_errors':
604
					if ( isset( $get_data['method'] ) ) {
605
606
						$method      = sanitize_key( $get_data['method'] );
607
						$error_url   = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=mapping_errors&method=' . $method );
608
						$success_url = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=mapping_errors' );
609
610
						if ( isset( $get_data['map_transient'] ) ) {
611
							$transient = sanitize_key( $get_data['map_transient'] );
612
							$posted    = $this->sfwp_transients->get( $transient );
613
						}
614
615
						if ( isset( $posted ) && is_array( $posted ) ) {
616
							$map_row = $posted;
617
						} elseif ( 'edit' === $method || 'delete' === $method ) {
618
							$map_row = $this->mappings->get_failed_object_map( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
619
						}
620
621
						if ( isset( $map_row ) && is_array( $map_row ) ) {
622
							$salesforce_id = $map_row['salesforce_id'];
623
							$wordpress_id  = $map_row['wordpress_id'];
624
						}
625
626
						if ( 'edit' === $method ) {
627
							require_once plugin_dir_path( $this->file ) . '/templates/admin/mapping-errors-edit.php';
628
						} elseif ( 'delete' === $method ) {
629
							require_once plugin_dir_path( $this->file ) . '/templates/admin/mapping-errors-delete.php';
630
						}
631
					} else {
632
633
						if ( isset( $get_data['mapping_error_transient'] ) ) {
634
							$transient = sanitize_key( $get_data['mapping_error_transient'] );
635
							$posted    = $this->sfwp_transients->get( $transient );
636
						}
637
638
						$ids_string = '';
639
						$ids        = array();
640
						if ( isset( $posted['delete'] ) ) {
641
							$ids_string = maybe_serialize( $posted['delete'] );
642
							$ids        = $posted['delete'];
643
						}
644
645
						$error_url   = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=mapping_errors&ids=' . $ids_string );
646
						$success_url = get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=mapping_errors' );
647
						require_once plugin_dir_path( $this->file ) . '/templates/admin/mapping-errors.php';
648
					}
649
					break;
650
				case 'import-export':
651
					require_once plugin_dir_path( $this->file ) . '/templates/admin/import-export.php';
652
					break;
653
				default:
654
					$include_settings = apply_filters( $this->option_prefix . 'settings_tab_include_settings', true, $tab );
655
					$content_before   = apply_filters( $this->option_prefix . 'settings_tab_content_before', null, $tab );
656
					$content_after    = apply_filters( $this->option_prefix . 'settings_tab_content_after', null, $tab );
657
					if ( null !== $content_before ) {
658
						echo esc_html( $content_before );
659
					}
660
					if ( true === $include_settings ) {
661
						require_once plugin_dir_path( $this->file ) . '/templates/admin/settings.php';
662
					}
663
					if ( null !== $content_after ) {
664
						echo esc_html( $content_after );
665
					}
666
					break;
667
			} // End switch statement.
668
		} 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...
669
			echo sprintf(
670
				'<p>Error <strong>%1$s</strong>: %2$s</p>',
671
				absint( $ex->getCode() ),
672
				esc_html( $ex->getMessage() )
673
			);
674
		} catch ( Exception $ex ) {
675
			echo sprintf(
676
				'<p>Error <strong>%1$s</strong>: %2$s</p>',
677
				absint( $ex->getCode() ),
678
				esc_html( $ex->getMessage() )
679
			);
680
		} // End try for menu/page setup.
681
		echo '</div>';
682
	}
683
684
	/**
685
	 * Create default WordPress admin settings form. This runs the Settings page.
686
	 */
687
	public function salesforce_settings_forms() {
688
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
689
		$page     = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
690
		$section  = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
691
692
		$input_callback_default   = array( $this, 'display_input_field' );
693
		$input_checkboxes_default = array( $this, 'display_checkboxes' );
694
		$input_select_default     = array( $this, 'display_select' );
695
		$link_default             = array( $this, 'display_link' );
696
697
		$all_field_callbacks = array(
698
			'text'       => $input_callback_default,
699
			'checkboxes' => $input_checkboxes_default,
700
			'select'     => $input_select_default,
701
			'link'       => $link_default,
702
		);
703
704
		$this->fields_settings( 'settings', 'settings', $all_field_callbacks );
705
		$this->fields_fieldmaps( 'fieldmaps', 'objects' );
706
		$this->fields_scheduling( 'schedule', 'schedule', $all_field_callbacks );
707
		$this->fields_log_settings( 'log_settings', 'log_settings', $all_field_callbacks );
708
		$this->fields_errors( 'mapping_errors', 'mapping_errors', $all_field_callbacks );
709
	}
710
711
	/**
712
	 * Fields for the Settings tab
713
	 * This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
714
	 *
715
	 * @param string $page what page we're on.
716
	 * @param string $section what section of the page.
717
	 * @param array  $callbacks method to call.
718
	 */
719
	private function fields_settings( $page, $section, $callbacks ) {
720
		add_settings_section( $page, ucwords( $page ), null, $page );
721
		$salesforce_settings = array(
722
			'consumer_key'                   => array(
723
				'title'    => __( 'Consumer Key', 'object-sync-for-salesforce' ),
724
				'callback' => $callbacks['text'],
725
				'page'     => $page,
726
				'section'  => $section,
727
				'args'     => array(
728
					'type'     => 'text',
729
					'validate' => 'sanitize_validate_text',
730
					'desc'     => '',
731
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_KEY',
732
				),
733
734
			),
735
			'consumer_secret'                => array(
736
				'title'    => __( 'Consumer Secret', 'object-sync-for-salesforce' ),
737
				'callback' => $callbacks['text'],
738
				'page'     => $page,
739
				'section'  => $section,
740
				'args'     => array(
741
					'type'     => 'text',
742
					'validate' => 'sanitize_validate_text',
743
					'desc'     => '',
744
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_SECRET',
745
				),
746
			),
747
			'callback_url'                   => array(
748
				'title'    => __( 'Callback URL', 'object-sync-for-salesforce' ),
749
				'callback' => $callbacks['text'],
750
				'page'     => $page,
751
				'section'  => $section,
752
				'args'     => array(
753
					'type'     => 'url',
754
					'validate' => 'sanitize_validate_text',
755
					'desc'     => sprintf(
756
						// translators: %1$s is the admin URL for the Authorize tab.
757
						__( 'In most cases, you will want to use %1$s for this value.', 'object-sync-for-salesforce' ),
758
						get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=authorize' )
759
					),
760
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CALLBACK_URL',
761
				),
762
			),
763
			'login_base_url'                 => array(
764
				'title'    => __( 'Login Base URL', 'object-sync-for-salesforce' ),
765
				'callback' => $callbacks['text'],
766
				'page'     => $page,
767
				'section'  => $section,
768
				'args'     => array(
769
					'type'     => 'url',
770
					'validate' => 'sanitize_validate_text',
771
					'desc'     => sprintf(
772
						// translators: 1) production salesforce login, 2) sandbox salesforce login.
773
						__( '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' ),
774
						esc_url( 'https://login.salesforce.com' ),
775
						esc_url( 'https://test.salesforce.com' )
776
					),
777
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_LOGIN_BASE_URL',
778
				),
779
			),
780
			'authorize_url_path'             => array(
781
				'title'    => __( 'Authorize URL Path', 'object-sync-for-salesforce' ),
782
				'callback' => $callbacks['text'],
783
				'page'     => $page,
784
				'section'  => $section,
785
				'args'     => array(
786
					'type'     => 'text',
787
					'validate' => 'sanitize_validate_text',
788
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
789
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_AUTHORIZE_URL_PATH',
790
					'default'  => $this->default_authorize_url_path,
791
				),
792
			),
793
			'token_url_path'                 => array(
794
				'title'    => __( 'Token URL Path', 'object-sync-for-salesforce' ),
795
				'callback' => $callbacks['text'],
796
				'page'     => $page,
797
				'section'  => $section,
798
				'args'     => array(
799
					'type'     => 'text',
800
					'validate' => 'sanitize_validate_text',
801
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
802
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_TOKEN_URL_PATH',
803
					'default'  => $this->default_token_url_path,
804
				),
805
			),
806
			'api_version'                    => array(
807
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
808
				'callback' => $callbacks['text'],
809
				'page'     => $page,
810
				'section'  => $section,
811
				'args'     => array(
812
					'type'     => 'text',
813
					'validate' => 'sanitize_validate_text',
814
					'desc'     => '',
815
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
816
					'default'  => $this->default_api_version,
817
				),
818
			),
819
			'object_filters'                 => array(
820
				'title'    => __( 'Limit Salesforce Objects', 'object-sync-for-salesforce' ),
821
				'callback' => $callbacks['checkboxes'],
822
				'page'     => $page,
823
				'section'  => $section,
824
				'args'     => array(
825
					'type'     => 'checkboxes',
826
					'validate' => 'sanitize_validate_text',
827
					'desc'     => __( 'Allows you to limit which Salesforce objects can be mapped', 'object-sync-for-salesforce' ),
828
					'items'    => array(
829
						'triggerable' => array(
830
							'text'    => __( 'Only Triggerable objects', 'object-sync-for-salesforce' ),
831
							'id'      => 'triggerable',
832
							'desc'    => '',
833
							'default' => $this->default_triggerable,
834
						),
835
						'updateable'  => array(
836
							'text'    => __( 'Only Updateable objects', 'object-sync-for-salesforce' ),
837
							'id'      => 'updateable',
838
							'desc'    => '',
839
							'default' => $this->default_updateable,
840
						),
841
					),
842
				),
843
			),
844
			'salesforce_field_display_value' => array(
845
				'title'    => __( 'Salesforce Field Display Value', 'object-sync-for-salesforce' ),
846
				'callback' => $callbacks['select'],
847
				'page'     => $page,
848
				'section'  => $section,
849
				'args'     => array(
850
					'type'     => 'select',
851
					'validate' => 'sanitize_validate_text',
852
					'desc'     => __( 'When choosing Salesforce fields to map, this value determines how the dropdown will identify Salesforce fields.', 'object-sync-for-salesforce' ),
853
					'constant' => '',
854
					'items'    => array(
855
						'field_label' => array(
856
							'text'  => __( 'Field Label', 'object-sync-for-salesforce' ),
857
							'value' => 'field_label',
858
						),
859
						'api_name'    => array(
860
							'text'  => __( 'API Name', 'object-sync-for-salesforce' ),
861
							'value' => 'api_name',
862
						),
863
					),
864
				),
865
			),
866
			'pull_query_limit'               => array(
867
				'title'    => __( 'Pull query record limit', 'object-sync-for-salesforce' ),
868
				'callback' => $callbacks['text'],
869
				'page'     => $page,
870
				'section'  => $section,
871
				'args'     => array(
872
					'type'     => 'number',
873
					'validate' => 'absint',
874
					'desc'     => __( 'Limit the number of records that can be pulled from Salesforce in a single query.', 'object-sync-for-salesforce' ),
875
					'constant' => '',
876
					'default'  => $this->default_pull_limit,
877
				),
878
			),
879
			'pull_throttle'                  => array(
880
				'title'    => __( 'Pull throttle (seconds)', 'object-sync-for-salesforce' ),
881
				'callback' => $callbacks['text'],
882
				'page'     => $page,
883
				'section'  => $section,
884
				'args'     => array(
885
					'type'     => 'number',
886
					'validate' => 'sanitize_validate_text',
887
					'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' ),
888
					'constant' => '',
889
					'default'  => $this->default_pull_throttle,
890
				),
891
			),
892
		);
893
894
		// only show soap settings if the soap extension is enabled on the server.
895
		if ( true === $this->salesforce['soap_available'] ) {
896
			$salesforce_settings['use_soap']       = array(
897
				'title'    => __( 'Enable the Salesforce SOAP API?', 'object-sync-for-salesforce' ),
898
				'callback' => $callbacks['text'],
899
				'page'     => $page,
900
				'section'  => $section,
901
				'class'    => 'object-sync-for-salesforce-enable-soap',
902
				'args'     => array(
903
					'type'     => 'checkbox',
904
					'validate' => 'sanitize_text_field',
905
					'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' ),
906
					'constant' => '',
907
				),
908
			);
909
			$salesforce_settings['soap_wsdl_path'] = array(
910
				'title'    => __( 'Path to SOAP WSDL file', 'object-sync-for-salesforce' ),
911
				'callback' => $callbacks['text'],
912
				'page'     => $page,
913
				'section'  => $section,
914
				'class'    => 'object-sync-for-salesforce-soap-wsdl-path',
915
				'args'     => array(
916
					'type'     => 'text',
917
					'validate' => 'sanitize_text_field',
918
					'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' ),
919
					'constant' => '',
920
				),
921
			);
922
		}
923
924
		$salesforce_settings['debug_mode']               = array(
925
			'title'    => __( 'Debug mode?', 'object-sync-for-salesforce' ),
926
			'callback' => $callbacks['text'],
927
			'page'     => $page,
928
			'section'  => $section,
929
			'args'     => array(
930
				'type'     => 'checkbox',
931
				'validate' => 'sanitize_text_field',
932
				'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' ),
933
				'constant' => '',
934
			),
935
		);
936
		$salesforce_settings['delete_data_on_uninstall'] = array(
937
			'title'    => __( 'Delete plugin data on uninstall?', 'object-sync-for-salesforce' ),
938
			'callback' => $callbacks['text'],
939
			'page'     => $page,
940
			'section'  => $section,
941
			'args'     => array(
942
				'type'     => 'checkbox',
943
				'validate' => 'sanitize_text_field',
944
				'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' ),
945
				'constant' => '',
946
			),
947
		);
948
949
		if ( true === is_object( $this->salesforce['sfapi'] ) && true === $this->salesforce['sfapi']->is_authorized() ) {
950
			$salesforce_settings['api_version'] = array(
951
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
952
				'callback' => $callbacks['select'],
953
				'page'     => $page,
954
				'section'  => $section,
955
				'args'     => array(
956
					'type'     => 'select',
957
					'validate' => 'sanitize_text_field',
958
					'desc'     => '',
959
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
960
					'items'    => $this->version_options(),
961
				),
962
			);
963
		}
964
965
		foreach ( $salesforce_settings as $key => $attributes ) {
966
			$id       = $this->option_prefix . $key;
967
			$name     = $this->option_prefix . $key;
968
			$title    = $attributes['title'];
969
			$callback = $attributes['callback'];
970
			$validate = $attributes['args']['validate'];
971
			$page     = $attributes['page'];
972
			$section  = $attributes['section'];
973
			$class    = isset( $attributes['class'] ) ? $attributes['class'] : '';
974
			$args     = array_merge(
975
				$attributes['args'],
976
				array(
977
					'title'     => $title,
978
					'id'        => $id,
979
					'label_for' => $id,
980
					'name'      => $name,
981
					'class'     => $class,
982
				)
983
			);
984
985
			// if there is a constant and it is defined, don't run a validate function.
986
			if ( isset( $attributes['args']['constant'] ) && defined( $attributes['args']['constant'] ) ) {
987
				$validate = '';
988
			}
989
990
			add_settings_field( $id, $title, $callback, $page, $section, $args );
991
			register_setting( $page, $id, array( $this, $validate ) );
992
		}
993
	}
994
995
	/**
996
	 * Fields for the Fieldmaps tab
997
	 * This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
998
	 *
999
	 * @param string $page what page we're on.
1000
	 * @param string $section what section of the page.
1001
	 * @param string $input_callback method to call.
1002
	 */
1003
	private function fields_fieldmaps( $page, $section, $input_callback = '' ) {
1004
		add_settings_section( $page, ucwords( $page ), null, $page );
1005
	}
1006
1007
	/**
1008
	 * Fields for the Scheduling tab
1009
	 * This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
1010
	 *
1011
	 * @param string $page what page we're on.
1012
	 * @param string $section what section of the page.
1013
	 * @param array  $callbacks method to call.
1014
	 */
1015
	private function fields_scheduling( $page, $section, $callbacks ) {
1016
1017
		add_settings_section( 'batch', __( 'Batch Settings', 'object-sync-for-salesforce' ), null, $page );
1018
		$section           = 'batch';
1019
		$schedule_settings = array(
1020
			'action_scheduler_batch_size'         => array(
1021
				'title'    => __( 'Batch size', 'object-sync-for-salesforce' ),
1022
				'callback' => $callbacks['text'],
1023
				'page'     => $page,
1024
				'section'  => $section,
1025
				'args'     => array(
1026
					'type'     => 'number',
1027
					'validate' => 'absint',
1028
					'default'  => 5,
1029
					'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' ),
1030
					'constant' => '',
1031
				),
1032
1033
			),
1034
			'action_scheduler_concurrent_batches' => array(
1035
				'title'    => __( 'Concurrent batches', 'object-sync-for-salesforce' ),
1036
				'callback' => $callbacks['text'],
1037
				'page'     => $page,
1038
				'section'  => $section,
1039
				'args'     => array(
1040
					'type'     => 'number',
1041
					'validate' => 'absint',
1042
					'default'  => 3,
1043
					'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' ),
1044
					'constant' => '',
1045
				),
1046
			),
1047
		);
1048
1049
		foreach ( $this->schedulable_classes as $key => $value ) {
1050
			add_settings_section( $key, $value['label'], null, $page );
1051
			if ( isset( $value['initializer'] ) ) {
1052
				$schedule_settings[ $key . '_schedule_number' ] = array(
1053
					'title'    => __( 'Run schedule every', 'object-sync-for-salesforce' ),
1054
					'callback' => $callbacks['text'],
1055
					'page'     => $page,
1056
					'section'  => $key,
1057
					'args'     => array(
1058
						'type'     => 'number',
1059
						'validate' => 'absint',
1060
						'desc'     => '',
1061
						'constant' => '',
1062
					),
1063
				);
1064
				$schedule_settings[ $key . '_schedule_unit' ]   = array(
1065
					'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
1066
					'callback' => $callbacks['select'],
1067
					'page'     => $page,
1068
					'section'  => $key,
1069
					'args'     => array(
1070
						'type'     => 'select',
1071
						'validate' => 'sanitize_validate_text',
1072
						'desc'     => '',
1073
						'items'    => array(
1074
							'minutes' => array(
1075
								'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
1076
								'value' => 'minutes',
1077
							),
1078
							'hours'   => array(
1079
								'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
1080
								'value' => 'hours',
1081
							),
1082
							'days'    => array(
1083
								'text'  => __( 'Days', 'object-sync-for-salesforce' ),
1084
								'value' => 'days',
1085
							),
1086
						),
1087
					),
1088
				);
1089
			}
1090
			$schedule_settings[ $key . '_clear_button' ] = array(
1091
				// translators: $this->get_schedule_count is an integer showing how many items are in the current queue.
1092
				'title'    => sprintf( 'This queue has ' . _n( '%s item', '%s items', $this->get_schedule_count( $key ), 'object-sync-for-salesforce' ), $this->get_schedule_count( $key ) ),
1093
				'callback' => $callbacks['link'],
1094
				'page'     => $page,
1095
				'section'  => $key,
1096
				'args'     => array(
1097
					'label'      => __( 'Clear this queue', 'object-sync-for-salesforce' ),
1098
					'desc'       => '',
1099
					'url'        => esc_url( '?page=' . $this->admin_settings_url_param . '&amp;tab=clear_schedule&amp;schedule_name=' . $key ),
1100
					'link_class' => 'button button-secondary',
1101
				),
1102
			);
1103
			foreach ( $schedule_settings as $key => $attributes ) {
1104
				$id       = $this->option_prefix . $key;
1105
				$name     = $this->option_prefix . $key;
1106
				$title    = $attributes['title'];
1107
				$callback = $attributes['callback'];
1108
				$page     = $attributes['page'];
1109
				$section  = $attributes['section'];
1110
				$args     = array_merge(
1111
					$attributes['args'],
1112
					array(
1113
						'title'     => $title,
1114
						'id'        => $id,
1115
						'label_for' => $id,
1116
						'name'      => $name,
1117
					)
1118
				);
1119
				add_settings_field( $id, $title, $callback, $page, $section, $args );
1120
				register_setting( $page, $id );
1121
			}
1122
		} // End foreach statement.
1123
	}
1124
1125
	/**
1126
	 * Fields for the Log Settings tab
1127
	 * This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
1128
	 *
1129
	 * @param string $page what page we're on.
1130
	 * @param string $section what section of the page.
1131
	 * @param array  $callbacks method to call.
1132
	 */
1133
	private function fields_log_settings( $page, $section, $callbacks ) {
1134
		add_settings_section( $page, ucwords( str_replace( '_', ' ', $page ) ), null, $page );
1135
		$log_settings = array(
1136
			'enable_logging'        => array(
1137
				'title'    => __( 'Enable Logging?', 'object-sync-for-salesforce' ),
1138
				'callback' => $callbacks['text'],
1139
				'page'     => $page,
1140
				'section'  => $section,
1141
				'args'     => array(
1142
					'type'     => 'checkbox',
1143
					'validate' => 'absint',
1144
					'desc'     => '',
1145
					'constant' => '',
1146
				),
1147
			),
1148
			'statuses_to_log'       => array(
1149
				'title'    => __( 'Statuses to log', 'object-sync-for-salesforce' ),
1150
				'callback' => $callbacks['checkboxes'],
1151
				'page'     => $page,
1152
				'section'  => $section,
1153
				'args'     => array(
1154
					'type'     => 'checkboxes',
1155
					'validate' => 'sanitize_validate_text',
1156
					'desc'     => __( 'these are the statuses to log', 'object-sync-for-salesforce' ),
1157
					'items'    => array(
1158
						'error'   => array(
1159
							'text' => __( 'Error', 'object-sync-for-salesforce' ),
1160
							'id'   => 'error',
1161
							'desc' => '',
1162
						),
1163
						'success' => array(
1164
							'text' => __( 'Success', 'object-sync-for-salesforce' ),
1165
							'id'   => 'success',
1166
							'desc' => '',
1167
						),
1168
						'notice'  => array(
1169
							'text' => __( 'Notice', 'object-sync-for-salesforce' ),
1170
							'id'   => 'notice',
1171
							'desc' => '',
1172
						),
1173
						'debug'   => array(
1174
							'text' => __( 'Debug', 'object-sync-for-salesforce' ),
1175
							'id'   => 'debug',
1176
							'desc' => '',
1177
						),
1178
					),
1179
				),
1180
			),
1181
			'prune_logs'            => array(
1182
				'title'    => __( 'Automatically delete old log entries?', 'object-sync-for-salesforce' ),
1183
				'callback' => $callbacks['text'],
1184
				'page'     => $page,
1185
				'section'  => $section,
1186
				'args'     => array(
1187
					'type'     => 'checkbox',
1188
					'validate' => 'absint',
1189
					'desc'     => '',
1190
					'constant' => '',
1191
				),
1192
			),
1193
			'logs_how_old'          => array(
1194
				'title'    => __( 'Age to delete log entries', 'object-sync-for-salesforce' ),
1195
				'callback' => $callbacks['text'],
1196
				'page'     => $page,
1197
				'section'  => $section,
1198
				'args'     => array(
1199
					'type'     => 'text',
1200
					'validate' => 'sanitize_validate_text',
1201
					'desc'     => __( 'If automatic deleting is enabled, it will affect logs this old.', 'object-sync-for-salesforce' ),
1202
					'default'  => '2 weeks',
1203
					'constant' => '',
1204
				),
1205
			),
1206
			'logs_how_often_number' => array(
1207
				'title'    => __( 'Check for old logs every', 'object-sync-for-salesforce' ),
1208
				'callback' => $callbacks['text'],
1209
				'page'     => $page,
1210
				'section'  => $section,
1211
				'args'     => array(
1212
					'type'     => 'number',
1213
					'validate' => 'absint',
1214
					'desc'     => '',
1215
					'default'  => '1',
1216
					'constant' => '',
1217
				),
1218
			),
1219
			'logs_how_often_unit'   => array(
1220
				'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
1221
				'callback' => $callbacks['select'],
1222
				'page'     => $page,
1223
				'section'  => $section,
1224
				'args'     => array(
1225
					'type'     => 'select',
1226
					'validate' => 'sanitize_validate_text',
1227
					'desc'     => __( 'These two fields are how often the site will check for logs to delete.', 'object-sync-for-salesforce' ),
1228
					'items'    => array(
1229
						'minutes' => array(
1230
							'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
1231
							'value' => 'minutes',
1232
						),
1233
						'hours'   => array(
1234
							'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
1235
							'value' => 'hours',
1236
						),
1237
						'days'    => array(
1238
							'text'  => __( 'Days', 'object-sync-for-salesforce' ),
1239
							'value' => 'days',
1240
						),
1241
					),
1242
				),
1243
			),
1244
			'logs_how_many_number'  => array(
1245
				'title'    => __( 'Clear this many log entries', 'object-sync-for-salesforce' ),
1246
				'callback' => $callbacks['text'],
1247
				'page'     => $page,
1248
				'section'  => $section,
1249
				'args'     => array(
1250
					'type'     => 'number',
1251
					'validate' => 'absint',
1252
					'desc'     => __( 'This number is how many log entries the plugin will try to clear at a time. If you do not enter a number, the default is 100.', 'object-sync-for-salesforce' ),
1253
					'default'  => '100',
1254
					'constant' => '',
1255
				),
1256
			),
1257
			'triggers_to_log'       => array(
1258
				'title'    => __( 'Triggers to log', 'object-sync-for-salesforce' ),
1259
				'callback' => $callbacks['checkboxes'],
1260
				'page'     => $page,
1261
				'section'  => $section,
1262
				'args'     => array(
1263
					'type'     => 'checkboxes',
1264
					'validate' => 'sanitize_validate_text',
1265
					'desc'     => __( 'these are the triggers to log', 'object-sync-for-salesforce' ),
1266
					'items'    => array(
1267
						$this->mappings->sync_wordpress_create => array(
1268
							'text' => __( 'WordPress create', 'object-sync-for-salesforce' ),
1269
							'id'   => 'wordpress_create',
1270
							'desc' => '',
1271
						),
1272
						$this->mappings->sync_wordpress_update => array(
1273
							'text' => __( 'WordPress update', 'object-sync-for-salesforce' ),
1274
							'id'   => 'wordpress_update',
1275
							'desc' => '',
1276
						),
1277
						$this->mappings->sync_wordpress_delete => array(
1278
							'text' => __( 'WordPress delete', 'object-sync-for-salesforce' ),
1279
							'id'   => 'wordpress_delete',
1280
							'desc' => '',
1281
						),
1282
						$this->mappings->sync_sf_create => array(
1283
							'text' => __( 'Salesforce create', 'object-sync-for-salesforce' ),
1284
							'id'   => 'sf_create',
1285
							'desc' => '',
1286
						),
1287
						$this->mappings->sync_sf_update => array(
1288
							'text' => __( 'Salesforce update', 'object-sync-for-salesforce' ),
1289
							'id'   => 'sf_update',
1290
							'desc' => '',
1291
						),
1292
						$this->mappings->sync_sf_delete => array(
1293
							'text' => __( 'Salesforce delete', 'object-sync-for-salesforce' ),
1294
							'id'   => 'sf_delete',
1295
							'desc' => '',
1296
						),
1297
					),
1298
				),
1299
			),
1300
		);
1301
		foreach ( $log_settings as $key => $attributes ) {
1302
			$id       = $this->option_prefix . $key;
1303
			$name     = $this->option_prefix . $key;
1304
			$title    = $attributes['title'];
1305
			$callback = $attributes['callback'];
1306
			$page     = $attributes['page'];
1307
			$section  = $attributes['section'];
1308
			$args     = array_merge(
1309
				$attributes['args'],
1310
				array(
1311
					'title'     => $title,
1312
					'id'        => $id,
1313
					'label_for' => $id,
1314
					'name'      => $name,
1315
				)
1316
			);
1317
			add_settings_field( $id, $title, $callback, $page, $section, $args );
1318
			register_setting( $page, $id );
1319
		}
1320
	}
1321
1322
	/**
1323
	 * Fields for the Mapping Errors tab
1324
	 * This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
1325
	 *
1326
	 * @param string $page what page we're on.
1327
	 * @param string $section what section of the page.
1328
	 * @param array  $callbacks method to call.
1329
	 */
1330
	private function fields_errors( $page, $section, $callbacks ) {
1331
1332
		add_settings_section( $section, __( 'Mapping Error Settings', 'object-sync-for-salesforce' ), null, $page );
1333
		$error_settings = array(
1334
			'errors_per_page' => array(
1335
				'title'    => __( 'Errors per page', 'object-sync-for-salesforce' ),
1336
				'callback' => $callbacks['text'],
1337
				'page'     => $page,
1338
				'section'  => $section,
1339
				'args'     => array(
1340
					'type'     => 'number',
1341
					'validate' => 'absint',
1342
					'default'  => 50,
1343
					'desc'     => __( 'Set how many mapping errors to show on a single page.', 'object-sync-for-salesforce' ),
1344
					'constant' => '',
1345
				),
1346
			),
1347
		);
1348
1349
		foreach ( $error_settings as $key => $attributes ) {
1350
			$id       = $this->option_prefix . $key;
1351
			$name     = $this->option_prefix . $key;
1352
			$title    = $attributes['title'];
1353
			$callback = $attributes['callback'];
1354
			$page     = $attributes['page'];
1355
			$section  = $attributes['section'];
1356
			$args     = array_merge(
1357
				$attributes['args'],
1358
				array(
1359
					'title'     => $title,
1360
					'id'        => $id,
1361
					'label_for' => $id,
1362
					'name'      => $name,
1363
				)
1364
			);
1365
			add_settings_field( $id, $title, $callback, $page, $section, $args );
1366
			register_setting( $page, $id );
1367
		} // End foreach() method.
1368
	}
1369
1370
	/**
1371
	 * Create the notices, settings, and conditions by which admin notices should appear
1372
	 */
1373
	public function notices() {
1374
1375
		// before a notice is displayed, we should make sure we are on a page related to this plugin.
1376
		if ( ! isset( $_GET['page'] ) || $this->admin_settings_url_param !== $_GET['page'] ) {
1377
			return;
1378
		}
1379
1380
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
1381
1382
		$notices = array(
1383
			'permission'              => array(
1384
				'condition'   => ( false === $this->check_wordpress_admin_permissions() ),
1385
				'message'     => __( "Your account does not have permission to edit the Object Sync for Salesforce plugin's settings.", 'object-sync-for-salesforce' ),
1386
				'type'        => 'error',
1387
				'dismissible' => false,
1388
			),
1389
			'fieldmap'                => array(
1390
				'condition'   => isset( $get_data['transient'] ),
1391
				'message'     => __( 'Errors kept this fieldmap from being saved.', 'object-sync-for-salesforce' ),
1392
				'type'        => 'error',
1393
				'dismissible' => true,
1394
			),
1395
			'object_map'              => array(
1396
				'condition'   => isset( $get_data['map_transient'] ),
1397
				'message'     => __( 'Errors kept this object map from being saved.', 'object-sync-for-salesforce' ),
1398
				'type'        => 'error',
1399
				'dismissible' => true,
1400
			),
1401
			'data_saved'              => array(
1402
				'condition'   => isset( $get_data['data_saved'] ) && 'true' === $get_data['data_saved'],
1403
				'message'     => __( 'This data was successfully saved.', 'object-sync-for-salesforce' ),
1404
				'type'        => 'success',
1405
				'dismissible' => true,
1406
			),
1407
			'data_save_partial'       => array(
1408
				'condition'   => isset( $get_data['data_saved'] ) && 'partial' === $get_data['data_saved'],
1409
				'message'     => __( 'This data was partially successfully saved. This means some of the data was unable to save. If you have enabled logging in the plugin settings, there should be a log entry with further details.', 'object-sync-for-salesforce' ),
1410
				'type'        => 'error',
1411
				'dismissible' => true,
1412
			),
1413
			'data_save_error'         => array(
1414
				'condition'   => isset( $get_data['data_saved'] ) && 'false' === $get_data['data_saved'],
1415
				'message'     => __( 'This data was not successfully saved. Try again.', 'object-sync-for-salesforce' ),
1416
				'type'        => 'error',
1417
				'dismissible' => true,
1418
			),
1419
			'mapping_error_transient' => array(
1420
				'condition'   => isset( $get_data['mapping_error_transient'] ),
1421
				'message'     => __( 'Errors kept these mapping errors from being deleted.', 'object-sync-for-salesforce' ),
1422
				'type'        => 'error',
1423
				'dismissible' => true,
1424
			),
1425
		);
1426
1427
		foreach ( $notices as $key => $value ) {
1428
1429
			$condition = (bool) $value['condition'];
1430
			$message   = $value['message'];
1431
1432
			if ( isset( $value['dismissible'] ) ) {
1433
				$dismissible = $value['dismissible'];
1434
			} else {
1435
				$dismissible = false;
1436
			}
1437
1438
			if ( isset( $value['type'] ) ) {
1439
				$type = $value['type'];
1440
			} else {
1441
				$type = '';
1442
			}
1443
1444
			if ( ! isset( $value['template'] ) ) {
1445
				$template = '';
1446
			}
1447
1448
			if ( $condition ) {
1449
				new Object_Sync_Sf_Admin_Notice( $condition, $message, $dismissible, $type, $template );
1450
			}
1451
		}
1452
1453
	}
1454
1455
	/**
1456
	 * Get all the Salesforce object settings for fieldmapping
1457
	 * This takes either the $_POST array via ajax, or can be directly called with a $data array
1458
	 *
1459
	 * @param array $data must contain a Salesforce object, can optionally contain a type.
1460
	 * @return array $object_settings
1461
	 */
1462
	public function get_salesforce_object_description( $data = array() ) {
1463
		$ajax = false;
1464
		if ( empty( $data ) ) {
1465
			$data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1466
			$ajax = true;
1467
		}
1468
1469
		$object_description = array();
1470
1471
		if ( ! empty( $data['salesforce_object'] ) ) {
1472
			$object = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1473
1474
			$object_fields        = array();
1475
			$include_record_types = array();
1476
1477
			// these can come from ajax.
1478
			$include = isset( $data['include'] ) ? (array) $data['include'] : array();
1479
			$include = array_map( 'esc_attr', $include );
1480
1481
			if ( in_array( 'fields', $include, true ) || empty( $include ) ) {
1482
				$type = isset( $data['field_type'] ) ? esc_attr( $data['field_type'] ) : ''; // can come from ajax.
1483
1484
				// here, we should respect the decision of whether to show the API name or the label.
1485
				$display_value = get_option( $this->option_prefix . 'salesforce_field_display_value', 'field_label' );
1486
				if ( 'api_name' === $display_value ) {
1487
					$visible_label_field = 'name';
1488
				} else {
1489
					$visible_label_field = 'label';
1490
				}
1491
				$attributes = array( 'name', $visible_label_field );
1492
1493
				foreach ( $object['data']['fields'] as $key => $value ) {
1494
					if ( '' === $type || $type === $value['type'] ) {
1495
						$object_fields[ $key ] = $value;
1496
						if ( isset( $attributes ) ) {
1497
							$object_fields[ $key ] = array_intersect_key( $value, array_flip( $attributes ) );
1498
						}
1499
					}
1500
				}
1501
				$object_description['fields'] = $object_fields;
1502
			}
1503
1504
			if ( in_array( 'recordTypeInfos', $include, true ) ) {
1505
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1506
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1507
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1508
					}
1509
					$object_description['recordTypeInfos'] = $object_record_types;
1510
				}
1511
			}
1512
		}
1513
1514
		if ( true === $ajax ) {
1515
			wp_send_json_success( $object_description );
1516
		} else {
1517
			return $object_description;
1518
		}
1519
	}
1520
1521
	/**
1522
	 * Get all the Salesforce fields settings for fieldmapping
1523
	 * This takes either the $_POST array via ajax, or can be directly called with a $data array
1524
	 *
1525
	 * @param array $data must contain a Salesforce object unless it is Ajax, can optionally contain a type.
1526
	 * @return array $object_fields
1527
	 */
1528
	public function get_salesforce_object_fields( $data = array() ) {
1529
		$ajax      = false;
1530
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1531
		if ( empty( $data ) ) {
1532
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_object'] ) ) : '';
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($post_data['salesforce_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

1532
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['salesforce_object'] ) ) : '';
Loading history...
1533
			$ajax              = true;
1534
			// here, we should respect the decision of whether to show the API name or the label.
1535
			$display_value = get_option( $this->option_prefix . 'salesforce_field_display_value', 'field_label' );
1536
			if ( 'api_name' === $display_value ) {
1537
				$visible_label_field = 'name';
1538
			} else {
1539
				$visible_label_field = 'label';
1540
			}
1541
			$attributes = array( 'name', $visible_label_field );
1542
		} else {
1543
			$salesforce_object = isset( $data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $data['salesforce_object'] ) ) : '';
1544
		}
1545
		$object_fields = array();
1546
		if ( ! empty( $salesforce_object ) ) {
1547
			$object               = $this->salesforce['sfapi']->object_describe( esc_attr( $salesforce_object ) );
1548
			$object_fields        = array();
1549
			$type                 = isset( $data['type'] ) ? esc_attr( $data['type'] ) : '';
1550
			$include_record_types = isset( $data['include_record_types'] ) ? esc_attr( $data['include_record_types'] ) : false;
1551
			foreach ( $object['data']['fields'] as $key => $value ) {
1552
				if ( '' === $type || $type === $value['type'] ) {
1553
					$object_fields[ $key ] = $value;
1554
					if ( isset( $attributes ) ) {
1555
						$object_fields[ $key ] = array_intersect_key( $value, array_flip( $attributes ) );
1556
					}
1557
				}
1558
			}
1559
			if ( true === $include_record_types ) {
0 ignored issues
show
introduced by
The condition true === $include_record_types is always false.
Loading history...
1560
				$object_record_types = array();
1561
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1562
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1563
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1564
					}
1565
				}
1566
			}
1567
		}
1568
1569
		if ( true === $ajax ) {
1570
			$ajax_response = array(
1571
				'fields' => $object_fields,
1572
			);
1573
			wp_send_json_success( $ajax_response );
1574
		} else {
1575
			return $object_fields;
1576
		}
1577
1578
	}
1579
1580
	/**
1581
	 * Get WordPress object fields for fieldmapping
1582
	 * This takes either the $_POST array via ajax, or can be directly called with a $wordpress_object field
1583
	 *
1584
	 * @param string $wordpress_object is the name of the WordPress object.
1585
	 * @return array $object_fields
1586
	 */
1587
	public function get_wordpress_object_fields( $wordpress_object = '' ) {
1588
		$ajax      = false;
1589
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1590
		if ( empty( $wordpress_object ) ) {
1591
			$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

1591
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1592
			$ajax             = true;
1593
		}
1594
1595
		$object_fields = $this->wordpress->get_wordpress_object_fields( $wordpress_object );
1596
1597
		if ( true === $ajax ) {
1598
			$ajax_response = array(
1599
				'fields' => $object_fields,
1600
			);
1601
			wp_send_json_success( $ajax_response );
1602
		} else {
1603
			return $object_fields;
1604
		}
1605
	}
1606
1607
	/**
1608
	 * Get WordPress and Salesforce object fields together for fieldmapping
1609
	 * This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $salesforce_object fields
1610
	 *
1611
	 * @deprecated since 1.9.0
1612
	 * @param string $wordpress_object is the name of the WordPress object.
1613
	 * @param string $salesforce_object is the name of the Salesforce object.
1614
	 * @return array $object_fields
1615
	 */
1616
	public function get_wp_sf_object_fields( $wordpress_object = '', $salesforce_object = '' ) {
1617
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1618
		if ( empty( $wordpress_object ) ) {
1619
			$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

1619
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1620
		}
1621
		if ( empty( $salesforce_object ) ) {
1622
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_object'] ) ) : '';
1623
		}
1624
1625
		$object_fields['wordpress']  = $this->get_wordpress_object_fields( $wordpress_object );
1626
		$object_fields['salesforce'] = $this->get_salesforce_object_fields(
1627
			array(
1628
				'salesforce_object' => $salesforce_object,
1629
			)
1630
		);
1631
1632
		if ( ! empty( $post_data ) ) {
1633
			wp_send_json_success( $object_fields );
1634
		} else {
1635
			return $object_fields;
1636
		}
1637
	}
1638
1639
	/**
1640
	 * Manually push the WordPress object to Salesforce
1641
	 * This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $wordpress_id fields
1642
	 *
1643
	 * @param string $wordpress_object is the name of the WordPress object.
1644
	 * @param int    $wordpress_id is the ID of the WordPress record.
1645
	 * @param bool   $force_return Force the method to return json instead of outputting it.
1646
	 */
1647
	public function push_to_salesforce( $wordpress_object = '', $wordpress_id = '', $force_return = false ) {
1648
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1649
		if ( empty( $wordpress_object ) && empty( $wordpress_id ) ) {
1650
			$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

1650
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1651
			$wordpress_id     = isset( $post_data['wordpress_id'] ) ? absint( $post_data['wordpress_id'] ) : '';
1652
		}
1653
1654
		// clarify what that variable is in this context.
1655
		$object_type = $wordpress_object;
1656
1657
		// When objects are already mapped, there is a Salesforce id as well. Otherwise, it's blank.
1658
		$salesforce_id = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( $post_data['salesforce_id'] ) : '';
1659
		if ( '' === $salesforce_id ) {
1660
			$method = 'POST';
1661
		} else {
1662
			$method = 'PUT';
1663
		}
1664
1665
		$result = $this->push->manual_push( $object_type, $wordpress_id, $method );
1666
1667
		if ( false === $force_return && ! empty( $post_data['wordpress_object'] ) && ! empty( $post_data['wordpress_id'] ) ) {
1668
			wp_send_json_success( $result );
1669
		} else {
1670
			return $result;
1671
		}
1672
1673
	}
1674
1675
	/**
1676
	 * Manually pull the Salesforce object into WordPress
1677
	 * This takes either the $_POST array via ajax, or can be directly called with $salesforce_id fields
1678
	 *
1679
	 * @param string $salesforce_id is the ID of the Salesforce record.
1680
	 * @param string $wordpress_object is the name of the WordPress object.
1681
	 */
1682
	public function pull_from_salesforce( $salesforce_id = '', $wordpress_object = '' ) {
1683
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1684
		if ( empty( $wordpress_object ) && empty( $salesforce_id ) ) {
1685
			$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

1685
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1686
			$salesforce_id    = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_id'] ) ) : '';
1687
		}
1688
		$type   = $this->salesforce['sfapi']->get_sobject_type( $salesforce_id );
1689
		$result = $this->pull->manual_pull( $type, $salesforce_id, $wordpress_object ); // we want the wp object to make sure we get the right fieldmap.
1690
		if ( ! empty( $post_data ) ) {
1691
			wp_send_json_success( $result );
1692
		} else {
1693
			return $result;
1694
		}
1695
	}
1696
1697
	/**
1698
	 * Manually pull the Salesforce object into WordPress
1699
	 * This takes an id for a mapping object row
1700
	 *
1701
	 * @param int $mapping_id is the ID of the mapping object record.
1702
	 */
1703
	public function refresh_mapped_data( $mapping_id = '' ) {
1704
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1705
		if ( empty( $mapping_id ) ) {
1706
			$mapping_id = isset( $post_data['mapping_id'] ) ? absint( $post_data['mapping_id'] ) : '';
1707
		}
1708
		$result = $this->mappings->get_all_object_maps(
1709
			array(
1710
				'id' => $mapping_id,
1711
			)
1712
		);
1713
1714
		$object_map = array();
1715
1716
		// result is an array of arrays, not just one array.
1717
		if ( 1 === count( $result ) ) {
1718
			$object_map = $result[0];
1719
		}
1720
1721
		if ( ! empty( $post_data ) ) {
1722
			wp_send_json_success( $object_map );
1723
		} else {
1724
			return $object_map;
1725
		}
1726
	}
1727
1728
	/**
1729
	 * Prepare fieldmap data and redirect after processing
1730
	 * This runs when the create or update forms are submitted
1731
	 * It is public because it depends on an admin hook
1732
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1733
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1734
	 */
1735
	public function prepare_fieldmap_data() {
1736
		$error     = false;
1737
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1738
		$cachekey  = wp_json_encode( $post_data );
1739
		if ( false !== $cachekey ) {
1740
			$cachekey = md5( $cachekey );
1741
		}
1742
1743
		if ( ! isset( $post_data['label'] ) || ! isset( $post_data['salesforce_object'] ) || ! isset( $post_data['wordpress_object'] ) ) {
1744
			$error = true;
1745
		}
1746
		if ( true === $error ) {
1747
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1748
			if ( '' !== $cachekey ) {
1749
				$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

1749
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1750
			}
1751
		} else { // there are no errors
1752
			// send the row to the fieldmap class
1753
			// if it is add or clone, use the create method.
1754
			$method            = esc_attr( $post_data['method'] );
1755
			$salesforce_fields = $this->get_salesforce_object_fields(
1756
				array(
1757
					'salesforce_object' => $post_data['salesforce_object'],
1758
				)
1759
			);
1760
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1761
			if ( 'add' === $method || 'clone' === $method ) {
1762
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1763
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method.
1764
				$id     = esc_attr( $post_data['id'] );
1765
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
1766
			}
1767
			if ( false === $result ) { // if the database didn't save, it's still an error.
1768
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1769
				if ( '' !== $cachekey ) {
1770
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1771
				}
1772
			} else {
1773
				// if the user has saved a fieldmap, clear the currently running query value if there is one.
1774
				if ( '' !== get_option( $this->option_prefix . 'currently_pulling_query_' . $post_data['salesforce_object'], '' ) ) {
1775
					$this->pull->clear_current_type_query( $post_data['salesforce_object'] );
1776
				}
1777
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1778
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1779
				}
1780
				// then send the user to the list of fieldmaps.
1781
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1782
			}
1783
		}
1784
		wp_safe_redirect( $url );
1785
		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...
1786
	}
1787
1788
	/**
1789
	 * Delete fieldmap data and redirect after processing
1790
	 * This runs when the delete link is clicked, after the user confirms
1791
	 * It is public because it depends on an admin hook
1792
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1793
	 */
1794
	public function delete_fieldmap() {
1795
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1796
		if ( $post_data['id'] ) {
1797
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1798
			if ( true === $result ) {
1799
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1800
			} else {
1801
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1802
			}
1803
			wp_safe_redirect( $url );
1804
			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...
1805
		}
1806
	}
1807
1808
	/**
1809
	 * Prepare object data and redirect after processing
1810
	 * This runs when the update form is submitted
1811
	 * It is public because it depends on an admin hook
1812
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1813
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1814
	 */
1815
	public function prepare_object_map_data() {
1816
		$error     = false;
1817
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1818
		$cachekey  = wp_json_encode( $post_data );
1819
		if ( false !== $cachekey ) {
1820
			$cachekey = md5( $cachekey );
1821
		}
1822
1823
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1824
			$error = true;
1825
		}
1826
		if ( true === $error ) {
1827
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1828
			if ( '' !== $cachekey ) {
1829
				$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

1829
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1830
			}
1831
		} else { // there are no errors
1832
			// send the row to the object map class.
1833
			$method = esc_attr( $post_data['method'] );
1834
			if ( 'edit' === $method ) { // if it is edit, use the update method.
1835
				$id     = esc_attr( $post_data['id'] );
1836
				$result = $this->mappings->update_object_map( $post_data, $id );
1837
			}
1838
			if ( false === $result ) { // if the database didn't save, it's still an error.
1839
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1840
				if ( '' !== $cachekey ) {
1841
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1842
				}
1843
			} else {
1844
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1845
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1846
				}
1847
				// then send the user to the success redirect url.
1848
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1849
			}
1850
		}
1851
		wp_safe_redirect( $url );
1852
		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...
1853
	}
1854
1855
	/**
1856
	 * Delete object map data and redirect after processing
1857
	 * This runs when the delete link is clicked on an error row, after the user confirms
1858
	 * It is public because it depends on an admin hook
1859
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1860
	 */
1861
	public function delete_object_map() {
1862
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1863
		if ( isset( $post_data['id'] ) ) {
1864
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1865
			if ( true === $result ) {
1866
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1867
			} else {
1868
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1869
			}
1870
			wp_safe_redirect( $url );
1871
			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...
1872
		} elseif ( $post_data['delete'] ) {
1873
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1874
			$cachekey  = wp_json_encode( $post_data );
1875
			if ( false !== $cachekey ) {
1876
				$cachekey = md5( $cachekey );
1877
			}
1878
			$error = false;
1879
			if ( ! isset( $post_data['delete'] ) ) {
1880
				$error = true;
1881
			}
1882
			if ( true === $error ) {
1883
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1884
				if ( '' !== $cachekey ) {
1885
					$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

1885
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1886
				}
1887
			} else { // there are no errors.
1888
				$result = $this->mappings->delete_object_map( array_keys( $post_data['delete'] ) );
1889
				if ( true === $result ) {
1890
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1891
				}
1892
1893
				if ( false === $result ) { // if the database didn't save, it's still an error.
1894
					$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1895
					if ( '' !== $cachekey ) {
1896
						$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
1897
					}
1898
				} else {
1899
					if ( isset( $post_data['mapping_error_transient'] ) ) { // there was previously an error saved. can delete it now.
1900
						$this->sfwp_transients->delete( esc_attr( $post_data['mapping_error_transient'] ) );
1901
					}
1902
					// then send the user to the list of fieldmaps.
1903
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1904
				}
1905
			}
1906
			wp_safe_redirect( $url );
1907
			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...
1908
		}
1909
	}
1910
1911
	/**
1912
	 * Import a json file and use it for plugin data
1913
	 */
1914
	public function import_json_file() {
1915
1916
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_import'], 'object_sync_for_salesforce_nonce_import' ) ) {
1917
			return;
1918
		}
1919
		if ( ! current_user_can( 'manage_options' ) ) {
1920
			return;
1921
		}
1922
		$path      = $_FILES['import_file']['name'];
1923
		$extension = pathinfo( $path, PATHINFO_EXTENSION );
1924
		if ( 'json' !== $extension ) {
1925
			wp_die( esc_html__( 'Please upload a valid .json file', 'object-sync-for-salesforce' ) );
1926
		}
1927
1928
		$import_file = $_FILES['import_file']['tmp_name'];
1929
		if ( empty( $import_file ) ) {
1930
			wp_die( esc_html__( 'Please upload a file to import', 'object-sync-for-salesforce' ) );
1931
		}
1932
1933
		// Retrieve the data from the file and convert the json object to an array.
1934
		$data = (array) json_decode( file_get_contents( $import_file ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
1935
1936
		$overwrite = isset( $_POST['overwrite'] ) ? esc_attr( $_POST['overwrite'] ) : '';
1937
		if ( true === filter_var( $overwrite, FILTER_VALIDATE_BOOLEAN ) ) {
1938
			if ( isset( $data['fieldmaps'] ) ) {
1939
				$fieldmaps = $this->mappings->get_fieldmaps();
1940
				foreach ( $fieldmaps as $fieldmap ) {
1941
					$id     = $fieldmap['id'];
1942
					$delete = $this->mappings->delete_fieldmap( $id );
1943
				}
1944
			}
1945
			if ( isset( $data['object_maps'] ) ) {
1946
				$object_maps = $this->mappings->get_all_object_maps();
1947
				foreach ( $object_maps as $object_map ) {
1948
					$id     = $object_map['id'];
1949
					$delete = $this->mappings->delete_object_map( $id );
1950
				}
1951
			}
1952
			if ( isset( $data['plugin_settings'] ) ) {
1953
				foreach ( $data['plugin_settings'] as $key => $value ) {
1954
					delete_option( $value['option_name'] );
1955
				}
1956
			}
1957
		}
1958
1959
		$success = true;
1960
1961
		if ( isset( $data['fieldmaps'] ) ) {
1962
			foreach ( $data['fieldmaps'] as $fieldmap ) {
1963
				unset( $fieldmap['id'] );
1964
				$create = $this->mappings->create_fieldmap( $fieldmap );
1965
				if ( false === $create ) {
1966
					$success = false;
1967
				}
1968
			}
1969
		}
1970
1971
		if ( isset( $data['object_maps'] ) ) {
1972
			$successful_object_maps = array();
1973
			$error_object_maps      = array();
1974
			foreach ( $data['object_maps'] as $object_map ) {
1975
				unset( $object_map['id'] );
1976
				if ( isset( $object_map['object_type'] ) ) {
1977
					$sf_sync_trigger = $this->mappings->sync_sf_create;
1978
					$create          = $this->pull->salesforce_pull_process_records( $object_map['object_type'], $object_map['salesforce_id'], $sf_sync_trigger );
1979
				} else {
1980
					$create = $this->mappings->create_object_map( $object_map );
1981
				}
1982
				if ( false === $create ) {
1983
					$error_object_maps[] = $object_map;
1984
				} else {
1985
					$successful_object_maps[] = $create;
1986
				}
1987
			}
1988
		}
1989
1990
		if ( isset( $data['plugin_settings'] ) ) {
1991
			foreach ( $data['plugin_settings'] as $key => $value ) {
1992
				update_option( $value['option_name'], maybe_unserialize( $value['option_value'] ), $value['autoload'] );
1993
			}
1994
		}
1995
1996
		$status = 'error';
1997
		if ( isset( $this->logging ) ) {
1998
			$logging = $this->logging;
1999
		} elseif ( class_exists( 'Object_Sync_Sf_Logging' ) ) {
2000
			$logging = new Object_Sync_Sf_Logging( $this->wpdb, $this->version );
0 ignored issues
show
Unused Code introduced by
The call to Object_Sync_Sf_Logging::__construct() has too many arguments starting with $this->wpdb. ( Ignorable by Annotation )

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

2000
			$logging = /** @scrutinizer ignore-call */ new Object_Sync_Sf_Logging( $this->wpdb, $this->version );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2001
		}
2002
2003
		$body = sprintf( esc_html__( 'These are the import items that were not able to save: ', 'object-sync-for-salesforce' ) . '<ul>' );
2004
		foreach ( $error_object_maps as $mapping_object ) {
2005
			$body .= sprintf(
2006
				// translators: placeholders are: 1) the mapping object row ID, 2) the ID of the Salesforce object, 3) the WordPress object type.
2007
				'<li>' . esc_html__( 'Mapping object id (if it exists): %1$s. Salesforce Id: %2$s. WordPress object type: %3$s', 'object-sync-for-salesforce' ) . '</li>',
2008
				isset( $mapping_object['id'] ) ? absint( $mapping_object['id'] ) : '',
2009
				esc_attr( $mapping_object['salesforce_id'] ),
2010
				esc_attr( $mapping_object['wordpress_object'] )
2011
			);
2012
		}
2013
		$body .= sprintf( '</ul>' );
2014
2015
		$logging->setup(
2016
			sprintf(
2017
				// translators: %1$s is the log status.
2018
				esc_html__( '%1$s on import: some of the rows were unable to save. Read this post for details.', 'object-sync-for-salesforce' ),
2019
				ucfirst( esc_attr( $status ) )
2020
			),
2021
			$body,
2022
			0,
2023
			0,
2024
			$status
2025
		);
2026
2027
		if ( empty( $error_object_maps ) && ! empty( $successful_object_maps ) ) {
2028
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=true' ) );
2029
			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...
2030
		} elseif ( ! empty( $error_object_maps ) && ! empty( $successful_object_maps ) ) {
2031
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=partial' ) );
2032
		} else {
2033
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=false' ) );
2034
			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...
2035
		}
2036
2037
	}
2038
2039
	/**
2040
	 * Create a json file for exporting
2041
	 */
2042
	public function export_json_file() {
2043
2044
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_export'], 'object_sync_for_salesforce_nonce_export' ) ) {
2045
			return;
2046
		}
2047
		if ( ! current_user_can( 'manage_options' ) ) {
2048
			return;
2049
		}
2050
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
2051
		$export    = array();
2052
		if ( in_array( 'fieldmaps', $post_data['export'], true ) ) {
2053
			$export['fieldmaps'] = $this->mappings->get_fieldmaps();
2054
		}
2055
		if ( in_array( 'object_maps', $post_data['export'], true ) ) {
2056
			$export['object_maps'] = $this->mappings->get_all_object_maps();
2057
		}
2058
		if ( in_array( 'plugin_settings', $post_data['export'], true ) ) {
2059
			$wpdb                      = $this->wpdb;
2060
			$export_results            = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$wpdb->base_prefix}options` WHERE option_name LIKE %s;", $wpdb->esc_like( $this->option_prefix ) . '%' ), ARRAY_A );
2061
			$export['plugin_settings'] = $export_results;
2062
		}
2063
		nocache_headers();
2064
		header( 'Content-Type: application/json; charset=utf-8' );
2065
		header( 'Content-Disposition: attachment; filename=object-sync-for-salesforce-data-export-' . gmdate( 'm-d-Y' ) . '.json' );
2066
		header( 'Expires: 0' );
2067
		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

2067
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
2068
		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...
2069
	}
2070
2071
	/**
2072
	 * Default display for <input> fields
2073
	 *
2074
	 * @param array $args is the arguments to create the field.
2075
	 */
2076
	public function display_input_field( $args ) {
2077
		$type    = $args['type'];
2078
		$id      = $args['label_for'];
2079
		$name    = $args['name'];
2080
		$desc    = $args['desc'];
2081
		$checked = '';
2082
2083
		$class = 'regular-text';
2084
2085
		if ( 'checkbox' === $type ) {
2086
			$class = 'checkbox';
2087
		}
2088
2089
		if ( isset( $args['class'] ) ) {
2090
			$class = $args['class'];
2091
		}
2092
2093
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
2094
			$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

2094
			$value = esc_attr( /** @scrutinizer ignore-type */ get_option( $id, '' ) );
Loading history...
2095
			if ( 'checkbox' === $type ) {
2096
				$value = filter_var( get_option( $id, false ), FILTER_VALIDATE_BOOLEAN );
2097
				if ( true === $value ) {
2098
					$checked = 'checked ';
2099
				}
2100
				$value = 1;
2101
			}
2102
			if ( '' === $value && isset( $args['default'] ) && '' !== $args['default'] ) {
2103
				$value = $args['default'];
2104
			}
2105
2106
			echo sprintf(
2107
				'<input type="%1$s" value="%2$s" name="%3$s" id="%4$s" class="%5$s"%6$s>',
2108
				esc_attr( $type ),
2109
				esc_attr( $value ),
2110
				esc_attr( $name ),
2111
				esc_attr( $id ),
2112
				sanitize_html_class( $class . esc_html( ' code' ) ),
2113
				esc_html( $checked )
2114
			);
2115
			if ( '' !== $desc ) {
2116
				echo sprintf(
2117
					'<p class="description">%1$s</p>',
2118
					esc_html( $desc )
2119
				);
2120
			}
2121
		} else {
2122
			echo sprintf(
2123
				'<p><code>%1$s</code></p>',
2124
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
2125
			);
2126
		}
2127
	}
2128
2129
	/**
2130
	 * Display for multiple checkboxes
2131
	 * Above method can handle a single checkbox as it is
2132
	 *
2133
	 * @param array $args is the arguments to create the checkboxes.
2134
	 */
2135
	public function display_checkboxes( $args ) {
2136
		$type    = 'checkbox';
2137
		$name    = $args['name'];
2138
		$options = get_option( $name, array() );
2139
		foreach ( $args['items'] as $key => $value ) {
2140
			$text    = $value['text'];
2141
			$id      = $value['id'];
2142
			$desc    = $value['desc'];
2143
			$checked = '';
2144
			if ( is_array( $options ) && in_array( (string) $key, $options, true ) ) {
2145
				$checked = 'checked';
2146
			} elseif ( is_array( $options ) && empty( $options ) ) {
2147
				if ( isset( $value['default'] ) && true === $value['default'] ) {
2148
					$checked = 'checked';
2149
				}
2150
			}
2151
			echo sprintf(
2152
				'<div class="checkbox"><label><input type="%1$s" value="%2$s" name="%3$s[]" id="%4$s"%5$s>%6$s</label></div>',
2153
				esc_attr( $type ),
2154
				esc_attr( $key ),
2155
				esc_attr( $name ),
2156
				esc_attr( $id ),
2157
				esc_html( $checked ),
2158
				esc_html( $text )
2159
			);
2160
			if ( '' !== $desc ) {
2161
				echo sprintf(
2162
					'<p class="description">%1$s</p>',
2163
					esc_html( $desc )
2164
				);
2165
			}
2166
		}
2167
	}
2168
2169
	/**
2170
	 * Display for a dropdown
2171
	 *
2172
	 * @param array $args is the arguments needed to create the dropdown.
2173
	 */
2174
	public function display_select( $args ) {
2175
		$type = $args['type'];
2176
		$id   = $args['label_for'];
2177
		$name = $args['name'];
2178
		$desc = $args['desc'];
2179
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
2180
			$current_value = get_option( $name );
2181
2182
			echo sprintf(
2183
				'<div class="select"><select id="%1$s" name="%2$s"><option value="">- ' . esc_html__( 'Select one', 'object-sync-for-salesforce' ) . ' -</option>',
2184
				esc_attr( $id ),
2185
				esc_attr( $name )
2186
			);
2187
2188
			foreach ( $args['items'] as $key => $value ) {
2189
				$text     = $value['text'];
2190
				$value    = $value['value'];
2191
				$selected = '';
2192
				if ( $key === $current_value || $value === $current_value ) {
2193
					$selected = ' selected';
2194
				}
2195
2196
				echo sprintf(
2197
					'<option value="%1$s"%2$s>%3$s</option>',
2198
					esc_attr( $value ),
2199
					esc_attr( $selected ),
2200
					esc_html( $text )
2201
				);
2202
2203
			}
2204
			echo '</select>';
2205
			if ( '' !== $desc ) {
2206
				echo sprintf(
2207
					'<p class="description">%1$s</p>',
2208
					esc_html( $desc )
2209
				);
2210
			}
2211
			echo '</div>';
2212
		} else {
2213
			echo sprintf(
2214
				'<p><code>%1$s</code></p>',
2215
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
2216
			);
2217
		}
2218
	}
2219
2220
	/**
2221
	 * Dropdown formatted list of Salesforce API versions
2222
	 *
2223
	 * @return array $args is the array of API versions for the dropdown.
2224
	 */
2225
	private function version_options() {
2226
		$args = array();
2227
		if ( defined( 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION' ) || ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== $this->admin_settings_url_param ) {
2228
			return $args;
2229
		}
2230
		$versions = $this->salesforce['sfapi']->get_api_versions();
2231
		foreach ( $versions['data'] as $key => $value ) {
2232
			$args[] = array(
2233
				'value' => $value['version'],
2234
				'text'  => $value['label'] . ' (' . $value['version'] . ')',
2235
			);
2236
		}
2237
		return $args;
2238
	}
2239
2240
	/**
2241
	 * Default display for <a href> links
2242
	 *
2243
	 * @param array $args is the arguments to make the link.
2244
	 */
2245
	public function display_link( $args ) {
2246
		$label = $args['label'];
2247
		$desc  = $args['desc'];
2248
		$url   = $args['url'];
2249
		if ( isset( $args['link_class'] ) ) {
2250
			echo sprintf(
2251
				'<p><a class="%1$s" href="%2$s">%3$s</a></p>',
2252
				esc_attr( $args['link_class'] ),
2253
				esc_url( $url ),
2254
				esc_html( $label )
2255
			);
2256
		} else {
2257
			echo sprintf(
2258
				'<p><a href="%1$s">%2$s</a></p>',
2259
				esc_url( $url ),
2260
				esc_html( $label )
2261
			);
2262
		}
2263
2264
		if ( '' !== $desc ) {
2265
			echo sprintf(
2266
				'<p class="description">%1$s</p>',
2267
				esc_html( $desc )
2268
			);
2269
		}
2270
2271
	}
2272
2273
	/**
2274
	 * Allow for a standard sanitize/validate method. We could use more specific ones if need be, but this one provides a baseline.
2275
	 *
2276
	 * @param string $option is the option value.
2277
	 * @return string $option is the sanitized option value.
2278
	 */
2279
	public function sanitize_validate_text( $option ) {
2280
		if ( is_array( $option ) ) {
0 ignored issues
show
introduced by
The condition is_array($option) is always false.
Loading history...
2281
			$options = array();
2282
			foreach ( $option as $key => $value ) {
2283
				$options[ $key ] = sanitize_text_field( $value );
2284
			}
2285
			return $options;
2286
		}
2287
		$option = sanitize_text_field( $option );
2288
		return $option;
2289
	}
2290
2291
	/**
2292
	 * Run a demo of Salesforce API call on the authenticate tab after WordPress has authenticated with it
2293
	 *
2294
	 * @param object $sfapi this is the Salesforce API object.
2295
	 */
2296
	private function status( $sfapi ) {
2297
2298
		$versions = $sfapi->get_api_versions();
2299
2300
		// format this array into text so users can see the versions.
2301
		if ( true === $versions['cached'] ) {
2302
			$versions_is_cached = esc_html__( 'This list is cached, and', 'object-sync-for-salesforce' );
2303
		} else {
2304
			$versions_is_cached = esc_html__( 'This list is not cached, but', 'object-sync-for-salesforce' );
2305
		}
2306
2307
		if ( true === $versions['from_cache'] ) {
2308
			$versions_from_cache = esc_html__( 'items were loaded from the cache', 'object-sync-for-salesforce' );
2309
		} else {
2310
			$versions_from_cache = esc_html__( 'items were not loaded from the cache', 'object-sync-for-salesforce' );
2311
		}
2312
2313
		$versions_apicall_summary = sprintf(
2314
			// 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.
2315
			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' ),
2316
			$versions_is_cached,
2317
			$versions_from_cache
2318
		);
2319
2320
		$contacts = $sfapi->query( 'SELECT Name, Id from Contact LIMIT 100' );
2321
2322
		// format this array into html so users can see the contacts.
2323
		if ( true === $contacts['cached'] ) {
2324
			$contacts_is_cached = esc_html__( 'They are cached, and', 'object-sync-for-salesforce' );
2325
		} else {
2326
			$contacts_is_cached = esc_html__( 'They are not cached, but', 'object-sync-for-salesforce' );
2327
		}
2328
2329
		if ( true === $contacts['from_cache'] ) {
2330
			$contacts_from_cache = esc_html__( 'they were loaded from the cache', 'object-sync-for-salesforce' );
2331
		} else {
2332
			$contacts_from_cache = esc_html__( 'they were not loaded from the cache', 'object-sync-for-salesforce' );
2333
		}
2334
2335
		if ( true === $contacts['is_redo'] ) {
2336
			$contacts_refreshed_token = esc_html__( 'This request did require refreshing the Salesforce token', 'object-sync-for-salesforce' );
2337
		} else {
2338
			$contacts_refreshed_token = esc_html__( 'This request did not require refreshing the Salesforce token', 'object-sync-for-salesforce' );
2339
		}
2340
2341
		// display contact summary if there are any contacts.
2342
		if ( 0 < absint( $contacts['data']['totalSize'] ) ) {
2343
			$contacts_apicall_summary = sprintf(
2344
				// 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.
2345
				esc_html__( 'Salesforce successfully returned %1$s %2$s records. %3$s %4$s. %5$s.', 'object-sync-for-salesforce' ),
2346
				absint( $contacts['data']['totalSize'] ),
2347
				esc_html( $contacts['data']['records'][0]['attributes']['type'] ),
2348
				$contacts_is_cached,
2349
				$contacts_from_cache,
2350
				$contacts_refreshed_token
2351
			);
2352
		} else {
2353
			$contacts_apicall_summary = '';
2354
		}
2355
2356
		require_once plugin_dir_path( $this->file ) . '/templates/admin/status.php';
2357
2358
	}
2359
2360
	/**
2361
	 * Deauthorize WordPress from Salesforce.
2362
	 * This deletes the tokens from the database; it does not currently do anything in Salesforce
2363
	 * For this plugin at this time, that is the decision we are making: don't do any kind of authorization stuff inside Salesforce
2364
	 */
2365
	private function logout() {
2366
		$delete_access_token = delete_option( $this->option_prefix . 'access_token' );
2367
		if ( true === $delete_access_token ) {
2368
			$this->access_token = '';
2369
		}
2370
		$delete_instance_url = delete_option( $this->option_prefix . 'instance_url' );
2371
		if ( true === $delete_instance_url ) {
2372
			$this->instance_url = '';
2373
		}
2374
		$delete_refresh_token = delete_option( $this->option_prefix . 'refresh_token' );
2375
		if ( true === $delete_refresh_token ) {
2376
			$this->refresh_token = '';
2377
		}
2378
		echo sprintf(
2379
			'<p>You have been logged out. You can use the <a href="%1$s">%2$s</a> tab to log in again.</p>',
2380
			esc_url( get_admin_url( null, 'options-general.php?page' . $this->admin_settings_url_param . '&tab=authorize' ) ),
2381
			esc_html__( 'Authorize', 'object-sync-for-salesforce' )
2382
		);
2383
	}
2384
2385
	/**
2386
	 * Ajax call to clear the plugin cache.
2387
	 */
2388
	public function clear_sfwp_cache() {
2389
		$result   = $this->clear_cache( true );
2390
		$response = array(
2391
			'message' => $result['message'],
2392
			'success' => $result['success'],
2393
		);
2394
		wp_send_json_success( $response );
2395
	}
2396
2397
	/**
2398
	 * Clear the plugin's cache.
2399
	 * This uses the flush method contained in the WordPress cache to clear all of this plugin's cached data.
2400
	 *
2401
	 * @param bool $ajax Whether this is an Ajax request or not.
2402
	 * @return array
2403
	 */
2404
	private function clear_cache( $ajax = false ) {
2405
		$result  = $this->wordpress->sfwp_transients->flush();
2406
		$success = $result['success'];
2407
		if ( 0 < $result['count'] ) {
2408
			if ( true === $success ) {
2409
				$message = __( 'The plugin cache has been cleared.', 'object-sync-for-salesforce' );
2410
			} else {
2411
				$message = __( 'There was an error clearing the plugin cache. Try refreshing this page.', 'object-sync-for-salesforce' );
2412
			}
2413
		} else {
2414
			$success = true;
2415
			$message = __( 'The cache was not cleared because it is empty. You can try again later.', 'object-sync-for-salesforce' );
2416
		}
2417
		if ( false === $ajax ) {
2418
			echo '<p>' . esc_html( $message ) . '</p>';
2419
		} else {
2420
			return array(
2421
				'message' => esc_html( $message ),
2422
				'success' => $success,
2423
			);
2424
		}
2425
	}
2426
2427
	/**
2428
	 * Check WordPress Admin permissions
2429
	 * Check if the current user is allowed to access the Salesforce plugin options
2430
	 */
2431
	private function check_wordpress_admin_permissions() {
2432
2433
		// one programmatic way to give this capability to additional user roles is the
2434
		// object_sync_for_salesforce_roles_configure_salesforce hook
2435
		// it runs on activation of this plugin, and will assign the below capability to any role
2436
		// coming from the hook.
2437
2438
		// alternatively, other roles can get this capability in whatever other way you like
2439
		// point is: to administer this plugin, you need this capability.
2440
2441
		if ( ! current_user_can( 'configure_salesforce' ) ) {
2442
			return false;
2443
		} else {
2444
			return true;
2445
		}
2446
2447
	}
2448
2449
	/**
2450
	 * Show what we know about this user's relationship to a Salesforce object, if any
2451
	 *
2452
	 * @param object $user this is the user object from WordPress.
2453
	 */
2454
	public function show_salesforce_user_fields( $user ) {
2455
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2456
		if ( true === $this->check_wordpress_admin_permissions() ) {
2457
			$mappings = $this->mappings->load_all_by_wordpress( 'user', $user->ID );
2458
			$fieldmap = $this->mappings->get_fieldmaps(
2459
				null, // id field must be null for multiples.
2460
				array(
2461
					'wordpress_object' => 'user',
2462
				)
2463
			);
2464
			if ( count( $mappings ) > 0 ) {
2465
				foreach ( $mappings as $mapping ) {
2466
					if ( isset( $mapping['id'] ) && ! isset( $get_data['edit_salesforce_mapping'] ) && ! isset( $get_data['delete_salesforce_mapping'] ) ) {
2467
						require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce.php';
2468
					} elseif ( ! empty( $fieldmap ) ) { // is the user mapped to something already?
2469
						if ( isset( $get_data['edit_salesforce_mapping'] ) && true === filter_var( $get_data['edit_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2470
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-change.php';
2471
						} elseif ( isset( $get_data['delete_salesforce_mapping'] ) && true === filter_var( $get_data['delete_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2472
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-delete.php';
2473
						} else {
2474
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-map.php';
2475
						}
2476
					}
2477
				}
2478
			} else {
2479
				require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-map.php';
2480
			}
2481
		}
2482
	}
2483
2484
	/**
2485
	 * If the user profile has been mapped to Salesforce, do it
2486
	 *
2487
	 * @param int $user_id the ID of the WordPress user.
2488
	 */
2489
	public function save_salesforce_user_fields( $user_id ) {
2490
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
2491
		if ( isset( $post_data['salesforce_update_mapped_user'] ) && true === filter_var( $post_data['salesforce_update_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2492
			$mapping_object                  = $this->mappings->get_object_maps(
2493
				array(
2494
					'wordpress_id'     => $user_id,
2495
					'wordpress_object' => 'user',
2496
				)
2497
			);
2498
			$mapping_object['salesforce_id'] = $post_data['salesforce_id'];
2499
2500
			$result = $this->mappings->update_object_map( $mapping_object, $mapping_object['id'] );
2501
		} elseif ( isset( $post_data['salesforce_create_mapped_user'] ) && true === filter_var( $post_data['salesforce_create_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2502
			// if a Salesforce ID was entered.
2503
			if ( isset( $post_data['salesforce_id'] ) && ! empty( $post_data['salesforce_id'] ) ) {
2504
				$mapping_object = $this->create_object_map( $user_id, 'user', $post_data['salesforce_id'] );
2505
			} elseif ( isset( $post_data['push_new_user_to_salesforce'] ) ) {
2506
				// otherwise, create a new record in Salesforce.
2507
				$result = $this->push_to_salesforce( 'user', $user_id );
2508
			}
2509
		} elseif ( isset( $post_data['salesforce_delete_mapped_user'] ) && true === filter_var( $post_data['salesforce_delete_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2510
			// if a Salesforce ID was entered.
2511
			if ( isset( $post_data['mapping_id'] ) && ! empty( $post_data['mapping_id'] ) ) {
2512
				$delete = $this->mappings->delete_object_map( $post_data['mapping_id'] );
2513
			}
2514
		}
2515
	}
2516
2517
	/**
2518
	 * Render tabs for settings pages in admin
2519
	 *
2520
	 * @param array  $tabs is the tabs for the settings menu.
2521
	 * @param string $tab is a single tab.
2522
	 */
2523
	private function tabs( $tabs, $tab = '' ) {
2524
2525
		$get_data        = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2526
		$consumer_key    = $this->login_credentials['consumer_key'];
2527
		$consumer_secret = $this->login_credentials['consumer_secret'];
2528
		$callback_url    = $this->login_credentials['callback_url'];
2529
2530
		$current_tab = $tab;
2531
		echo '<h2 class="nav-tab-wrapper">';
2532
		foreach ( $tabs as $tab_key => $tab_caption ) {
2533
			$active = $current_tab === $tab_key ? ' nav-tab-active' : '';
2534
2535
			if ( true === $this->salesforce['is_authorized'] ) {
2536
				echo sprintf(
2537
					'<a class="nav-tab%1$s" href="%2$s">%3$s</a>',
2538
					esc_attr( $active ),
2539
					esc_url( '?page=' . $this->admin_settings_url_param . '&tab=' . $tab_key ),
2540
					esc_html( $tab_caption )
2541
				);
2542
			} elseif ( 'settings' === $tab_key || ( 'authorize' === $tab_key && isset( $consumer_key ) && isset( $consumer_secret ) && ! empty( $consumer_key ) && ! empty( $consumer_secret ) ) ) {
2543
				echo sprintf(
2544
					'<a class="nav-tab%1$s" href="%2$s">%3$s</a>',
2545
					esc_attr( $active ),
2546
					esc_url( '?page=' . $this->admin_settings_url_param . '&tab=' . $tab_key ),
2547
					esc_html( $tab_caption )
2548
				);
2549
			}
2550
		}
2551
		echo '</h2>';
2552
2553
		if ( isset( $get_data['tab'] ) ) {
2554
			$tab = sanitize_key( $get_data['tab'] );
2555
		} else {
2556
			$tab = '';
2557
		}
2558
	}
2559
2560
	/**
2561
	 * Clear schedule
2562
	 * This clears the schedule if the user clicks the button
2563
	 *
2564
	 * @param string $schedule_name is the name of the schedule being cleared.
2565
	 */
2566
	private function clear_schedule( $schedule_name = '' ) {
2567
		if ( '' !== $schedule_name ) {
2568
			$this->queue->cancel( $schedule_name );
2569
			// translators: $schedule_name is the name of the current queue. Defaults: salesforce_pull, salesforce_push, salesforce.
2570
			echo sprintf( esc_html__( 'You have cleared the %s schedule.', 'object-sync-for-salesforce' ), esc_html( $schedule_name ) );
2571
		} else {
2572
			echo esc_html__( 'You need to specify the name of the schedule you want to clear.', 'object-sync-for-salesforce' );
2573
		}
2574
	}
2575
2576
	/**
2577
	 * Get count of schedule items
2578
	 *
2579
	 * @param string $schedule_name is the name of the schedule.
2580
	 * @return int $count
2581
	 */
2582
	private function get_schedule_count( $schedule_name = '' ) {
2583
		if ( '' !== $schedule_name ) {
2584
			$count       = count(
2585
				$this->queue->search(
2586
					array(
2587
						'group'  => $schedule_name,
2588
						'status' => ActionScheduler_Store::STATUS_PENDING,
2589
					),
2590
					'ARRAY_A'
2591
				)
2592
			);
2593
			$group_count = count(
2594
				$this->queue->search(
2595
					array(
2596
						'group'  => $schedule_name . $this->action_group_suffix,
2597
						'status' => ActionScheduler_Store::STATUS_PENDING,
2598
					),
2599
					'ARRAY_A'
2600
				)
2601
			);
2602
			return $count + $group_count;
2603
		} else {
2604
			return 0;
2605
		}
2606
	}
2607
2608
	/**
2609
	 * Create an object map between a WordPress object and a Salesforce object
2610
	 *
2611
	 * @param int    $wordpress_id Unique identifier for the WordPress object.
2612
	 * @param string $wordpress_object What kind of object is it.
2613
	 * @param string $salesforce_id Unique identifier for the Salesforce object.
2614
	 * @param string $action Did we push or pull.
2615
	 * @return int   $wpdb->insert_id This is the database row for the map object
2616
	 */
2617
	private function create_object_map( $wordpress_id, $wordpress_object, $salesforce_id, $action = '' ) {
2618
		// Create object map and save it.
2619
		$mapping_object = $this->mappings->create_object_map(
2620
			array(
2621
				'wordpress_id'      => $wordpress_id, // wordpress unique id.
2622
				'salesforce_id'     => $salesforce_id, // salesforce unique id. we don't care what kind of object it is at this point.
2623
				'wordpress_object'  => $wordpress_object, // keep track of what kind of wp object this is.
2624
				'last_sync'         => current_time( 'mysql' ),
2625
				'last_sync_action'  => $action,
2626
				'last_sync_status'  => $this->mappings->status_success,
2627
				'last_sync_message' => __( 'Mapping object updated via function: ', 'object-sync-for-salesforce' ) . __FUNCTION__,
2628
			)
2629
		);
2630
2631
		return $mapping_object;
2632
2633
	}
2634
2635
}
2636