Completed
Push — master ( 78ff96...d27003 )
by Jonathan
18s queued 13s
created

Object_Sync_Sf_Admin::display_checkboxes()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 29
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 25
nc 9
nop 1
dl 0
loc 29
rs 8.0555
c 0
b 0
f 0
1
<?php
2
/**
3
 * Create default WordPress admin functionality to configure the plugin.
4
 *
5
 * @class   Object_Sync_Sf_Admin
6
 * @package Object_Sync_Salesforce
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Object_Sync_Sf_Admin class.
13
 */
14
class Object_Sync_Sf_Admin {
15
16
	/**
17
	 * Current version of the plugin
18
	 *
19
	 * @var string
20
	 */
21
	public $version;
22
23
	/**
24
	 * The main plugin file
25
	 *
26
	 * @var string
27
	 */
28
	public $file;
29
30
	/**
31
	 * Global object of `$wpdb`, the WordPress database
32
	 *
33
	 * @var object
34
	 */
35
	public $wpdb;
36
37
	/**
38
	 * The plugin's slug so we can include it when necessary
39
	 *
40
	 * @var string
41
	 */
42
	public $slug;
43
44
	/**
45
	 * The plugin's prefix when saving options to the database
46
	 *
47
	 * @var string
48
	 */
49
	public $option_prefix;
50
51
	/**
52
	 * Suffix for group name in ActionScheduler
53
	 *
54
	 * @var string
55
	 */
56
	public $action_group_suffix;
57
58
	/**
59
	 * Login credentials for the Salesforce API; comes from wp-config or from the plugin settings
60
	 *
61
	 * @var array
62
	 */
63
	public $login_credentials;
64
65
	/**
66
	 * Array of what classes in the plugin can be scheduled to occur with `wp_cron` events
67
	 *
68
	 * @var array
69
	 */
70
	public $schedulable_classes;
71
72
	/**
73
	 * Object_Sync_Sf_Queue class
74
	 *
75
	 * @var object
76
	 */
77
	public $queue;
78
79
	/**
80
	 * Object_Sync_Sf_Logging class
81
	 *
82
	 * @var object
83
	 */
84
	public $logging;
85
86
	/**
87
	 * Object_Sync_Sf_Mapping class
88
	 *
89
	 * @var object
90
	 */
91
	public $mappings;
92
93
	/**
94
	 * Object_Sync_Sf_WordPress class
95
	 *
96
	 * @var object
97
	 */
98
	public $wordpress;
99
100
	/**
101
	 * Object_Sync_Sf_Salesforce class
102
	 * This contains Salesforce API methods
103
	 *
104
	 * @var array
105
	 */
106
	public $salesforce;
107
108
	/**
109
	 * Object_Sync_Sf_Salesforce_Push class
110
	 *
111
	 * @var object
112
	 */
113
	public $push;
114
115
	/**
116
	 * Object_Sync_Sf_Salesforce_Pull class
117
	 *
118
	 * @var object
119
	 */
120
	public $pull;
121
122
	/**
123
	 * Object_Sync_Sf_WordPress_Transient class
124
	 *
125
	 * @var object
126
	 */
127
	private $sfwp_transients;
128
129
	/**
130
	 * URL fragment for the plugin's settings page
131
	 *
132
	 * @var string
133
	 */
134
	private $admin_settings_url_param;
135
136
	/**
137
	 * Data for admin notices
138
	 *
139
	 * @var array
140
	 */
141
	public $notices_data;
142
143
	/**
144
	 * Salesforce access token
145
	 *
146
	 * @var string
147
	 */
148
	private $access_token;
149
150
	/**
151
	 * Salesforce instance URL
152
	 *
153
	 * @var string
154
	 */
155
	private $instance_url;
156
157
	/**
158
	 * Salesforce refresh token
159
	 *
160
	 * @var string
161
	 */
162
	private $refresh_token;
163
164
	/**
165
	 * Default path for the Salesforce authorize URL
166
	 *
167
	 * @var string
168
	 */
169
	public $default_authorize_url_path;
170
171
	/**
172
	 * Default path for the Salesforce token URL
173
	 *
174
	 * @var string
175
	 */
176
	public $default_token_url_path;
177
178
	/**
179
	 * What version of the Salesforce API should be the default on the settings screen.
180
	 * Users can edit what version is used, but they won't see a correct list of all their available versions until WordPress has
181
	 * been authenticated with Salesforce.
182
	 *
183
	 * @var string
184
	 * @deprecated as of 2.2.0; will be removed in version 3.0. This property will stay until 3.0.0 because it is a public value and it could be accessed by other code.
185
	 */
186
	public $default_api_version;
187
188
	/**
189
	 * Default max number of pull records. Users can edit this.
190
	 *
191
	 * @var int
192
	 */
193
	public $default_pull_limit;
194
195
	/**
196
	 * Default throttle for how often to pull from Salesforce. Users can edit this.
197
	 *
198
	 * @var int
199
	 */
200
	public $default_pull_throttle;
201
202
	/**
203
	 * Default for whether to limit to triggerable items. Users can edit this.
204
	 *
205
	 * @var bool
206
	 */
207
	public $default_triggerable;
208
209
	/**
210
	 * Default for whether to limit to items that can be updated. Users can edit this.
211
	 *
212
	 * @var bool
213
	 */
214
	public $default_updateable;
215
216
	/**
217
	 * Constructor for admin class
218
	 */
219
	public function __construct() {
220
		$this->version             = object_sync_for_salesforce()->version;
221
		$this->file                = object_sync_for_salesforce()->file;
222
		$this->wpdb                = object_sync_for_salesforce()->wpdb;
223
		$this->slug                = object_sync_for_salesforce()->slug;
224
		$this->option_prefix       = object_sync_for_salesforce()->option_prefix;
225
		$this->action_group_suffix = object_sync_for_salesforce()->action_group_suffix;
226
227
		$this->login_credentials   = object_sync_for_salesforce()->login_credentials;
228
		$this->wordpress           = object_sync_for_salesforce()->wordpress;
229
		$this->salesforce          = object_sync_for_salesforce()->salesforce;
230
		$this->mappings            = object_sync_for_salesforce()->mappings;
231
		$this->push                = object_sync_for_salesforce()->push;
232
		$this->pull                = object_sync_for_salesforce()->pull;
233
		$this->logging             = object_sync_for_salesforce()->logging;
234
		$this->schedulable_classes = object_sync_for_salesforce()->schedulable_classes;
235
		$this->queue               = object_sync_for_salesforce()->queue;
236
237
		// set the Salesforce API version.
238
		// as of version 2.2.0, this is set by the plugin and is not configurable in the interface.
239
		// this class variable will be removed in 3.0.0.
240
		$this->default_api_version = $this->login_credentials['rest_api_version'];
0 ignored issues
show
Deprecated Code introduced by
The property Object_Sync_Sf_Admin::$default_api_version has been deprecated: as of 2.2.0; will be removed in version 3.0. This property will stay until 3.0.0 because it is a public value and it could be accessed by other code. ( Ignorable by Annotation )

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

240
		/** @scrutinizer ignore-deprecated */ $this->default_api_version = $this->login_credentials['rest_api_version'];

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

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

Loading history...
241
242
		$this->sfwp_transients          = object_sync_for_salesforce()->wordpress->sfwp_transients;
243
		$this->admin_settings_url_param = 'object-sync-salesforce-admin';
244
		$this->notices_data             = $this->notices_data();
245
246
		// default authorize url path.
247
		$this->default_authorize_url_path = '/services/oauth2/authorize';
248
		// default token url path.
249
		$this->default_token_url_path = '/services/oauth2/token';
250
		// default pull record limit.
251
		$this->default_pull_limit = 25;
252
		// default pull throttle for avoiding going over api limits.
253
		$this->default_pull_throttle = 5;
254
		// default setting for triggerable items.
255
		$this->default_triggerable = true;
256
		// default setting for updateable items.
257
		$this->default_updateable = true;
258
259
		$this->add_actions();
260
	}
261
262
	/**
263
	 * Create the action hooks to create the admin pages.
264
	 */
265
	public function add_actions() {
266
267
		// settings link.
268
		add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 5 );
269
270
		// CSS and Javascript.
271
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_and_styles' ) );
272
273
		// Settings API forms and notices.
274
		add_action( 'admin_menu', array( $this, 'create_admin_menu' ) );
275
		add_action( 'admin_init', array( $this, 'salesforce_settings_forms' ) );
276
		add_action( 'admin_init', array( $this, 'display_notices' ) );
277
		add_action( 'admin_post_post_fieldmap', array( $this, 'prepare_fieldmap_data' ) );
278
		add_action( 'admin_post_delete_fieldmap', array( $this, 'delete_fieldmap' ) );
279
280
		// Ajax for fieldmap forms.
281
		add_action( 'wp_ajax_get_salesforce_object_description', array( $this, 'get_salesforce_object_description' ), 10, 1 );
282
		add_action( 'wp_ajax_get_salesforce_object_fields', array( $this, 'get_salesforce_object_fields' ), 10, 1 );
283
		add_action( 'wp_ajax_get_wordpress_object_fields', array( $this, 'get_wordpress_object_fields' ), 10, 1 );
284
285
		// Ajax events that can be manually called.
286
		add_action( 'wp_ajax_push_to_salesforce', array( $this, 'push_to_salesforce' ), 10, 3 );
287
		add_action( 'wp_ajax_pull_from_salesforce', array( $this, 'pull_from_salesforce' ), 10, 2 );
288
		add_action( 'wp_ajax_refresh_mapped_data', array( $this, 'refresh_mapped_data' ), 10, 1 );
289
		add_action( 'wp_ajax_clear_sfwp_cache', array( $this, 'clear_sfwp_cache' ) );
290
		add_action( 'wp_ajax_delete_salesforce_api_version', array( $this, 'delete_salesforce_api_version' ) );
291
292
		// we add a Salesforce box on user profiles.
293
		add_action( 'edit_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
294
		add_action( 'show_user_profile', array( $this, 'show_salesforce_user_fields' ), 10, 1 );
295
296
		// and we can update Salesforce fields on the user profile box.
297
		add_action( 'personal_options_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
298
		add_action( 'edit_user_profile_update', array( $this, 'save_salesforce_user_fields' ), 10, 1 );
299
300
		// when either field for schedule settings changes.
301
		foreach ( $this->schedulable_classes as $key => $value ) {
302
			// if the user doesn't have any action schedule tasks, let's not leave them empty.
303
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'initial_action_schedule' ), 10, 3 );
304
			add_filter( 'pre_update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'initial_action_schedule' ), 10, 3 );
305
306
			// this is if the user is changing their tasks.
307
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_number', array( $this, 'change_action_schedule' ), 10, 3 );
308
			add_filter( 'update_option_' . $this->option_prefix . $key . '_schedule_unit', array( $this, 'change_action_schedule' ), 10, 3 );
309
		}
310
311
		// when ActionScheduler runs its migration, resave the schedule options.
312
		add_action( 'action_scheduler/migration_complete', array( $this, 'resave_action_schedules' ) );
313
314
		// handle post requests for object maps.
315
		add_action( 'admin_post_delete_object_map', array( $this, 'delete_object_map' ) );
316
		add_action( 'admin_post_post_object_map', array( $this, 'prepare_object_map_data' ) );
317
318
		// import and export plugin data.
319
		add_action( 'admin_post_object_sync_for_salesforce_import', array( $this, 'import_json_file' ) );
320
		add_action( 'admin_post_object_sync_for_salesforce_export', array( $this, 'export_json_file' ) );
321
322
	}
323
324
	/**
325
	 * Display a Settings link on the main Plugins page
326
	 *
327
	 * @param array  $links the array of links for the main plugins page.
328
	 * @param string $file the filename.
329
	 * @return array $links the array of links for the main plugins page
330
	 */
331
	public function plugin_action_links( $links, $file ) {
332
		if ( plugin_basename( $this->file ) === $file ) {
333
			$settings = '<a href="' . get_admin_url() . 'options-general.php?page=' . $this->admin_settings_url_param . '">' . __( 'Settings', 'object-sync-for-salesforce' ) . '</a>';
334
			array_unshift( $links, $settings );
335
		}
336
		return $links;
337
	}
338
339
	/**
340
	 * Admin styles. Load the CSS and JavaScript for the plugin's settings
341
	 */
342
	public function admin_scripts_and_styles() {
343
344
		// Developers might not want to bother with select2 or selectwoo, so we allow that to be changeable.
345
		$select_library = apply_filters( $this->option_prefix . 'select_library', 'selectwoo' );
346
347
		/*
348
		 * example to modify the select library
349
		 * add_filter( 'object_sync_for_salesforce_select_library', 'select_library', 10, 1 );
350
		 * function select_library( $select_library ) {
351
		 * 	$select_library = 'select2';
352
		 *  // this could also be empty; in that case we would just use default browser select
353
		 * 	return $select_library;
354
		 * }
355
		*/
356
357
		$javascript_dependencies = array( 'jquery' );
358
		$css_dependencies        = array();
359
		if ( '' !== $select_library ) {
360
			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 );
361
			$javascript_dependencies[] = $select_library . 'js';
362
			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' );
363
			$css_dependencies[] = $select_library . 'css';
364
		}
365
366
		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 );
367
		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' );
368
	}
369
370
	/**
371
	 * Initial recurring tasks for ActionScheduler
372
	 *
373
	 * @param string $new_schedule the new, unserialized option value.
374
	 * @param string $old_schedule the old option value.
375
	 * @param string $option_name option name.
376
	 * @return string $new_schedule
377
	 */
378
	public function initial_action_schedule( $new_schedule, $old_schedule, $option_name ) {
379
380
		// get the current schedule name from the task, based on pattern in the foreach.
381
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
382
		$schedule_name     = $matches[1];
383
		$action_group_name = $schedule_name . $this->action_group_suffix;
384
385
		// make sure there are no tasks already.
386
		$current_tasks = as_get_scheduled_actions(
387
			array(
388
				'hook'  => $this->schedulable_classes[ $schedule_name ]['initializer'],
389
				'group' => $action_group_name,
390
			),
391
			ARRAY_A
392
		);
393
394
		// exit if there are already tasks; they'll be saved if the option data changed.
395
		if ( ! empty( $current_tasks ) ) {
396
			return $new_schedule;
397
		}
398
399
		$this->set_action_schedule( $schedule_name, $action_group_name );
400
401
		return $new_schedule;
402
403
	}
404
405
	/**
406
	 * Update recurring tasks for ActionScheduler if options change
407
	 *
408
	 * @param string $old_schedule the old option value.
409
	 * @param string $new_schedule the new, unserialized option value.
410
	 * @param string $option_name option name.
411
	 */
412
	public function change_action_schedule( $old_schedule, $new_schedule, $option_name ) {
413
414
		// this method does not run if the option's data is unchanged.
415
416
		// get the current schedule name from the task, based on pattern in the foreach.
417
		preg_match( '/' . $this->option_prefix . '(.*)_schedule/', $option_name, $matches );
418
		$schedule_name     = $matches[1];
419
		$action_group_name = $schedule_name . $this->action_group_suffix;
420
421
		$this->set_action_schedule( $schedule_name, $action_group_name );
422
423
	}
424
425
	/**
426
	 * Set up recurring tasks for ActionScheduler
427
	 *
428
	 * @param string $schedule_name the name of the schedule.
429
	 * @param string $action_group_name the group's name.
430
	 */
431
	private function set_action_schedule( $schedule_name, $action_group_name ) {
432
		// exit if there is no initializer property on this schedule.
433
		if ( ! isset( $this->schedulable_classes[ $schedule_name ]['initializer'] ) ) {
434
			return;
435
		}
436
437
		// cancel previous task.
438
		$this->queue->cancel(
439
			$this->schedulable_classes[ $schedule_name ]['initializer'],
440
			array(),
441
			$action_group_name
442
		);
443
444
		// create new recurring task for ActionScheduler to check for data to pull from Salesforce.
445
		$this->queue->schedule_recurring(
446
			time(), // plugin seems to expect UTC.
447
			$this->queue->get_frequency( $schedule_name, 'seconds' ),
448
			$this->schedulable_classes[ $schedule_name ]['initializer'],
449
			array(),
450
			$action_group_name
451
		);
452
	}
453
454
	/**
455
	 * When it finishes its migration, resave the scheduled tasks for ActionScheduler.
456
	 */
457
	public function resave_action_schedules() {
458
		// for each schedulable action, go ahead and resave it.
459
		$schedules_updated  = array();
460
		$schedules_restored = array();
461
		foreach ( $this->schedulable_classes as $key => $value ) {
462
			// make sure it has an initializer property; this is used on recurring tasks.
463
			if ( isset( $value['initializer'] ) ) {
464
				// toggle the schedule number setting.
465
				$schedule_option_name  = $this->option_prefix . $key . '_schedule_number';
466
				$previous_option_value = get_option( $schedule_option_name, 0 );
467
				$previous_option_value = filter_var( $previous_option_value, FILTER_SANITIZE_NUMBER_INT );
468
				$new_option_value      = $previous_option_value + 1;
469
				$schedule_updated      = update_option( $schedule_option_name, $new_option_value );
470
				if ( true === $schedule_updated ) {
471
					$schedules_updated[] = $key;
472
					$schedule_restored   = update_option( $schedule_option_name, $previous_option_value );
473
					if ( true === $schedule_restored ) {
474
						$schedules_restored[] = $key;
475
					}
476
				}
477
			}
478
		}
479
480
		// create a log entry from the updated scheduled tasks.
481
		if ( ! empty( $schedules_updated ) || ! empty( $schedules_restored ) ) {
482
			$status = 'success';
483
		} else {
484
			$status = 'error';
485
		}
486
487
		$body = sprintf( esc_html__( 'These are the scheduled tasks that were updated: ', 'object-sync-for-salesforce' ) . '<ul>' );
488
		foreach ( $schedules_updated as $schedule_updated ) {
489
			$body .= sprintf(
490
				// translators: placeholders are: 1) the schedule name.
491
				'<li>' . esc_html__( 'Schedule name: %1$s.', 'object-sync-for-salesforce' ) . '</li>',
492
				esc_attr( $schedule_updated )
493
			);
494
		}
495
		$body .= '</ul>';
496
497
		$body .= sprintf( esc_html__( 'These are the scheduled tasks that have the same frequency as they had pre-migration: ', 'object-sync-for-salesforce' ) . '<ul>' );
498
		foreach ( $schedules_restored as $schedule_restored ) {
499
			$body .= sprintf(
500
				// translators: placeholders are: 1) the schedule name.
501
				'<li>' . esc_html__( 'Schedule name: %1$s.', 'object-sync-for-salesforce' ) . '</li>',
502
				esc_attr( $schedule_restored )
503
			);
504
		}
505
		$body .= '</ul>';
506
507
		$body .= sprintf( esc_html__( 'If any tasks were not updated, or were not able to keep the same frequency they had before, go to the Scheduling tab to update them.', 'object-sync-for-salesforce' ) );
508
		$body .= sprintf(
509
			// translators: %1$s is the schedule settings URL.
510
			wp_kses_post( 'If any tasks were not updated, or were not able to keep the same frequency they had before, go to the <a href="' . admin_url( 'options-general.php?page=object-sync-salesforce-admin&tab=schedule' ) . '">%1$s</a> tab to update them.', 'object-sync-for-salesforce' ),
0 ignored issues
show
Unused Code introduced by
The call to wp_kses_post() has too many arguments starting with 'object-sync-for-salesforce'. ( Ignorable by Annotation )

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

510
			/** @scrutinizer ignore-call */ 
511
   wp_kses_post( 'If any tasks were not updated, or were not able to keep the same frequency they had before, go to the <a href="' . admin_url( 'options-general.php?page=object-sync-salesforce-admin&tab=schedule' ) . '">%1$s</a> tab to update them.', 'object-sync-for-salesforce' ),

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...
511
			esc_html__( 'Scheduling', 'object-sync-for-salesforce' )
512
		);
513
514
		$this->logging->setup(
515
			sprintf(
516
				// translators: %1$s is the log status, %2$s is the name of a WordPress object. %3$s is the id of that object.
517
				esc_html__( '%1$s ActionScheduler: the ActionScheduler library has completed its migration. See the log entry content for status on each recurring task.', 'object-sync-for-salesforce' ),
518
				ucfirst( esc_attr( $status ) )
519
			),
520
			$body,
521
			0,
522
			0,
523
			$status
524
		);
525
	}
526
527
	/**
528
	 * Create the WordPress admin options page
529
	 */
530
	public function create_admin_menu() {
531
		$title = __( 'Salesforce', 'object-sync-for-salesforce' );
532
		add_options_page( $title, $title, 'configure_salesforce', $this->admin_settings_url_param, array( $this, 'show_admin_page' ) );
533
	}
534
535
	/**
536
	 * Render the admin pages in WordPress. This also allows other plugins to add tabs to this plugin's settings screen
537
	 */
538
	public function show_admin_page() {
539
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
540
		echo '<div class="wrap">';
541
		echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
542
		$allowed = $this->check_wordpress_admin_permissions();
543
		if ( false === $allowed ) {
544
			return;
545
		}
546
		$tabs = array(
547
			'settings'      => __( 'Settings', 'object-sync-for-salesforce' ),
548
			'authorize'     => __( 'Authorize', 'object-sync-for-salesforce' ),
549
			'fieldmaps'     => __( 'Fieldmaps', 'object-sync-for-salesforce' ),
550
			'schedule'      => __( 'Scheduling', 'object-sync-for-salesforce' ),
551
			'import-export' => __( 'Import &amp; Export', 'object-sync-for-salesforce' ),
552
		); // this creates the tabs for the admin.
553
554
		// optionally make tab(s) for logging and log settings.
555
		$logging_enabled      = get_option( $this->option_prefix . 'enable_logging', false );
556
		$tabs['log_settings'] = __( 'Log Settings', 'object-sync-for-salesforce' );
557
558
		$mapping_errors       = $this->mappings->get_failed_object_maps();
559
		$mapping_errors_total = isset( $mapping_errors['total'] ) ? $mapping_errors['total'] : 0;
560
		if ( 0 < $mapping_errors_total ) {
561
			$tabs['mapping_errors'] = __( 'Mapping Errors', 'object-sync-for-salesforce' );
562
		}
563
564
		// filter for extending the tabs available on the page
565
		// currently it will go into the default switch case for $tab.
566
		$tabs = apply_filters( $this->option_prefix . 'settings_tabs', $tabs );
567
568
		$tab = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
569
		$this->tabs( $tabs, $tab );
570
571
		$consumer_key    = $this->login_credentials['consumer_key'];
572
		$consumer_secret = $this->login_credentials['consumer_secret'];
573
		$callback_url    = $this->login_credentials['callback_url'];
574
575
		if ( true !== $this->salesforce['is_authorized'] ) {
576
			$url     = esc_url( $callback_url );
577
			$anchor  = esc_html__( 'Authorize tab', 'object-sync-for-salesforce' );
578
			$message = sprintf( 'Salesforce needs to be authorized to connect to this website. Use the <a href="%s">%s</a> to connect.', $url, $anchor );
579
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
580
		}
581
582
		if ( 0 === count( $this->mappings->get_fieldmaps() ) ) {
583
			$url     = esc_url( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=fieldmaps' ) );
584
			$anchor  = esc_html__( 'Fieldmaps tab', 'object-sync-for-salesforce' );
585
			$message = sprintf( 'No fieldmaps exist yet. Use the <a href="%s">%s</a> to map WordPress and Salesforce objects to each other.', $url, $anchor );
586
			require plugin_dir_path( $this->file ) . '/templates/admin/error.php';
587
		}
588
589
		try {
590
			switch ( $tab ) {
591
				case 'authorize':
592
					if ( isset( $get_data['code'] ) ) {
593
						// this string is an oauth token.
594
						$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

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

1638
			$salesforce_object = isset( $post_data['salesforce_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['salesforce_object'] ) ) : '';
Loading history...
1639
			$ajax              = true;
1640
			// here, we should respect the decision of whether to show the API name or the label.
1641
			$display_value = get_option( $this->option_prefix . 'salesforce_field_display_value', 'field_label' );
1642
			if ( 'api_name' === $display_value ) {
1643
				$visible_label_field = 'name';
1644
			} else {
1645
				$visible_label_field = 'label';
1646
			}
1647
			$attributes = array( 'name', $visible_label_field );
1648
		} else {
1649
			$salesforce_object = isset( $data['salesforce_object'] ) ? sanitize_text_field( wp_unslash( $data['salesforce_object'] ) ) : '';
1650
		}
1651
		$object_fields = array();
1652
		if ( ! empty( $salesforce_object ) ) {
1653
			$object               = $this->salesforce['sfapi']->object_describe( esc_attr( $salesforce_object ) );
1654
			$object_fields        = array();
1655
			$type                 = isset( $data['type'] ) ? esc_attr( $data['type'] ) : '';
1656
			$include_record_types = isset( $data['include_record_types'] ) ? esc_attr( $data['include_record_types'] ) : false;
1657
			foreach ( $object['data']['fields'] as $key => $value ) {
1658
				if ( '' === $type || $type === $value['type'] ) {
1659
					$object_fields[ $key ] = $value;
1660
					if ( isset( $attributes ) ) {
1661
						$object_fields[ $key ] = array_intersect_key( $value, array_flip( $attributes ) );
1662
					}
1663
				}
1664
			}
1665
			if ( true === $include_record_types ) {
0 ignored issues
show
introduced by
The condition true === $include_record_types is always false.
Loading history...
1666
				$object_record_types = array();
1667
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1668
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1669
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1670
					}
1671
				}
1672
			}
1673
		}
1674
1675
		if ( true === $ajax ) {
1676
			$ajax_response = array(
1677
				'fields' => $object_fields,
1678
			);
1679
			wp_send_json_success( $ajax_response );
1680
		} else {
1681
			return $object_fields;
1682
		}
1683
1684
	}
1685
1686
	/**
1687
	 * Get WordPress object fields for fieldmapping
1688
	 * This takes either the $_POST array via ajax, or can be directly called with a $wordpress_object field
1689
	 *
1690
	 * @param string $wordpress_object is the name of the WordPress object.
1691
	 * @return array $object_fields
1692
	 */
1693
	public function get_wordpress_object_fields( $wordpress_object = '' ) {
1694
		$ajax      = false;
1695
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1696
		if ( empty( $wordpress_object ) ) {
1697
			$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

1697
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1698
			$ajax             = true;
1699
		}
1700
1701
		$object_fields = $this->wordpress->get_wordpress_object_fields( $wordpress_object );
1702
1703
		if ( true === $ajax ) {
1704
			$ajax_response = array(
1705
				'fields' => $object_fields,
1706
			);
1707
			wp_send_json_success( $ajax_response );
1708
		} else {
1709
			return $object_fields;
1710
		}
1711
	}
1712
1713
	/**
1714
	 * Manually push the WordPress object to Salesforce
1715
	 * This takes either the $_POST array via ajax, or can be directly called with $wordpress_object and $wordpress_id fields
1716
	 *
1717
	 * @param string $wordpress_object is the name of the WordPress object.
1718
	 * @param int    $wordpress_id is the ID of the WordPress record.
1719
	 * @param bool   $force_return Force the method to return json instead of outputting it.
1720
	 */
1721
	public function push_to_salesforce( $wordpress_object = '', $wordpress_id = '', $force_return = false ) {
1722
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1723
		if ( empty( $wordpress_object ) && empty( $wordpress_id ) ) {
1724
			$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

1724
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1725
			$wordpress_id     = isset( $post_data['wordpress_id'] ) ? absint( $post_data['wordpress_id'] ) : '';
1726
		}
1727
1728
		// clarify what that variable is in this context.
1729
		$object_type = $wordpress_object;
1730
1731
		// When objects are already mapped, there is a Salesforce id as well. Otherwise, it's blank.
1732
		$salesforce_id = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( $post_data['salesforce_id'] ) : '';
1733
		if ( '' === $salesforce_id ) {
1734
			$method = 'POST';
1735
		} else {
1736
			$method = 'PUT';
1737
		}
1738
1739
		$result = $this->push->manual_push( $object_type, $wordpress_id, $method );
1740
1741
		if ( false === $force_return && ! empty( $post_data['wordpress_object'] ) && ! empty( $post_data['wordpress_id'] ) ) {
1742
			wp_send_json_success( $result );
1743
		} else {
1744
			return $result;
1745
		}
1746
1747
	}
1748
1749
	/**
1750
	 * Manually pull the Salesforce object into WordPress
1751
	 * This takes either the $_POST array via ajax, or can be directly called with $salesforce_id fields
1752
	 *
1753
	 * @param string $salesforce_id is the ID of the Salesforce record.
1754
	 * @param string $wordpress_object is the name of the WordPress object.
1755
	 */
1756
	public function pull_from_salesforce( $salesforce_id = '', $wordpress_object = '' ) {
1757
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1758
		if ( empty( $wordpress_object ) && empty( $salesforce_id ) ) {
1759
			$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

1759
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1760
			$salesforce_id    = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_id'] ) ) : '';
1761
		}
1762
		$type   = $this->salesforce['sfapi']->get_sobject_type( $salesforce_id );
1763
		$result = $this->pull->manual_pull( $type, $salesforce_id, $wordpress_object ); // we want the wp object to make sure we get the right fieldmap.
1764
		if ( ! empty( $post_data ) ) {
1765
			wp_send_json_success( $result );
1766
		} else {
1767
			return $result;
1768
		}
1769
	}
1770
1771
	/**
1772
	 * Manually pull the Salesforce object into WordPress
1773
	 * This takes an id for a mapping object row
1774
	 *
1775
	 * @param int $mapping_id is the ID of the mapping object record.
1776
	 */
1777
	public function refresh_mapped_data( $mapping_id = '' ) {
1778
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1779
		if ( empty( $mapping_id ) ) {
1780
			$mapping_id = isset( $post_data['mapping_id'] ) ? absint( $post_data['mapping_id'] ) : '';
1781
		}
1782
		$result = $this->mappings->get_all_object_maps(
1783
			array(
1784
				'id' => $mapping_id,
1785
			)
1786
		);
1787
1788
		$object_map = array();
1789
1790
		// result is an array of arrays, not just one array.
1791
		if ( 1 === count( $result ) ) {
1792
			$object_map = $result[0];
1793
		}
1794
1795
		if ( ! empty( $post_data ) ) {
1796
			wp_send_json_success( $object_map );
1797
		} else {
1798
			return $object_map;
1799
		}
1800
	}
1801
1802
	/**
1803
	 * Prepare fieldmap data and redirect after processing
1804
	 * This runs when the create or update forms are submitted
1805
	 * It is public because it depends on an admin hook
1806
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1807
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1808
	 */
1809
	public function prepare_fieldmap_data() {
1810
		$error     = false;
1811
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1812
		$cachekey  = wp_json_encode( $post_data );
1813
		if ( false !== $cachekey ) {
1814
			$cachekey = md5( $cachekey );
1815
		}
1816
1817
		if ( ! isset( $post_data['label'] ) || ! isset( $post_data['salesforce_object'] ) || ! isset( $post_data['wordpress_object'] ) ) {
1818
			$error = true;
1819
		}
1820
		if ( true === $error ) {
1821
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1822
			if ( '' !== $cachekey ) {
1823
				$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

1823
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1824
			}
1825
		} else { // there are no errors
1826
			// send the row to the fieldmap class
1827
			// if it is add or clone, use the create method.
1828
			$method            = esc_attr( $post_data['method'] );
1829
			$salesforce_fields = $this->get_salesforce_object_fields(
1830
				array(
1831
					'salesforce_object' => $post_data['salesforce_object'],
1832
				)
1833
			);
1834
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1835
			if ( 'add' === $method || 'clone' === $method ) {
1836
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1837
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method.
1838
				$id     = esc_attr( $post_data['id'] );
1839
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
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'] ) . '&transient=' . $cachekey;
1845
				}
1846
			} else {
1847
				// if the user has saved a fieldmap, clear the currently running query value if there is one.
1848
				if ( '' !== get_option( $this->option_prefix . 'currently_pulling_query_' . $post_data['salesforce_object'], '' ) ) {
1849
					$this->pull->clear_current_type_query( $post_data['salesforce_object'] );
1850
				}
1851
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1852
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1853
				}
1854
				// then send the user to the list of fieldmaps.
1855
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1856
			}
1857
		}
1858
		wp_safe_redirect( $url );
1859
		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...
1860
	}
1861
1862
	/**
1863
	 * Delete fieldmap data and redirect after processing
1864
	 * This runs when the delete link is clicked, after the user confirms
1865
	 * It is public because it depends on an admin hook
1866
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1867
	 */
1868
	public function delete_fieldmap() {
1869
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1870
		if ( $post_data['id'] ) {
1871
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1872
			if ( true === $result ) {
1873
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1874
			} else {
1875
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1876
			}
1877
			wp_safe_redirect( $url );
1878
			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...
1879
		}
1880
	}
1881
1882
	/**
1883
	 * Prepare object data and redirect after processing
1884
	 * This runs when the update form is submitted
1885
	 * It is public because it depends on an admin hook
1886
	 * It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1887
	 * This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1888
	 */
1889
	public function prepare_object_map_data() {
1890
		$error     = false;
1891
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1892
		$cachekey  = wp_json_encode( $post_data );
1893
		if ( false !== $cachekey ) {
1894
			$cachekey = md5( $cachekey );
1895
		}
1896
1897
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1898
			$error = true;
1899
		}
1900
		if ( true === $error ) {
1901
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1902
			if ( '' !== $cachekey ) {
1903
				$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

1903
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1904
			}
1905
		} else { // there are no errors
1906
			// send the row to the object map class.
1907
			$method = esc_attr( $post_data['method'] );
1908
			if ( 'edit' === $method ) { // if it is edit, use the update method.
1909
				$id     = esc_attr( $post_data['id'] );
1910
				$result = $this->mappings->update_object_map( $post_data, $id );
1911
			}
1912
			if ( false === $result ) { // if the database didn't save, it's still an error.
1913
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1914
				if ( '' !== $cachekey ) {
1915
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1916
				}
1917
			} else {
1918
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1919
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1920
				}
1921
				// then send the user to the success redirect url.
1922
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1923
			}
1924
		}
1925
		wp_safe_redirect( $url );
1926
		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...
1927
	}
1928
1929
	/**
1930
	 * Delete object map data and redirect after processing
1931
	 * This runs when the delete link is clicked on an error row, after the user confirms
1932
	 * It is public because it depends on an admin hook
1933
	 * It then calls the Object_Sync_Sf_Mapping class and the delete method
1934
	 */
1935
	public function delete_object_map() {
1936
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1937
		if ( isset( $post_data['id'] ) ) {
1938
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1939
			if ( true === $result ) {
1940
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1941
			} else {
1942
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1943
			}
1944
			wp_safe_redirect( $url );
1945
			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...
1946
		} elseif ( $post_data['delete'] ) {
1947
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1948
			$cachekey  = wp_json_encode( $post_data );
1949
			if ( false !== $cachekey ) {
1950
				$cachekey = md5( $cachekey );
1951
			}
1952
			$error = false;
1953
			if ( ! isset( $post_data['delete'] ) ) {
1954
				$error = true;
1955
			}
1956
			if ( true === $error ) {
1957
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1958
				if ( '' !== $cachekey ) {
1959
					$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

1959
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . /** @scrutinizer ignore-type */ $cachekey;
Loading history...
1960
				}
1961
			} else { // there are no errors.
1962
				$result = $this->mappings->delete_object_map( array_keys( $post_data['delete'] ) );
1963
				if ( true === $result ) {
1964
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1965
				}
1966
1967
				if ( false === $result ) { // if the database didn't save, it's still an error.
1968
					$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1969
					if ( '' !== $cachekey ) {
1970
						$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&mapping_error_transient=' . $cachekey;
1971
					}
1972
				} else {
1973
					if ( isset( $post_data['mapping_error_transient'] ) ) { // there was previously an error saved. can delete it now.
1974
						$this->sfwp_transients->delete( esc_attr( $post_data['mapping_error_transient'] ) );
1975
					}
1976
					// then send the user to the list of fieldmaps.
1977
					$url = esc_url_raw( $post_data['redirect_url_success'] );
1978
				}
1979
			}
1980
			wp_safe_redirect( $url );
1981
			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...
1982
		}
1983
	}
1984
1985
	/**
1986
	 * Import a json file and use it for plugin data
1987
	 */
1988
	public function import_json_file() {
1989
1990
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_import'], 'object_sync_for_salesforce_nonce_import' ) ) {
1991
			return;
1992
		}
1993
		if ( ! current_user_can( 'manage_options' ) ) {
1994
			return;
1995
		}
1996
		$path      = $_FILES['import_file']['name'];
1997
		$extension = pathinfo( $path, PATHINFO_EXTENSION );
1998
		if ( 'json' !== $extension ) {
1999
			wp_die( esc_html__( 'Please upload a valid .json file', 'object-sync-for-salesforce' ) );
2000
		}
2001
2002
		$import_file = $_FILES['import_file']['tmp_name'];
2003
		if ( empty( $import_file ) ) {
2004
			wp_die( esc_html__( 'Please upload a file to import', 'object-sync-for-salesforce' ) );
2005
		}
2006
2007
		// Retrieve the data from the file and convert the json object to an array.
2008
		$data = (array) json_decode( file_get_contents( $import_file ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
2009
2010
		$overwrite = isset( $_POST['overwrite'] ) ? esc_attr( $_POST['overwrite'] ) : '';
2011
		if ( true === filter_var( $overwrite, FILTER_VALIDATE_BOOLEAN ) ) {
2012
			if ( isset( $data['fieldmaps'] ) ) {
2013
				$fieldmaps = $this->mappings->get_fieldmaps();
2014
				foreach ( $fieldmaps as $fieldmap ) {
2015
					$id     = $fieldmap['id'];
2016
					$delete = $this->mappings->delete_fieldmap( $id );
2017
				}
2018
			}
2019
			if ( isset( $data['object_maps'] ) ) {
2020
				$object_maps = $this->mappings->get_all_object_maps();
2021
				foreach ( $object_maps as $object_map ) {
2022
					$id     = $object_map['id'];
2023
					$delete = $this->mappings->delete_object_map( $id );
2024
				}
2025
			}
2026
			if ( isset( $data['plugin_settings'] ) ) {
2027
				foreach ( $data['plugin_settings'] as $key => $value ) {
2028
					delete_option( $value['option_name'] );
2029
				}
2030
			}
2031
		}
2032
2033
		// if the option says to, set all the imported fieldmaps to inactive.
2034
		$import_fieldmaps_inactive = isset( $_POST['import_fieldmaps_inactive'] ) ? esc_attr( $_POST['import_fieldmaps_inactive'] ) : '';
2035
		if ( true === filter_var( $import_fieldmaps_inactive, FILTER_VALIDATE_BOOLEAN ) ) {
2036
			if ( isset( $data['fieldmaps'] ) ) {
2037
				foreach ( $data['fieldmaps'] as $key => $fieldmap ) {
2038
					$data['fieldmaps'][ $key ]['fieldmap_status'] = 'inactive';
2039
				}
2040
			}
2041
		}
2042
2043
		$success = true;
2044
2045
		if ( isset( $data['fieldmaps'] ) ) {
2046
			$successful_fieldmaps = array();
2047
			$error_fieldmaps      = array();
2048
			foreach ( $data['fieldmaps'] as $fieldmap ) {
2049
				unset( $fieldmap['id'] );
2050
				$create = $this->mappings->create_fieldmap( $fieldmap );
2051
				if ( false === $create ) {
2052
					$success = false;
2053
				}
2054
				if ( false === $create ) {
2055
					$error_fieldmaps[] = $fieldmap;
2056
				} else {
2057
					$successful_fieldmaps[] = $create;
2058
				}
2059
			}
2060
		}
2061
2062
		if ( isset( $data['object_maps'] ) ) {
2063
			$successful_object_maps = array();
2064
			$error_object_maps      = array();
2065
			foreach ( $data['object_maps'] as $object_map ) {
2066
				unset( $object_map['id'] );
2067
				if ( isset( $object_map['object_type'] ) ) {
2068
					$sf_sync_trigger = $this->mappings->sync_sf_create;
2069
					$create          = $this->pull->salesforce_pull_process_records( $object_map['object_type'], $object_map['salesforce_id'], $sf_sync_trigger );
2070
				} else {
2071
					$create = $this->mappings->create_object_map( $object_map );
2072
				}
2073
				if ( false === $create ) {
2074
					$error_object_maps[] = $object_map;
2075
				} else {
2076
					$successful_object_maps[] = $create;
2077
				}
2078
			}
2079
		}
2080
2081
		if ( isset( $data['plugin_settings'] ) ) {
2082
			foreach ( $data['plugin_settings'] as $key => $value ) {
2083
				update_option( $value['option_name'], maybe_unserialize( $value['option_value'] ), $value['autoload'] );
2084
			}
2085
		}
2086
2087
		if ( ! empty( $error_fieldmaps ) && ! empty( $error_object_maps ) ) {
2088
			$status = 'error';
2089
			$body   = sprintf( esc_html__( 'These are the import items that were not able to save: ', 'object-sync-for-salesforce' ) . '<ul>' );
2090
			foreach ( $error_fieldmaps as $fieldmap ) {
2091
				$body .= sprintf(
2092
					// translators: placeholders are: 1) the fieldmap row ID, 2) the Salesforce object type, 3) the WordPress object type.
2093
					'<li>' . esc_html__( 'Fieldmap id (if it exists): %1$s. Salesforce object type: %2$s. WordPress object type: %3$s', 'object-sync-for-salesforce' ) . '</li>',
2094
					isset( $fieldmap['id'] ) ? absint( $fieldmap['id'] ) : '',
2095
					esc_attr( $fieldmap['salesforce_object'] ),
2096
					esc_attr( $fieldmap['wordpress_object'] )
2097
				);
2098
			}
2099
			foreach ( $error_object_maps as $mapping_object ) {
2100
				$body .= sprintf(
2101
					// translators: placeholders are: 1) the mapping object row ID, 2) the ID of the Salesforce object, 3) the WordPress object type.
2102
					'<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>',
2103
					isset( $mapping_object['id'] ) ? absint( $mapping_object['id'] ) : '',
2104
					esc_attr( $mapping_object['salesforce_id'] ),
2105
					esc_attr( $mapping_object['wordpress_object'] )
2106
				);
2107
			}
2108
			$body .= sprintf( '</ul>' );
2109
2110
			$this->logging->setup(
2111
				sprintf(
2112
					// translators: %1$s is the log status.
2113
					esc_html__( '%1$s on import: some of the rows were unable to save. Read this post for details.', 'object-sync-for-salesforce' ),
2114
					ucfirst( esc_attr( $status ) )
2115
				),
2116
				$body,
2117
				0,
2118
				0,
2119
				$status
2120
			);
2121
		}
2122
2123
		if ( empty( $error_fieldmaps ) && empty( $error_object_maps ) && ( ! empty( $successful_fieldmaps ) || ! empty( $successful_object_maps ) ) ) {
2124
			$this->clear_cache( false );
2125
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=true' ) );
2126
			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...
2127
		} elseif ( ! empty( $error_fieldmaps ) && ! empty( $successful_fieldmaps ) ) {
2128
			$this->clear_cache( false );
2129
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=partial' ) );
2130
		} elseif ( ! empty( $error_object_maps ) && ! empty( $successful_object_maps ) ) {
2131
			$this->clear_cache( false );
2132
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=partial' ) );
2133
		} else {
2134
			wp_safe_redirect( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=import-export&data_saved=false' ) );
2135
			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...
2136
		}
2137
2138
	}
2139
2140
	/**
2141
	 * Create a json file for exporting
2142
	 */
2143
	public function export_json_file() {
2144
2145
		if ( ! wp_verify_nonce( $_POST['object_sync_for_salesforce_nonce_export'], 'object_sync_for_salesforce_nonce_export' ) ) {
2146
			return;
2147
		}
2148
		if ( ! current_user_can( 'manage_options' ) ) {
2149
			return;
2150
		}
2151
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
2152
		$export    = array();
2153
		if ( in_array( 'fieldmaps', $post_data['export'], true ) ) {
2154
			$export['fieldmaps'] = $this->mappings->get_fieldmaps();
2155
		}
2156
		if ( in_array( 'object_maps', $post_data['export'], true ) ) {
2157
			$export['object_maps'] = $this->mappings->get_all_object_maps();
2158
		}
2159
		if ( in_array( 'plugin_settings', $post_data['export'], true ) ) {
2160
			$wpdb                      = $this->wpdb;
2161
			$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 );
2162
			$export['plugin_settings'] = $export_results;
2163
		}
2164
		nocache_headers();
2165
		header( 'Content-Type: application/json; charset=utf-8' );
2166
		header( 'Content-Disposition: attachment; filename=object-sync-for-salesforce-data-export-' . gmdate( 'm-d-Y' ) . '.json' );
2167
		header( 'Expires: 0' );
2168
		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

2168
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
2169
		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...
2170
	}
2171
2172
	/**
2173
	 * Default display for <input> fields
2174
	 *
2175
	 * @param array $args is the arguments to create the field.
2176
	 */
2177
	public function display_input_field( $args ) {
2178
		$type    = $args['type'];
2179
		$id      = $args['label_for'];
2180
		$name    = $args['name'];
2181
		$desc    = $args['desc'];
2182
		$checked = '';
2183
2184
		$class = 'regular-text';
2185
2186
		if ( 'checkbox' === $type ) {
2187
			$class = 'checkbox';
2188
		}
2189
2190
		if ( isset( $args['class'] ) ) {
2191
			$class = $args['class'];
2192
		}
2193
2194
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
2195
			$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

2195
			$value = esc_attr( /** @scrutinizer ignore-type */ get_option( $id, '' ) );
Loading history...
2196
			if ( 'checkbox' === $type ) {
2197
				$value = filter_var( get_option( $id, false ), FILTER_VALIDATE_BOOLEAN );
2198
				if ( true === $value ) {
2199
					$checked = 'checked ';
2200
				}
2201
				$value = 1;
2202
			}
2203
			if ( '' === $value && isset( $args['default'] ) && '' !== $args['default'] ) {
2204
				$value = $args['default'];
2205
			}
2206
2207
			echo sprintf(
2208
				'<input type="%1$s" value="%2$s" name="%3$s" id="%4$s" class="%5$s"%6$s>',
2209
				esc_attr( $type ),
2210
				esc_attr( $value ),
2211
				esc_attr( $name ),
2212
				esc_attr( $id ),
2213
				sanitize_html_class( $class . esc_html( ' code' ) ),
2214
				esc_html( $checked )
2215
			);
2216
			if ( '' !== $desc ) {
2217
				echo sprintf(
2218
					'<p class="description">%1$s</p>',
2219
					esc_html( $desc )
2220
				);
2221
			}
2222
		} else {
2223
			echo sprintf(
2224
				'<p><code>%1$s</code></p>',
2225
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
2226
			);
2227
		}
2228
	}
2229
2230
	/**
2231
	 * Display for multiple checkboxes
2232
	 * Above method can handle a single checkbox as it is
2233
	 *
2234
	 * @param array $args is the arguments to create the checkboxes.
2235
	 */
2236
	public function display_checkboxes( $args ) {
2237
		$type    = 'checkbox';
2238
		$name    = $args['name'];
2239
		$options = get_option( $name, array() );
2240
		foreach ( $args['items'] as $key => $value ) {
2241
			$text    = $value['text'];
2242
			$id      = $value['id'];
2243
			$desc    = $value['desc'];
2244
			$checked = '';
2245
			if ( is_array( $options ) && in_array( (string) $key, $options, true ) ) {
2246
				$checked = 'checked';
2247
			} elseif ( is_array( $options ) && empty( $options ) ) {
2248
				if ( isset( $value['default'] ) && true === $value['default'] ) {
2249
					$checked = 'checked';
2250
				}
2251
			}
2252
			echo sprintf(
2253
				'<div class="checkbox"><label><input type="%1$s" value="%2$s" name="%3$s[]" id="%4$s"%5$s>%6$s</label></div>',
2254
				esc_attr( $type ),
2255
				esc_attr( $key ),
2256
				esc_attr( $name ),
2257
				esc_attr( $id ),
2258
				esc_html( $checked ),
2259
				esc_html( $text )
2260
			);
2261
			if ( '' !== $desc ) {
2262
				echo sprintf(
2263
					'<p class="description">%1$s</p>',
2264
					esc_html( $desc )
2265
				);
2266
			}
2267
		}
2268
	}
2269
2270
	/**
2271
	 * Display for a dropdown
2272
	 *
2273
	 * @param array $args is the arguments needed to create the dropdown.
2274
	 */
2275
	public function display_select( $args ) {
2276
		$type = $args['type'];
2277
		$id   = $args['label_for'];
2278
		$name = $args['name'];
2279
		$desc = $args['desc'];
2280
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
2281
			$current_value = get_option( $name );
2282
2283
			echo sprintf(
2284
				'<div class="select"><select id="%1$s" name="%2$s"><option value="">- ' . esc_html__( 'Select one', 'object-sync-for-salesforce' ) . ' -</option>',
2285
				esc_attr( $id ),
2286
				esc_attr( $name )
2287
			);
2288
2289
			foreach ( $args['items'] as $key => $value ) {
2290
				$text     = $value['text'];
2291
				$value    = $value['value'];
2292
				$selected = '';
2293
				if ( $key === $current_value || $value === $current_value ) {
2294
					$selected = ' selected';
2295
				}
2296
2297
				echo sprintf(
2298
					'<option value="%1$s"%2$s>%3$s</option>',
2299
					esc_attr( $value ),
2300
					esc_attr( $selected ),
2301
					esc_html( $text )
2302
				);
2303
2304
			}
2305
			echo '</select>';
2306
			if ( '' !== $desc ) {
2307
				echo sprintf(
2308
					'<p class="description">%1$s</p>',
2309
					esc_html( $desc )
2310
				);
2311
			}
2312
			echo '</div>';
2313
		} else {
2314
			echo sprintf(
2315
				'<p><code>%1$s</code></p>',
2316
				esc_html__( 'Defined in wp-config.php', 'object-sync-for-salesforce' )
2317
			);
2318
		}
2319
	}
2320
2321
	/**
2322
	 * Default display for <a href> links
2323
	 *
2324
	 * @param array $args is the arguments to make the link.
2325
	 */
2326
	public function display_link( $args ) {
2327
		$label = $args['label'];
2328
		$desc  = $args['desc'];
2329
		$url   = $args['url'];
2330
		if ( isset( $args['link_class'] ) ) {
2331
			echo sprintf(
2332
				'<p><a class="%1$s" href="%2$s">%3$s</a></p>',
2333
				esc_attr( $args['link_class'] ),
2334
				esc_url( $url ),
2335
				esc_html( $label )
2336
			);
2337
		} else {
2338
			echo sprintf(
2339
				'<p><a href="%1$s">%2$s</a></p>',
2340
				esc_url( $url ),
2341
				esc_html( $label )
2342
			);
2343
		}
2344
2345
		if ( '' !== $desc ) {
2346
			echo sprintf(
2347
				'<p class="description">%1$s</p>',
2348
				esc_html( $desc )
2349
			);
2350
		}
2351
2352
	}
2353
2354
	/**
2355
	 * Allow for a standard sanitize/validate method. We could use more specific ones if need be, but this one provides a baseline.
2356
	 *
2357
	 * @param string $option is the option value.
2358
	 * @return string $option is the sanitized option value.
2359
	 */
2360
	public function sanitize_validate_text( $option ) {
2361
		if ( is_array( $option ) ) {
0 ignored issues
show
introduced by
The condition is_array($option) is always false.
Loading history...
2362
			$options = array();
2363
			foreach ( $option as $key => $value ) {
2364
				$options[ $key ] = sanitize_text_field( $value );
2365
			}
2366
			return $options;
2367
		}
2368
		$option = sanitize_text_field( $option );
2369
		return $option;
2370
	}
2371
2372
	/**
2373
	 * Run a demo of Salesforce API call on the authenticate tab after WordPress has authenticated with it
2374
	 *
2375
	 * @param object $sfapi this is the Salesforce API object.
2376
	 */
2377
	private function status( $sfapi ) {
2378
2379
		$contacts = $sfapi->query( 'SELECT Name, Id from Contact LIMIT 100' );
2380
2381
		if ( 200 !== $contacts['code'] ) {
2382
			$contacts_apicall_summary = esc_html__( 'The plugin was unable to load a sample of Contacts from Salesforce. This may mean that there are connection issues, or that the authorization has expired.', 'object-sync-for-salesforce' );
2383
		} else {
2384
2385
			// format this array into html so users can see the contacts.
2386
			if ( true === $contacts['cached'] ) {
2387
				$contacts_is_cached = esc_html__( 'They are cached, and', 'object-sync-for-salesforce' );
2388
			} else {
2389
				$contacts_is_cached = esc_html__( 'They are not cached, but', 'object-sync-for-salesforce' );
2390
			}
2391
2392
			if ( true === $contacts['from_cache'] ) {
2393
				$contacts_from_cache = esc_html__( 'they were loaded from the cache', 'object-sync-for-salesforce' );
2394
			} else {
2395
				$contacts_from_cache = esc_html__( 'they were not loaded from the cache', 'object-sync-for-salesforce' );
2396
			}
2397
2398
			if ( true === $contacts['is_redo'] ) {
2399
				$contacts_refreshed_token = esc_html__( 'This request did require refreshing the Salesforce token', 'object-sync-for-salesforce' );
2400
			} else {
2401
				$contacts_refreshed_token = esc_html__( 'This request did not require refreshing the Salesforce token', 'object-sync-for-salesforce' );
2402
			}
2403
2404
			// display contact summary if there are any contacts.
2405
			if ( 0 < absint( $contacts['data']['totalSize'] ) ) {
2406
				$contacts_apicall_summary = sprintf(
2407
					// 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.
2408
					esc_html__( 'Salesforce successfully returned %1$s %2$s records. %3$s %4$s. %5$s.', 'object-sync-for-salesforce' ),
2409
					absint( $contacts['data']['totalSize'] ),
2410
					esc_html( $contacts['data']['records'][0]['attributes']['type'] ),
2411
					$contacts_is_cached,
2412
					$contacts_from_cache,
2413
					$contacts_refreshed_token
2414
				);
2415
			} else {
2416
				$contacts_apicall_summary = '';
2417
			}
2418
		}
2419
2420
		require_once plugin_dir_path( $this->file ) . '/templates/admin/status.php';
2421
2422
	}
2423
2424
	/**
2425
	 * Deauthorize WordPress from Salesforce.
2426
	 * This deletes the tokens from the database; it does not currently do anything in Salesforce
2427
	 * For this plugin at this time, that is the decision we are making: don't do any kind of authorization stuff inside Salesforce
2428
	 */
2429
	private function logout() {
2430
		$delete_access_token = delete_option( $this->option_prefix . 'access_token' );
2431
		if ( true === $delete_access_token ) {
2432
			$this->access_token = '';
2433
		}
2434
		$delete_instance_url = delete_option( $this->option_prefix . 'instance_url' );
2435
		if ( true === $delete_instance_url ) {
2436
			$this->instance_url = '';
2437
		}
2438
		$delete_refresh_token = delete_option( $this->option_prefix . 'refresh_token' );
2439
		if ( true === $delete_refresh_token ) {
2440
			$this->refresh_token = '';
2441
		}
2442
		echo sprintf(
2443
			'<p>You have been logged out. You can use the <a href="%1$s">%2$s</a> tab to log in again.</p>',
2444
			esc_url( get_admin_url( null, 'options-general.php?page=' . $this->admin_settings_url_param . '&tab=authorize' ) ),
2445
			esc_html__( 'Authorize', 'object-sync-for-salesforce' )
2446
		);
2447
	}
2448
2449
	/**
2450
	 * Ajax call to clear the plugin cache.
2451
	 */
2452
	public function clear_sfwp_cache() {
2453
		$result   = $this->clear_cache( true );
2454
		$response = array(
2455
			'message' => $result['message'],
2456
			'success' => $result['success'],
2457
		);
2458
		wp_send_json_success( $response );
2459
	}
2460
2461
	/**
2462
	 * Ajax call to delete the object_sync_for_salesforce_api_version value from wp_options
2463
	 * @deprecated and will be removed in 3.0.0.
2464
	 */
2465
	public function delete_salesforce_api_version() {
2466
		$deprecated_option_key = $this->option_prefix . 'api_version';
2467
		$result                = delete_option( $deprecated_option_key );
2468
		if ( true === $result ) {
2469
			$message = sprintf(
2470
				// translators: the parameter is the option key where the API version was stored.
2471
				esc_html__( 'The %1$s value was successfully deleted.', 'object-sync-for-salesforce' ),
2472
				'<code>' . $deprecated_option_key . '</code>'
2473
			);
2474
		} else {
2475
			$message = sprintf(
2476
				// translators: the parameter is the option key where the API version was stored.
2477
				esc_html__( 'There was an error in deleting the %1$s value. This may mean you have stored the value in a wp-config.php file instaed of in the options table. If this is not the case, you may want to reload the page and try again.', 'object-sync-for-salesforce' ),
2478
				'<code>' . $deprecated_option_key . '</code>'
2479
			);
2480
		}
2481
		$response = array(
2482
			'message' => $message,
2483
			'success' => $result,
2484
		);
2485
		wp_send_json_success( $response );
2486
	}
2487
2488
	/**
2489
	 * Clear the plugin's cache.
2490
	 * This uses the flush method contained in the WordPress cache to clear all of this plugin's cached data.
2491
	 *
2492
	 * @param bool $ajax Whether this is an Ajax request or not.
2493
	 * @return array
2494
	 */
2495
	private function clear_cache( $ajax = false ) {
2496
		$result  = $this->wordpress->sfwp_transients->flush();
2497
		$success = $result['success'];
2498
		if ( 0 < $result['count'] ) {
2499
			if ( true === $success ) {
2500
				$message = __( 'The plugin cache has been cleared.', 'object-sync-for-salesforce' );
2501
			} else {
2502
				$message = __( 'There was an error clearing the plugin cache. Try refreshing this page.', 'object-sync-for-salesforce' );
2503
			}
2504
		} else {
2505
			$success = true;
2506
			$message = __( 'The cache was not cleared because it is empty. You can try again later.', 'object-sync-for-salesforce' );
2507
		}
2508
		if ( false === $ajax ) {
2509
			echo '<p>' . esc_html( $message ) . '</p>';
2510
		} else {
2511
			return array(
2512
				'message' => esc_html( $message ),
2513
				'success' => $success,
2514
			);
2515
		}
2516
	}
2517
2518
	/**
2519
	 * Check WordPress Admin permissions
2520
	 * Check if the current user is allowed to access the Salesforce plugin options
2521
	 */
2522
	private function check_wordpress_admin_permissions() {
2523
2524
		// one programmatic way to give this capability to additional user roles is the
2525
		// object_sync_for_salesforce_roles_configure_salesforce hook
2526
		// it runs on activation of this plugin, and will assign the below capability to any role
2527
		// coming from the hook.
2528
2529
		// alternatively, other roles can get this capability in whatever other way you like
2530
		// point is: to administer this plugin, you need this capability.
2531
2532
		if ( ! current_user_can( 'configure_salesforce' ) ) {
2533
			return false;
2534
		} else {
2535
			return true;
2536
		}
2537
2538
	}
2539
2540
	/**
2541
	 * Check WordPress SSL status.
2542
	 * HTTPS is required to connect to Salesforce.
2543
	 *
2544
	 * @return bool $secure_admin
2545
	 */
2546
	private function check_wordpress_ssl() {
2547
2548
		// the wp_is_using_https() function was added in WordPress 5.7.
2549
		if ( ! function_exists( 'wp_is_using_https' ) ) {
2550
			return true;
2551
		}
2552
2553
		// the whole site is already using SSL.
2554
		if ( true === wp_is_using_https() ) {
2555
			return true;
2556
		}
2557
2558
		$secure_admin = false;
2559
2560
		// the admin is already forced to use SSL.
2561
		if ( true === FORCE_SSL_ADMIN ) {
0 ignored issues
show
introduced by
The condition true === FORCE_SSL_ADMIN is always false.
Loading history...
2562
			$secure_admin = true;
2563
		}
2564
2565
		// the server reports that the current URL is using HTTPS.
2566
		if ( isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ) {
2567
			$secure_admin = true;
2568
		}
2569
2570
		return $secure_admin;
2571
	}
2572
2573
	/**
2574
	 * Check WordPress SSL support.
2575
	 * This allows us to show a different message if SSL is supported, but is not in use.
2576
	 *
2577
	 * @return bool $https_supported
2578
	 */
2579
	private function check_wordpress_ssl_support() {
2580
2581
		// the wp_is_https_supported() function was added in WordPress 5.7.
2582
		if ( ! function_exists( 'wp_is_https_supported' ) ) {
2583
			return true;
2584
		}
2585
2586
		if ( true === wp_is_https_supported() ) {
2587
			return true;
2588
		}
2589
2590
		$https_supported = false;
2591
		return $https_supported;
2592
2593
	}
2594
2595
	/**
2596
	 * Show what we know about this user's relationship to a Salesforce object, if any
2597
	 *
2598
	 * @param object $user this is the user object from WordPress.
2599
	 */
2600
	public function show_salesforce_user_fields( $user ) {
2601
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2602
		if ( true === $this->check_wordpress_admin_permissions() ) {
2603
			$mappings = $this->mappings->load_all_by_wordpress( 'user', $user->ID );
2604
			$fieldmap = $this->mappings->get_fieldmaps(
2605
				null, // id field must be null for multiples.
2606
				array(
2607
					'wordpress_object' => 'user',
2608
				)
2609
			);
2610
			if ( count( $mappings ) > 0 ) {
2611
				foreach ( $mappings as $mapping ) {
2612
					if ( isset( $mapping['id'] ) && ! isset( $get_data['edit_salesforce_mapping'] ) && ! isset( $get_data['delete_salesforce_mapping'] ) ) {
2613
						require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce.php';
2614
					} elseif ( ! empty( $fieldmap ) ) { // is the user mapped to something already?
2615
						if ( isset( $get_data['edit_salesforce_mapping'] ) && true === filter_var( $get_data['edit_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2616
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-change.php';
2617
						} elseif ( isset( $get_data['delete_salesforce_mapping'] ) && true === filter_var( $get_data['delete_salesforce_mapping'], FILTER_VALIDATE_BOOLEAN ) ) {
2618
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-delete.php';
2619
						} else {
2620
							require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-map.php';
2621
						}
2622
					}
2623
				}
2624
			} else {
2625
				require_once plugin_dir_path( $this->file ) . '/templates/admin/user-profile-salesforce-map.php';
2626
			}
2627
		}
2628
	}
2629
2630
	/**
2631
	 * If the user profile has been mapped to Salesforce, do it
2632
	 *
2633
	 * @param int $user_id the ID of the WordPress user.
2634
	 */
2635
	public function save_salesforce_user_fields( $user_id ) {
2636
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
2637
		if ( isset( $post_data['salesforce_update_mapped_user'] ) && true === filter_var( $post_data['salesforce_update_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2638
			$mapping_objects = $this->mappings->get_all_object_maps(
2639
				array(
2640
					'wordpress_id'     => $user_id,
2641
					'wordpress_object' => 'user',
2642
				)
2643
			);
2644
			foreach ( $mapping_objects as $mapping_object ) {
2645
				$mapping_object['salesforce_id'] = $post_data['salesforce_id'];
2646
				$result                          = $this->mappings->update_object_map( $mapping_object, $mapping_object['id'] );
2647
			}
2648
		} elseif ( isset( $post_data['salesforce_create_mapped_user'] ) && true === filter_var( $post_data['salesforce_create_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2649
			// if a Salesforce ID was entered.
2650
			if ( isset( $post_data['salesforce_id'] ) && ! empty( $post_data['salesforce_id'] ) ) {
2651
				$mapping_object = $this->create_object_map( $user_id, 'user', $post_data['salesforce_id'] );
2652
			} elseif ( isset( $post_data['push_new_user_to_salesforce'] ) ) {
2653
				// otherwise, create a new record in Salesforce.
2654
				$result = $this->push_to_salesforce( 'user', $user_id );
2655
			}
2656
		} elseif ( isset( $post_data['salesforce_delete_mapped_user'] ) && true === filter_var( $post_data['salesforce_delete_mapped_user'], FILTER_VALIDATE_BOOLEAN ) ) {
2657
			// if a Salesforce ID was entered.
2658
			if ( isset( $post_data['mapping_id'] ) && ! empty( $post_data['mapping_id'] ) ) {
2659
				$delete = $this->mappings->delete_object_map( $post_data['mapping_id'] );
2660
			}
2661
		}
2662
	}
2663
2664
	/**
2665
	 * Render tabs for settings pages in admin
2666
	 *
2667
	 * @param array  $tabs is the tabs for the settings menu.
2668
	 * @param string $tab is a single tab.
2669
	 */
2670
	private function tabs( $tabs, $tab = '' ) {
2671
2672
		$get_data        = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
2673
		$consumer_key    = $this->login_credentials['consumer_key'];
2674
		$consumer_secret = $this->login_credentials['consumer_secret'];
2675
		$callback_url    = $this->login_credentials['callback_url'];
2676
2677
		$current_tab = $tab;
2678
		echo '<h2 class="nav-tab-wrapper">';
2679
		foreach ( $tabs as $tab_key => $tab_caption ) {
2680
			$active = $current_tab === $tab_key ? ' nav-tab-active' : '';
2681
2682
			if ( true === $this->salesforce['is_authorized'] ) {
2683
				echo sprintf(
2684
					'<a class="nav-tab%1$s" href="%2$s">%3$s</a>',
2685
					esc_attr( $active ),
2686
					esc_url( '?page=' . $this->admin_settings_url_param . '&tab=' . $tab_key ),
2687
					esc_html( $tab_caption )
2688
				);
2689
			} elseif ( 'settings' === $tab_key || ( 'authorize' === $tab_key && isset( $consumer_key ) && isset( $consumer_secret ) && ! empty( $consumer_key ) && ! empty( $consumer_secret ) ) ) {
2690
				echo sprintf(
2691
					'<a class="nav-tab%1$s" href="%2$s">%3$s</a>',
2692
					esc_attr( $active ),
2693
					esc_url( '?page=' . $this->admin_settings_url_param . '&tab=' . $tab_key ),
2694
					esc_html( $tab_caption )
2695
				);
2696
			}
2697
		}
2698
		echo '</h2>';
2699
2700
		if ( isset( $get_data['tab'] ) ) {
2701
			$tab = sanitize_key( $get_data['tab'] );
2702
		} else {
2703
			$tab = '';
2704
		}
2705
	}
2706
2707
	/**
2708
	 * Clear schedule
2709
	 * This clears the schedule if the user clicks the button
2710
	 *
2711
	 * @param string $schedule_name is the name of the schedule being cleared.
2712
	 */
2713
	private function clear_schedule( $schedule_name = '' ) {
2714
		if ( '' !== $schedule_name ) {
2715
			$this->queue->cancel( $schedule_name );
2716
			// translators: $schedule_name is the name of the current queue. Defaults: salesforce_pull, salesforce_push, salesforce.
2717
			echo sprintf( esc_html__( 'You have cleared the %s schedule.', 'object-sync-for-salesforce' ), esc_html( $schedule_name ) );
2718
		} else {
2719
			echo esc_html__( 'You need to specify the name of the schedule you want to clear.', 'object-sync-for-salesforce' );
2720
		}
2721
	}
2722
2723
	/**
2724
	 * Get count of schedule items
2725
	 *
2726
	 * @param string $schedule_name is the name of the schedule.
2727
	 * @return int $count
2728
	 */
2729
	private function get_schedule_count( $schedule_name = '' ) {
2730
		if ( '' !== $schedule_name ) {
2731
			$count       = count(
2732
				$this->queue->search(
2733
					array(
2734
						'group'  => $schedule_name,
2735
						'status' => ActionScheduler_Store::STATUS_PENDING,
2736
					),
2737
					'ARRAY_A'
2738
				)
2739
			);
2740
			$group_count = count(
2741
				$this->queue->search(
2742
					array(
2743
						'group'  => $schedule_name . $this->action_group_suffix,
2744
						'status' => ActionScheduler_Store::STATUS_PENDING,
2745
					),
2746
					'ARRAY_A'
2747
				)
2748
			);
2749
			return $count + $group_count;
2750
		} else {
2751
			return 0;
2752
		}
2753
	}
2754
2755
	/**
2756
	 * Create an object map between a WordPress object and a Salesforce object
2757
	 *
2758
	 * @param int    $wordpress_id Unique identifier for the WordPress object.
2759
	 * @param string $wordpress_object What kind of object is it.
2760
	 * @param string $salesforce_id Unique identifier for the Salesforce object.
2761
	 * @param string $action Did we push or pull.
2762
	 * @return int   $wpdb->insert_id This is the database row for the map object
2763
	 */
2764
	private function create_object_map( $wordpress_id, $wordpress_object, $salesforce_id, $action = '' ) {
2765
		// Create object map and save it.
2766
		$mapping_object = $this->mappings->create_object_map(
2767
			array(
2768
				'wordpress_id'      => $wordpress_id, // wordpress unique id.
2769
				'salesforce_id'     => $salesforce_id, // salesforce unique id. we don't care what kind of object it is at this point.
2770
				'wordpress_object'  => $wordpress_object, // keep track of what kind of wp object this is.
2771
				'last_sync'         => current_time( 'mysql' ),
2772
				'last_sync_action'  => $action,
2773
				'last_sync_status'  => $this->mappings->status_success,
2774
				'last_sync_message' => __( 'Mapping object updated via function: ', 'object-sync-for-salesforce' ) . __FUNCTION__,
2775
			)
2776
		);
2777
2778
		return $mapping_object;
2779
2780
	}
2781
2782
}
2783