Passed
Push — release/2.0.0 ( 0618f8...099c6c )
by Jonathan
03:24 queued 11s
created

Object_Sync_Sf_Admin   F

Complexity

Total Complexity 329

Size/Duplication

Total Lines 2567
Duplicated Lines 0 %

Importance

Changes 10
Bugs 2 Features 0
Metric Value
eloc 1447
c 10
b 2
f 0
dl 0
loc 2567
rs 0.8
wmc 329

45 Methods

Rating   Name   Duplication   Size   Complexity  
A check_wordpress_admin_permissions() 0 14 2
C show_salesforce_user_fields() 0 26 12
A set_action_schedule() 0 20 2
A clear_schedule() 0 7 2
A delete_fieldmap() 0 11 3
A add_actions() 0 52 2
A salesforce_settings_forms() 0 22 3
C delete_object_map() 0 47 12
A plugin_action_links() 0 6 2
F show_admin_page() 0 213 53
C notices() 0 77 11
C prepare_fieldmap_data() 0 51 14
C save_salesforce_user_fields() 0 25 13
A pull_from_salesforce() 0 12 6
B export_json_file() 0 27 6
A create_admin_menu() 0 3 1
B display_select() 0 42 7
F import_json_file() 0 121 30
A __construct() 0 37 2
A admin_scripts_and_styles() 0 26 2
A fields_fieldmaps() 0 2 1
A get_schedule_count() 0 23 2
B push_to_salesforce() 0 24 10
C get_salesforce_object_description() 0 56 17
B status() 0 61 7
A clear_cache() 0 19 4
A display_link() 0 23 3
B display_checkboxes() 0 29 9
A clear_sfwp_cache() 0 7 1
B prepare_object_map_data() 0 38 10
A change_action_schedule() 0 10 1
B fields_scheduling() 0 106 4
A fields_errors() 0 37 2
A initial_action_schedule() 0 24 2
A refresh_mapped_data() 0 22 5
B tabs() 0 34 11
C fields_settings() 0 273 8
A create_object_map() 0 15 1
A sanitize_validate_text() 0 10 3
B fields_log_settings() 0 186 2
A version_options() 0 13 5
A logout() 0 17 4
B display_input_field() 0 49 11
D get_salesforce_object_fields() 0 48 17
A get_wordpress_object_fields() 0 17 4

How to fix   Complexity   

Complex Class

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

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

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

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
250
	/**
251
	 * Create the action hooks to create the admin pages.
252
	 */
253
	public function add_actions() {
254
255
		// settings link.
256
		add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 5 );
257
258
		// CSS and Javascript.
259
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_and_styles' ) );
260
261
		// Settings API forms and notices.
262
		add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
263
		add_action( 'admin_init', array( $this, 'salesforce_settings_forms' ) );
264
		add_action( 'admin_init', array( $this, 'notices' ) );
265
		add_action( 'admin_post_post_fieldmap', array( $this, 'prepare_fieldmap_data' ) );
266
		add_action( 'admin_post_delete_fieldmap', array( $this, 'delete_fieldmap' ) );
267
268
		// Ajax for fieldmap forms.
269
		add_action( 'wp_ajax_get_salesforce_object_description', array( $this, 'get_salesforce_object_description' ), 10, 1 );
270
		add_action( 'wp_ajax_get_salesforce_object_fields', array( $this, 'get_salesforce_object_fields' ), 10, 1 );
271
		add_action( 'wp_ajax_get_wordpress_object_fields', array( $this, 'get_wordpress_object_fields' ), 10, 1 );
272
273
		// Ajax events that can be manually called.
274
		add_action( 'wp_ajax_push_to_salesforce', array( $this, 'push_to_salesforce' ), 10, 3 );
275
		add_action( 'wp_ajax_pull_from_salesforce', array( $this, 'pull_from_salesforce' ), 10, 2 );
276
		add_action( 'wp_ajax_refresh_mapped_data', array( $this, 'refresh_mapped_data' ), 10, 1 );
277
		add_action( 'wp_ajax_clear_sfwp_cache', array( $this, 'clear_sfwp_cache' ) );
278
279
		// we add a Salesforce box on user profiles.
280
		add_action( 'edit_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
281
		add_action( 'show_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
282
283
		// and we can update Salesforce fields on the user profile box.
284
		add_action( 'personal_options_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
285
		add_action( 'edit_user_profile_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
286
287
		// when either field for schedule settings changes.
288
		foreach ( $this->schedulable_classes as $key => $value ) {
289
			// if the user doesn't have any action schedule tasks, let's not leave them empty.
290
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'initial_action_schedule' ), 10, 3 );
291
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'initial_action_schedule' ), 10, 3 );
292
293
			// this is if the user is changing their tasks.
294
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'change_action_schedule' ), 10, 3 );
295
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'change_action_schedule' ), 10, 3 );
296
		}
297
298
		// handle post requests for object maps.
299
		add_action( 'admin_post_delete_object_map', array( $this, 'delete_object_map' ) );
300
		add_action( 'admin_post_post_object_map', array( $this, 'prepare_object_map_data' ) );
301
302
		// import and export plugin data.
303
		add_action( 'admin_post_object_sync_for_salesforce_import', array( $this, 'import_json_file' ) );
304
		add_action( 'admin_post_object_sync_for_salesforce_export', array( $this, 'export_json_file' ) );
305
306
	}
307
308
	/**
309
	 * Display a Settings link on the main Plugins page
310
	 *
311
	 * @param array  $links the array of links for the main plugins page.
312
	 * @param string $file the filename.
313
	 * @return array $links the array of links for the main plugins page
314
	 */
315
	public function plugin_action_links( $links, $file ) {
316
		if ( plugin_basename( $this->file ) === $file ) {
317
			$settings = '<a href="' . get_admin_url() . 'options-general.php?page=' . $this->admin_settings_url_param . '">' . __( 'Settings', 'object-sync-for-salesforce' ) . '</a>';
318
			array_unshift( $links, $settings );
319
		}
320
		return $links;
321
	}
322
323
	/**
324
	 * Admin styles. Load the CSS and JavaScript for the plugin's settings
325
	 */
326
	public function admin_scripts_and_styles() {
327
328
		// Developers might not want to bother with select2 or selectwoo, so we allow that to be changeable.
329
		$select_library = apply_filters( $this->option_prefix . 'select_library', 'selectwoo' );
330
331
		/*
332
		 * example to modify the select library
333
		 * add_filter( 'object_sync_for_salesforce_select_library', 'select_library', 10, 1 );
334
		 * function select_library( $select_library ) {
335
		 * 	$select_library = 'select2';
336
		 *  // this could also be empty; in that case we would just use default browser select
337
		 * 	return $select_library;
338
		 * }
339
		*/
340
341
		$javascript_dependencies = array( 'jquery' );
342
		$css_dependencies        = array();
343
		if ( '' !== $select_library ) {
344
			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 );
345
			$javascript_dependencies[] = $select_library . 'js';
346
			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' );
347
			$css_dependencies[] = $select_library . 'css';
348
		}
349
350
		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 );
351
		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' );
352
	}
353
354
	/**
355
	 * Initial recurring tasks for ActionScheduler
356
	 *
357
	 * @param string $new_schedule the new, unserialized option value.
358
	 * @param string $old_schedule the old option value.
359
	 * @param string $option_name option name.
360
	 * @return string $new_schedule
361
	 */
362
	public function initial_action_schedule( $new_schedule, $old_schedule, $option_name ) {
363
364
		// get the current schedule name from the task, based on pattern in the foreach.
365
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
366
		$schedule_name     = $matches[1];
367
		$action_group_name = $schedule_name . $this->action_group_suffix;
368
369
		// make sure there are no tasks already.
370
		$current_tasks = as_get_scheduled_actions(
371
			array(
372
				'hook'  => $this->schedulable_classes[ $schedule_name ]['initializer'],
373
				'group' => $action_group_name,
374
			),
375
			ARRAY_A
376
		);
377
378
		// exit if there are already tasks; they'll be saved if the option data changed.
379
		if ( ! empty( $current_tasks ) ) {
380
			return $new_schedule;
381
		}
382
383
		$this->set_action_schedule( $schedule_name, $action_group_name );
384
385
		return $new_schedule;
386
387
	}
388
389
	/**
390
	 * Update recurring tasks for ActionScheduler if options change
391
	 *
392
	 * @param string $old_schedule the old option value.
393
	 * @param string $new_schedule the new, unserialized option value.
394
	 * @param string $option_name option name.
395
	 */
396
	public function change_action_schedule( $old_schedule, $new_schedule, $option_name ) {
397
398
		// this method does not run if the option's data is unchanged.
399
400
		// get the current schedule name from the task, based on pattern in the foreach.
401
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
402
		$schedule_name     = $matches[1];
403
		$action_group_name = $schedule_name . $this->action_group_suffix;
404
405
		$this->set_action_schedule( $schedule_name, $action_group_name );
406
407
	}
408
409
	/**
410
	 * Set up recurring tasks for ActionScheduler
411
	 *
412
	 * @param string $schedule_name the name of the schedule.
413
	 * @param string $action_group_name the group's name.
414
	 */
415
	private function set_action_schedule( $schedule_name, $action_group_name ) {
416
		// exit if there is no initializer property on this schedule.
417
		if ( ! isset( $this->schedulable_classes[ $schedule_name ]['initializer'] ) ) {
418
			return;
419
		}
420
421
		// cancel previous task.
422
		$this->queue->cancel(
423
			$this->schedulable_classes[ $schedule_name ]['initializer'],
424
			array(),
425
			$action_group_name
426
		);
427
428
		// create new recurring task for ActionScheduler to check for data to pull from Salesforce.
429
		$this->queue->schedule_recurring(
430
			time(), // plugin seems to expect UTC.
431
			$this->queue->get_frequency( $schedule_name, 'seconds' ),
432
			$this->schedulable_classes[ $schedule_name ]['initializer'],
433
			array(),
434
			$action_group_name
435
		);
436
	}
437
438
	/**
439
	 * Create the WordPress admin options page
440
	 */
441
	public function create_admin_menu() {
442
		$title = __( 'Salesforce', 'object-sync-for-salesforce' );
443
		add_options_page( $title, $title, 'configure_salesforce', $this->admin_settings_url_param, array( $this, 'show_admin_page' ) );
444
	}
445
446
	/**
447
	 * Render the admin pages in WordPress. This also allows other plugins to add tabs to this plugin's settings screen
448
	 */
449
	public function show_admin_page() {
450
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
451
		echo '<div class="wrap">';
452
		echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
453
		$allowed = $this->check_wordpress_admin_permissions();
454
		if ( false === $allowed ) {
455
			return;
456
		}
457
		$tabs = array(
458
			'settings'      => __( 'Settings', 'object-sync-for-salesforce' ),
459
			'authorize'     => __( 'Authorize', 'object-sync-for-salesforce' ),
460
			'fieldmaps'     => __( 'Fieldmaps', 'object-sync-for-salesforce' ),
461
			'schedule'      => __( 'Scheduling', 'object-sync-for-salesforce' ),
462
			'import-export' => __( 'Import &amp; Export', 'object-sync-for-salesforce' ),
463
		); // this creates the tabs for the admin.
464
465
		// optionally make tab(s) for logging and log settings.
466
		$logging_enabled      = get_option( $this->option_prefix . 'enable_logging', false );
467
		$tabs['log_settings'] = __( 'Log Settings', 'object-sync-for-salesforce' );
468
469
		$mapping_errors       = $this->mappings->get_failed_object_maps();
470
		$mapping_errors_total = isset( $mapping_errors['total'] ) ? $mapping_errors['total'] : 0;
471
		if ( 0 < $mapping_errors_total ) {
472
			$tabs['mapping_errors'] = __( 'Mapping Errors', 'object-sync-for-salesforce' );
473
		}
474
475
		// filter for extending the tabs available on the page
476
		// currently it will go into the default switch case for $tab.
477
		$tabs = apply_filters( $this->option_prefix . 'settings_tabs', $tabs );
478
479
		$tab = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
480
		$this->tabs( $tabs, $tab );
481
482
		$consumer_key    = $this->login_credentials['consumer_key'];
483
		$consumer_secret = $this->login_credentials['consumer_secret'];
484
		$callback_url    = $this->login_credentials['callback_url'];
485
486
		if ( true !== $this->salesforce['is_authorized'] ) {
487
			$url     = esc_url( $callback_url );
488
			$anchor  = esc_html__( 'Authorize tab', 'object-sync-for-salesforce' );
489
			$message = sprintf( 'Salesforce needs to be authorized to connect to this website. Use the <a href="%s">%s</a> to connect.', $url, $anchor );
490
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
491
		}
492
493
		if ( 0 === count( $this->mappings->get_fieldmaps() ) ) {
494
			$url     = esc_url( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=fieldmaps' ) );
495
			$anchor  = esc_html__( 'Fieldmaps tab', 'object-sync-for-salesforce' );
496
			$message = sprintf( 'No fieldmaps exist yet. Use the <a href="%s">%s</a> to map WordPress and Salesforce objects to each other.', $url, $anchor );
497
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
498
		}
499
500
		try {
501
			switch ( $tab ) {
502
				case 'authorize':
503
					if ( isset( $get_data['code'] ) ) {
504
						// this string is an oauth token.
505
						$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

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

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

1571
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1572
			$ajax             = true;
1573
		}
1574
1575
		$object_fields = $this->wordpress->get_wordpress_object_fields( $wordpress_object );
1576
1577
		if ( true === $ajax ) {
1578
			$ajax_response = array(
1579
				'fields' => $object_fields,
1580
			);
1581
			wp_send_json_success( $ajax_response );
1582
		} else {
1583
			return $object_fields;
1584
		}
1585
	}
1586
1587
	/**
1588
	 * Manually push the WordPress object to Salesforce
1589
	 * This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $wordpress_id fields
1590
	 *
1591
	 * @param string $wordpress_object is the name of the WordPress object.
1592
	 * @param int    $wordpress_id is the ID of the WordPress record.
1593
	 * @param bool   $force_return Force the method to return json instead of outputting it.
1594
	 */
1595
	public function push_to_salesforce( $wordpress_object = '', $wordpress_id = '', $force_return = false ) {
1596
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1597
		if ( empty( $wordpress_object ) && empty( $wordpress_id ) ) {
1598
			$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

1598
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1599
			$wordpress_id     = isset( $post_data['wordpress_id'] ) ? absint( $post_data['wordpress_id'] ) : '';
1600
		}
1601
1602
		// clarify what that variable is in this context.
1603
		$object_type = $wordpress_object;
1604
1605
		// When objects are already mapped, there is a Salesforce id as well. Otherwise, it's blank.
1606
		$salesforce_id = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( $post_data['salesforce_id'] ) : '';
1607
		if ( '' === $salesforce_id ) {
1608
			$method = 'POST';
1609
		} else {
1610
			$method = 'PUT';
1611
		}
1612
1613
		$result = $this->push->manual_push( $object_type, $wordpress_id, $method );
1614
1615
		if ( false === $force_return && ! empty( $post_data['wordpress_object'] ) && ! empty( $post_data['wordpress_id'] ) ) {
1616
			wp_send_json_success( $result );
1617
		} else {
1618
			return $result;
1619
		}
1620
1621
	}
1622
1623
	/**
1624
	 * Manually pull the Salesforce object into WordPress
1625
	 * This takes either the $_POST array via ajax, or can be directly called with $salesforce_id fields
1626
	 *
1627
	 * @param string $salesforce_id is the ID of the Salesforce record.
1628
	 * @param string $wordpress_object is the name of the WordPress object.
1629
	 */
1630
	public function pull_from_salesforce( $salesforce_id = '', $wordpress_object = '' ) {
1631
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1632
		if ( empty( $wordpress_object ) && empty( $salesforce_id ) ) {
1633
			$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

1633
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1634
			$salesforce_id    = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_id'] ) ) : '';
1635
		}
1636
		$type   = $this->salesforce['sfapi']->get_sobject_type( $salesforce_id );
1637
		$result = $this->pull->manual_pull( $type, $salesforce_id, $wordpress_object ); // we want the wp object to make sure we get the right fieldmap.
1638
		if ( ! empty( $post_data ) ) {
1639
			wp_send_json_success( $result );
1640
		} else {
1641
			return $result;
1642
		}
1643
	}
1644
1645
	/**
1646
	 * Manually pull the Salesforce object into WordPress
1647
	 * This takes an id for a mapping object row
1648
	 *
1649
	 * @param int $mapping_id is the ID of the mapping object record.
1650
	 */
1651
	public function refresh_mapped_data( $mapping_id = '' ) {
1652
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1653
		if ( empty( $mapping_id ) ) {
1654
			$mapping_id = isset( $post_data['mapping_id'] ) ? absint( $post_data['mapping_id'] ) : '';
1655
		}
1656
		$result = $this->mappings->get_all_object_maps(
1657
			array(
1658
				'id' => $mapping_id,
1659
			)
1660
		);
1661
1662
		$object_map = array();
1663
1664
		// result is an array of arrays, not just one array.
1665
		if ( 1 === count( $result ) ) {
1666
			$object_map = $result[0];
1667
		}
1668
1669
		if ( ! empty( $post_data ) ) {
1670
			wp_send_json_success( $object_map );
1671
		} else {
1672
			return $object_map;
1673
		}
1674
	}
1675
1676
	/**
1677
	 * Prepare fieldmap data and redirect after processing
1678
	 * This runs when the create or update forms are submitted
1679
	 * It is public because it depends on an admin hook
1680
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1681
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1682
	 */
1683
	public function prepare_fieldmap_data() {
1684
		$error     = false;
1685
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1686
		$cachekey  = wp_json_encode( $post_data );
1687
		if ( false !== $cachekey ) {
1688
			$cachekey = md5( $cachekey );
1689
		}
1690
1691
		if ( ! isset( $post_data['label'] ) || ! isset( $post_data['salesforce_object'] ) || ! isset( $post_data['wordpress_object'] ) ) {
1692
			$error = true;
1693
		}
1694
		if ( true === $error ) {
1695
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1696
			if ( '' !== $cachekey ) {
1697
				$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

1697
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1698
			}
1699
		} else { // there are no errors
1700
			// send the row to the fieldmap class
1701
			// if it is add or clone, use the create method.
1702
			$method            = esc_attr( $post_data['method'] );
1703
			$salesforce_fields = $this->get_salesforce_object_fields(
1704
				array(
1705
					'salesforce_object' => $post_data['salesforce_object'],
1706
				)
1707
			);
1708
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1709
			if ( 'add' === $method || 'clone' === $method ) {
1710
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1711
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method.
1712
				$id     = esc_attr( $post_data['id'] );
1713
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
1714
			}
1715
			if ( false === $result ) { // if the database didn't save, it's still an error.
1716
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1717
				if ( '' !== $cachekey ) {
1718
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1719
				}
1720
			} else {
1721
				// if the user has saved a fieldmap, clear the currently running query value if there is one.
1722
				if ( '' !== get_option( $this->option_prefix . 'currently_pulling_query_' . $post_data['salesforce_object'], '' ) ) {
1723
					$this->pull->clear_current_type_query( $post_data['salesforce_object'] );
1724
				}
1725
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1726
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1727
				}
1728
				// then send the user to the list of fieldmaps.
1729
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1730
			}
1731
		}
1732
		wp_safe_redirect( $url );
1733
		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...
1734
	}
1735
1736
	/**
1737
	 * Delete fieldmap data and redirect after processing
1738
	 * This runs when the delete link is clicked, after the user confirms
1739
	 * It is public because it depends on an admin hook
1740
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1741
	 */
1742
	public function delete_fieldmap() {
1743
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1744
		if ( $post_data['id'] ) {
1745
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1746
			if ( true === $result ) {
1747
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1748
			} else {
1749
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1750
			}
1751
			wp_safe_redirect( $url );
1752
			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...
1753
		}
1754
	}
1755
1756
	/**
1757
	 * Prepare object data and redirect after processing
1758
	 * This runs when the update form is submitted
1759
	 * It is public because it depends on an admin hook
1760
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1761
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1762
	 */
1763
	public function prepare_object_map_data() {
1764
		$error     = false;
1765
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1766
		$cachekey  = wp_json_encode( $post_data );
1767
		if ( false !== $cachekey ) {
1768
			$cachekey = md5( $cachekey );
1769
		}
1770
1771
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1772
			$error = true;
1773
		}
1774
		if ( true === $error ) {
1775
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1776
			if ( '' !== $cachekey ) {
1777
				$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

1777
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1778
			}
1779
		} else { // there are no errors
1780
			// send the row to the object map class.
1781
			$method = esc_attr( $post_data['method'] );
1782
			if ( 'edit' === $method ) { // if it is edit, use the update method.
1783
				$id     = esc_attr( $post_data['id'] );
1784
				$result = $this->mappings->update_object_map( $post_data, $id );
1785
			}
1786
			if ( false === $result ) { // if the database didn't save, it's still an error.
1787
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1788
				if ( '' !== $cachekey ) {
1789
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1790
				}
1791
			} else {
1792
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1793
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1794
				}
1795
				// then send the user to the success redirect url.
1796
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1797
			}
1798
		}
1799
		wp_safe_redirect( $url );
1800
		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...
1801
	}
1802
1803
	/**
1804
	 * Delete object map data and redirect after processing
1805
	 * This runs when the delete link is clicked on an error row, after the user confirms
1806
	 * It is public because it depends on an admin hook
1807
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1808
	 */
1809
	public function delete_object_map() {
1810
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1811
		if ( isset( $post_data['id'] ) ) {
1812
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1813
			if ( true === $result ) {
1814
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1815
			} else {
1816
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1817
			}
1818
			wp_safe_redirect( $url );
1819
			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...
1820
		} elseif ( $post_data['delete'] ) {
1821
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1822
			$cachekey  = wp_json_encode( $post_data );
1823
			if ( false !== $cachekey ) {
1824
				$cachekey = md5( $cachekey );
1825
			}
1826
			$error = false;
1827
			if ( ! isset( $post_data['delete'] ) ) {
1828
				$error = true;
1829
			}
1830
			if ( true === $error ) {
1831
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1832
				if ( '' !== $cachekey ) {
1833
					$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

1833
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1834
				}
1835
			} else { // there are no errors.
1836
				$result = $this->mappings->delete_object_map( array_keys( $post_data['delete'] ) );
1837
				if ( true === $result ) {
1838
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1839
				}
1840
1841
				if ( false === $result ) { // if the database didn't save, it's still an error.
1842
					$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1843
					if ( '' !== $cachekey ) {
1844
						$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
1845
					}
1846
				} else {
1847
					if ( isset( $post_data['mapping_error_transient'] ) ) { // there was previously an error saved. can delete it now.
1848
						$this->sfwp_transients->delete( esc_attr( $post_data['mapping_error_transient'] ) );
1849
					}
1850
					// then send the user to the list of fieldmaps.
1851
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1852
				}
1853
			}
1854
			wp_safe_redirect( $url );
1855
			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...
1856
		}
1857
	}
1858
1859
	/**
1860
	 * Import a json file and use it for plugin data
1861
	 */
1862
	public function import_json_file() {
1863
1864
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_import'], 'object_sync_for_salesforce_nonce_import' ) ) {
1865
			return;
1866
		}
1867
		if ( ! current_user_can( 'manage_options' ) ) {
1868
			return;
1869
		}
1870
		$path      = $_FILES['import_file']['name'];
1871
		$extension = pathinfo( $path, PATHINFO_EXTENSION );
1872
		if ( 'json' !== $extension ) {
1873
			wp_die( esc_html__( 'Please upload a valid .json file', 'object-sync-for-salesforce' ) );
1874
		}
1875
1876
		$import_file = $_FILES['import_file']['tmp_name'];
1877
		if ( empty( $import_file ) ) {
1878
			wp_die( esc_html__( 'Please upload a file to import', 'object-sync-for-salesforce' ) );
1879
		}
1880
1881
		// Retrieve the data from the file and convert the json object to an array.
1882
		$data = (array) json_decode( file_get_contents( $import_file ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
1883
1884
		$overwrite = isset( $_POST['overwrite'] ) ? esc_attr( $_POST['overwrite'] ) : '';
1885
		if ( true === filter_var( $overwrite, FILTER_VALIDATE_BOOLEAN ) ) {
1886
			if ( isset( $data['fieldmaps'] ) ) {
1887
				$fieldmaps = $this->mappings->get_fieldmaps();
1888
				foreach ( $fieldmaps as $fieldmap ) {
1889
					$id     = $fieldmap['id'];
1890
					$delete = $this->mappings->delete_fieldmap( $id );
1891
				}
1892
			}
1893
			if ( isset( $data['object_maps'] ) ) {
1894
				$object_maps = $this->mappings->get_all_object_maps();
1895
				foreach ( $object_maps as $object_map ) {
1896
					$id     = $object_map['id'];
1897
					$delete = $this->mappings->delete_object_map( $id );
1898
				}
1899
			}
1900
			if ( isset( $data['plugin_settings'] ) ) {
1901
				foreach ( $data['plugin_settings'] as $key => $value ) {
1902
					delete_option( $value['option_name'] );
1903
				}
1904
			}
1905
		}
1906
1907
		$success = true;
1908
1909
		if ( isset( $data['fieldmaps'] ) ) {
1910
			foreach ( $data['fieldmaps'] as $fieldmap ) {
1911
				unset( $fieldmap['id'] );
1912
				$create = $this->mappings->create_fieldmap( $fieldmap );
1913
				if ( false === $create ) {
1914
					$success = false;
1915
				}
1916
			}
1917
		}
1918
1919
		if ( isset( $data['object_maps'] ) ) {
1920
			$successful_object_maps = array();
1921
			$error_object_maps      = array();
1922
			foreach ( $data['object_maps'] as $object_map ) {
1923
				unset( $object_map['id'] );
1924
				if ( isset( $object_map['object_type'] ) ) {
1925
					$sf_sync_trigger = $this->mappings->sync_sf_create;
1926
					$create          = $this->pull->salesforce_pull_process_records( $object_map['object_type'], $object_map['salesforce_id'], $sf_sync_trigger );
1927
				} else {
1928
					$create = $this->mappings->create_object_map( $object_map );
1929
				}
1930
				if ( false === $create ) {
1931
					$error_object_maps[] = $object_map;
1932
				} else {
1933
					$successful_object_maps[] = $create;
1934
				}
1935
			}
1936
		}
1937
1938
		if ( isset( $data['plugin_settings'] ) ) {
1939
			foreach ( $data['plugin_settings'] as $key => $value ) {
1940
				update_option( $value['option_name'], maybe_unserialize( $value['option_value'] ), $value['autoload'] );
1941
			}
1942
		}
1943
1944
		$status = 'error';
1945
		if ( isset( $this->logging ) ) {
1946
			$logging = $this->logging;
1947
		} elseif ( class_exists( 'Object_Sync_Sf_Logging' ) ) {
1948
			$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

1948
			$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...
1949
		}
1950
1951
		$body = sprintf( esc_html__( 'These are the import items that were not able to save: ', 'object-sync-for-salesforce' ) . '<ul>' );
1952
		foreach ( $error_object_maps as $mapping_object ) {
1953
			$body .= sprintf(
1954
				// translators: placeholders are: 1) the mapping object row ID, 2) the ID of the Salesforce object, 3) the WordPress object type.
1955
				'<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>',
1956
				isset( $mapping_object['id'] ) ? absint( $mapping_object['id'] ) : '',
1957
				esc_attr( $mapping_object['salesforce_id'] ),
1958
				esc_attr( $mapping_object['wordpress_object'] )
1959
			);
1960
		}
1961
		$body .= sprintf( '</ul>' );
1962
1963
		$logging->setup(
1964
			sprintf(
1965
				// translators: %1$s is the log status.
1966
				esc_html__( '%1$s on import: some of the rows were unable to save. Read this post for details.', 'object-sync-for-salesforce' ),
1967
				ucfirst( esc_attr( $status ) )
1968
			),
1969
			$body,
1970
			0,
1971
			0,
1972
			$status
1973
		);
1974
1975
		if ( empty( $error_object_maps ) && ! empty( $successful_object_maps ) ) {
1976
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=true' ) );
1977
			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...
1978
		} elseif ( ! empty( $error_object_maps ) && ! empty( $successful_object_maps ) ) {
1979
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=partial' ) );
1980
		} else {
1981
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=false' ) );
1982
			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...
1983
		}
1984
1985
	}
1986
1987
	/**
1988
	 * Create a json file for exporting
1989
	 */
1990
	public function export_json_file() {
1991
1992
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_export'], 'object_sync_for_salesforce_nonce_export' ) ) {
1993
			return;
1994
		}
1995
		if ( ! current_user_can( 'manage_options' ) ) {
1996
			return;
1997
		}
1998
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1999
		$export    = array();
2000
		if ( in_array( 'fieldmaps', $post_data['export'], true ) ) {
2001
			$export['fieldmaps'] = $this->mappings->get_fieldmaps();
2002
		}
2003
		if ( in_array( 'object_maps', $post_data['export'], true ) ) {
2004
			$export['object_maps'] = $this->mappings->get_all_object_maps();
2005
		}
2006
		if ( in_array( 'plugin_settings', $post_data['export'], true ) ) {
2007
			$wpdb                      = $this->wpdb;
2008
			$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 );
2009
			$export['plugin_settings'] = $export_results;
2010
		}
2011
		nocache_headers();
2012
		header( 'Content-Type: application/json; charset=utf-8' );
2013
		header( 'Content-Disposition: attachment; filename=object-sync-for-salesforce-data-export-' . gmdate( 'm-d-Y' ) . '.json' );
2014
		header( 'Expires: 0' );
2015
		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

2015
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
2016
		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...
2017
	}
2018
2019
	/**
2020
	 * Default display for <input> fields
2021
	 *
2022
	 * @param array $args is the arguments to create the field.
2023
	 */
2024
	public function display_input_field( $args ) {
2025
		$type    = $args['type'];
2026
		$id      = $args['label_for'];
2027
		$name    = $args['name'];
2028
		$desc    = $args['desc'];
2029
		$checked = '';
2030
2031
		$class = 'regular-text';
2032
2033
		if ( 'checkbox' === $type ) {
2034
			$class = 'checkbox';
2035
		}
2036
2037
		if ( isset( $args['class'] ) ) {
2038
			$class = $args['class'];
2039
		}
2040
2041
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
2042
			$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

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