Completed
Push — master ( 48bc46...861ddb )
by Jonathan
04:46 queued 34s
created

Object_Sync_Sf_Admin   F

Complexity

Total Complexity 290

Size/Duplication

Total Lines 2217
Duplicated Lines 7.35 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 163
loc 2217
rs 0.8
c 0
b 0
f 0
wmc 290
lcom 1
cbo 1

43 Methods

Rating   Name   Duplication   Size   Complexity  
A set_action_schedule() 0 20 2
A clear_schedule() 0 7 2
A delete_fieldmap() 0 11 3
A add_actions() 0 35 2
A salesforce_settings_forms() 0 21 3
B delete_object_map() 0 44 11
F show_admin_page() 0 210 52
B notices() 0 72 10
C prepare_fieldmap_data() 0 44 12
B save_salesforce_user_fields() 0 19 8
A pull_from_salesforce() 0 12 6
A export_json_file() 0 25 6
A create_admin_menu() 0 3 1
B display_select() 0 38 7
A __construct() 0 35 2
F import_json_file() 0 92 25
A fields_fieldmaps() 0 2 1
A get_schedule_count() 0 19 2
B push_to_salesforce() 0 24 9
C get_salesforce_object_description() 0 43 15
B status() 0 54 6
A clear_cache() 0 14 3
A display_link() 0 20 3
B display_checkboxes() 0 27 9
A clear_sfwp_cache() 0 7 1
B prepare_object_map_data() 0 35 9
A check_wordpress_admin_permissions() 0 14 2
A change_action_schedule() 0 10 1
B fields_scheduling() 0 106 4
A initial_action_schedule() 0 24 2
A refresh_mapped_data() 0 14 4
B tabs() 0 25 9
B fields_settings() 0 266 6
B show_salesforce_user_fields() 0 24 9
A create_object_map() 0 15 1
B fields_log_settings() 0 173 2
A version_options() 0 13 5
A sanitize_validate_text() 0 10 3
A get_wp_sf_object_fields() 0 20 6
A logout() 0 7 1
B display_input_field() 0 41 10
B get_salesforce_object_fields() 0 23 11
A get_wordpress_object_fields() 0 14 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Object_Sync_Sf_Admin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

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

339
						$data          = esc_html( /** @scrutinizer ignore-type */ wp_unslash( $get_data['code'] ) );
Loading history...
340
						$is_authorized = $this->salesforce['sfapi']->request_token( $data );
341
						?>
342
						<script>window.location = '<?php echo esc_url_raw( $callback_url ); ?>'</script>
343
						<?php
344
					} elseif ( true === $this->salesforce['is_authorized'] ) {
345
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/authorized.php' );
346
							$this->status( $this->salesforce['sfapi'] );
347
					} elseif ( true === is_object( $this->salesforce['sfapi'] ) && isset( $consumer_key ) && isset( $consumer_secret ) ) {
348
						?>
349
						<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>
350
						<?php
351
					} else {
352
						$url    = esc_url( get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=settings' ) );
353
						$anchor = esc_html__( 'Settings', 'object-sync-for-salesforce' );
354
						// translators: placeholders are for the settings tab link: 1) the url, and 2) the anchor text
355
						$message = sprintf( esc_html__( 'Salesforce needs to be authorized to connect to this website but the credentials are missing. Use the <a href="%1$s">%2$s</a> tab to add them.', 'object-sync-salesforce' ), $url, $anchor );
356
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/error.php' );
357
					}
358
					break;
359
				case 'fieldmaps':
360
					if ( isset( $get_data['method'] ) ) {
361
362
						$method      = sanitize_key( $get_data['method'] );
363
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=fieldmaps&method=' . $method );
364
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=fieldmaps' );
365
366
						if ( isset( $get_data['transient'] ) ) {
367
							$transient = sanitize_key( $get_data['transient'] );
368
							$posted    = $this->sfwp_transients->get( $transient );
369
						}
370
371
						if ( isset( $posted ) && is_array( $posted ) ) {
372
							$map = $posted;
373
						} elseif ( 'edit' === $method || 'clone' === $method || 'delete' === $method ) {
374
							$map = $this->mappings->get_fieldmaps( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
375
						}
376
377
						if ( isset( $map ) && is_array( $map ) ) {
378
							$label                           = $map['label'];
379
							$salesforce_object               = $map['salesforce_object'];
380
							$salesforce_record_types_allowed = maybe_unserialize( $map['salesforce_record_types_allowed'] );
381
							$salesforce_record_type_default  = $map['salesforce_record_type_default'];
382
							$wordpress_object                = $map['wordpress_object'];
383
							$pull_trigger_field              = $map['pull_trigger_field'];
384
							$fieldmap_fields                 = $map['fields'];
385
							$sync_triggers                   = $map['sync_triggers'];
386
							$push_async                      = $map['push_async'];
387
							$push_drafts                     = $map['push_drafts'];
388
							$pull_to_drafts                  = $map['pull_to_drafts'];
389
							$weight                          = $map['weight'];
390
						}
391
392
						if ( 'add' === $method || 'edit' === $method || 'clone' === $method ) {
393
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-add-edit-clone.php' );
394
						} elseif ( 'delete' === $method ) {
395
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-delete.php' );
396
						}
397
					} else {
398
						$fieldmaps = $this->mappings->get_fieldmaps();
399
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/fieldmaps-list.php' );
400
					} // End if().
401
					break;
402
				case 'logout':
403
					$this->logout();
404
					break;
405
				case 'clear_cache':
406
					$this->clear_cache();
407
					break;
408
				case 'clear_schedule':
409
					if ( isset( $get_data['schedule_name'] ) ) {
410
						$schedule_name = sanitize_key( $get_data['schedule_name'] );
411
					}
412
					$this->clear_schedule( $schedule_name );
413
					break;
414
				case 'settings':
415
					require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/settings.php' );
416
					break;
417
				case 'mapping_errors':
418
					if ( isset( $get_data['method'] ) ) {
419
420
						$method      = sanitize_key( $get_data['method'] );
421
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors&method=' . $method );
422
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors' );
423
424
						if ( isset( $get_data['map_transient'] ) ) {
425
							$transient = sanitize_key( $get_data['map_transient'] );
426
							$posted    = $this->sfwp_transients->get( $transient );
427
						}
428
429
						if ( isset( $posted ) && is_array( $posted ) ) {
430
							$map_row = $posted;
431
						} elseif ( 'edit' === $method || 'delete' === $method ) {
432
							$map_row = $this->mappings->get_failed_object_map( isset( $get_data['id'] ) ? sanitize_key( $get_data['id'] ) : '' );
433
						}
434
435
						if ( isset( $map_row ) && is_array( $map_row ) ) {
436
							$salesforce_id = $map_row['salesforce_id'];
437
							$wordpress_id  = $map_row['wordpress_id'];
438
						}
439
440
						if ( 'edit' === $method ) {
441
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors-edit.php' );
442
						} elseif ( 'delete' === $method ) {
443
							require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors-delete.php' );
444
						}
445
					} else {
446
447
						if ( isset( $get_data['mapping_error_transient'] ) ) {
448
							$transient = sanitize_key( $get_data['mapping_error_transient'] );
449
							$posted    = $this->sfwp_transients->get( $transient );
450
						}
451
452
						$ids_string = '';
453
						$ids        = array();
454
						if ( isset( $posted['delete'] ) ) {
455
							$ids_string = maybe_serialize( $posted['delete'] );
456
							$ids        = $posted['delete'];
457
						}
458
459
						$error_url   = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors&ids=' . $ids_string );
460
						$success_url = get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=mapping_errors' );
461
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/mapping-errors.php' );
462
					}
463
					break;
464
				case 'import-export':
465
					require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/import-export.php' );
466
					break;
467
				default:
468
					$include_settings = apply_filters( $this->option_prefix . 'settings_tab_include_settings', true, $tab );
469
					$content_before   = apply_filters( $this->option_prefix . 'settings_tab_content_before', null, $tab );
470
					$content_after    = apply_filters( $this->option_prefix . 'settings_tab_content_after', null, $tab );
471
					if ( null !== $content_before ) {
472
						echo esc_html( $content_before );
473
					}
474
					if ( true === $include_settings ) {
475
						require_once( plugin_dir_path( __FILE__ ) . '/../templates/admin/settings.php' );
476
					}
477
					if ( null !== $content_after ) {
478
						echo esc_html( $content_after );
479
					}
480
					break;
481
			} // End switch().
482
		} catch ( SalesforceApiException $ex ) {
0 ignored issues
show
Bug introduced by
The type SalesforceApiException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
483
			echo sprintf( '<p>Error <strong>%1$s</strong>: %2$s</p>',
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
484
				absint( $ex->getCode() ),
485
				esc_html( $ex->getMessage() )
486
			);
487
		} catch ( Exception $ex ) {
488
			echo sprintf( '<p>Error <strong>%1$s</strong>: %2$s</p>',
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
489
				absint( $ex->getCode() ),
490
				esc_html( $ex->getMessage() )
491
			);
492
		} // End try().
493
		echo '</div>';
494
	}
495
496
	/**
497
	* Create default WordPress admin settings form for salesforce
498
	* This is for the Settings page/tab
499
	*
500
	*/
501
	public function salesforce_settings_forms() {
502
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
503
		$page     = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
504
		$section  = isset( $get_data['tab'] ) ? sanitize_key( $get_data['tab'] ) : 'settings';
505
506
		$input_callback_default   = array( $this, 'display_input_field' );
507
		$input_checkboxes_default = array( $this, 'display_checkboxes' );
508
		$input_select_default     = array( $this, 'display_select' );
509
		$link_default             = array( $this, 'display_link' );
510
511
		$all_field_callbacks = array(
512
			'text'       => $input_callback_default,
513
			'checkboxes' => $input_checkboxes_default,
514
			'select'     => $input_select_default,
515
			'link'       => $link_default,
516
		);
517
518
		$this->fields_settings( 'settings', 'settings', $all_field_callbacks );
519
		$this->fields_fieldmaps( 'fieldmaps', 'objects' );
520
		$this->fields_scheduling( 'schedule', 'schedule', $all_field_callbacks );
521
		$this->fields_log_settings( 'log_settings', 'log_settings', $all_field_callbacks );
522
	}
523
524
	/**
525
	* Fields for the Settings tab
526
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
527
	*
528
	* @param string $page
529
	* @param string $section
530
	* @param string $input_callback
531
	*/
532
	private function fields_settings( $page, $section, $callbacks ) {
533
		add_settings_section( $page, ucwords( $page ), null, $page );
534
		$salesforce_settings = array(
535
			'consumer_key'                   => array(
536
				'title'    => __( 'Consumer Key', 'object-sync-for-salesforce' ),
537
				'callback' => $callbacks['text'],
538
				'page'     => $page,
539
				'section'  => $section,
540
				'args'     => array(
541
					'type'     => 'text',
542
					'validate' => 'sanitize_validate_text',
543
					'desc'     => '',
544
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_KEY',
545
				),
546
547
			),
548
			'consumer_secret'                => array(
549
				'title'    => __( 'Consumer Secret', 'object-sync-for-salesforce' ),
550
				'callback' => $callbacks['text'],
551
				'page'     => $page,
552
				'section'  => $section,
553
				'args'     => array(
554
					'type'     => 'text',
555
					'validate' => 'sanitize_validate_text',
556
					'desc'     => '',
557
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CONSUMER_SECRET',
558
				),
559
			),
560
			'callback_url'                   => array(
561
				'title'    => __( 'Callback URL', 'object-sync-for-salesforce' ),
562
				'callback' => $callbacks['text'],
563
				'page'     => $page,
564
				'section'  => $section,
565
				'args'     => array(
566
					'type'     => 'url',
567
					'validate' => 'sanitize_validate_text',
568
					// translators: %1$s is the admin URL for the Authorize tab
569
					'desc'     => sprintf( __( 'In most cases, you will want to use %1$s for this value.', 'object-sync-for-salesforce' ),
570
						get_admin_url( null, 'options-general.php?page=object-sync-salesforce-admin&tab=authorize' )
571
					),
572
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_CALLBACK_URL',
573
				),
574
			),
575
			'login_base_url'                 => array(
576
				'title'    => __( 'Login Base URL', 'object-sync-for-salesforce' ),
577
				'callback' => $callbacks['text'],
578
				'page'     => $page,
579
				'section'  => $section,
580
				'args'     => array(
581
					'type'     => 'url',
582
					'validate' => 'sanitize_validate_text',
583
					// translators: 1) production salesforce login, 2) sandbox salesforce login
584
					'desc'     => sprintf( __( 'For most Salesforce setups, you should use %1$s for production and %2$s for sandbox.', 'object-sync-for-salesforce' ),
585
						esc_url( 'https://login.salesforce.com' ),
586
						esc_url( 'https://test.salesforce.com' )
587
					),
588
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_LOGIN_BASE_URL',
589
				),
590
			),
591
			'authorize_url_path'             => array(
592
				'title'    => __( 'Authorize URL Path', 'object-sync-for-salesforce' ),
593
				'callback' => $callbacks['text'],
594
				'page'     => $page,
595
				'section'  => $section,
596
				'args'     => array(
597
					'type'     => 'text',
598
					'validate' => 'sanitize_validate_text',
599
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
600
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_AUTHORIZE_URL_PATH',
601
					'default'  => $this->default_authorize_url_path,
602
				),
603
			),
604
			'token_url_path'                 => array(
605
				'title'    => __( 'Token URL Path', 'object-sync-for-salesforce' ),
606
				'callback' => $callbacks['text'],
607
				'page'     => $page,
608
				'section'  => $section,
609
				'args'     => array(
610
					'type'     => 'text',
611
					'validate' => 'sanitize_validate_text',
612
					'desc'     => __( 'For most Salesforce installs, this should not be changed.', 'object-sync-for-salesforce' ),
613
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_TOKEN_URL_PATH',
614
					'default'  => $this->default_token_url_path,
615
				),
616
			),
617
			'api_version'                    => array(
618
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
619
				'callback' => $callbacks['text'],
620
				'page'     => $page,
621
				'section'  => $section,
622
				'args'     => array(
623
					'type'     => 'text',
624
					'validate' => 'sanitize_validate_text',
625
					'desc'     => '',
626
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
627
					'default'  => $this->default_api_version,
628
				),
629
			),
630
			'object_filters'                 => array(
631
				'title'    => __( 'Limit Salesforce Objects', 'object-sync-for-salesforce' ),
632
				'callback' => $callbacks['checkboxes'],
633
				'page'     => $page,
634
				'section'  => $section,
635
				'args'     => array(
636
					'type'     => 'checkboxes',
637
					'validate' => 'sanitize_validate_text',
638
					'desc'     => __( 'Allows you to limit which Salesforce objects can be mapped', 'object-sync-for-salesforce' ),
639
					'items'    => array(
640
						'triggerable' => array(
641
							'text'    => __( 'Only Triggerable objects', 'object-sync-for-salesforce' ),
642
							'id'      => 'triggerable',
643
							'desc'    => '',
644
							'default' => $this->default_triggerable,
645
						),
646
						'updateable'  => array(
647
							'text'    => __( 'Only Updateable objects', 'object-sync-for-salesforce' ),
648
							'id'      => 'updateable',
649
							'desc'    => '',
650
							'default' => $this->default_updateable,
651
						),
652
					),
653
				),
654
			),
655
			'salesforce_field_display_value' => array(
656
				'title'    => __( 'Salesforce Field Display Value', 'object-sync-for-salesforce' ),
657
				'callback' => $callbacks['select'],
658
				'page'     => $page,
659
				'section'  => $section,
660
				'args'     => array(
661
					'type'     => 'select',
662
					'validate' => 'sanitize_validate_text',
663
					'desc'     => __( 'When choosing Salesforce fields to map, this value determines how the dropdown will identify Salesforce fields.', 'object-sync-for-salesforce' ),
664
					'constant' => '',
665
					'items'    => array(
666
						'field_label' => array(
667
							'text'  => __( 'Field Label', 'object-sync-for-salesforce' ),
668
							'value' => 'field_label',
669
						),
670
						/*'field_name'  => array(
671
							'text'  => __( 'Field Name', 'object-sync-for-salesforce' ),
672
							'value' => 'field_name',
673
						),*/
674
						'api_name'    => array(
675
							'text'  => __( 'API Name', 'object-sync-for-salesforce' ),
676
							'value' => 'api_name',
677
						),
678
					),
679
				),
680
			),
681
			'pull_query_limit'               => array(
682
				'title'    => __( 'Pull query record limit', 'object-sync-for-salesforce' ),
683
				'callback' => $callbacks['text'],
684
				'page'     => $page,
685
				'section'  => $section,
686
				'args'     => array(
687
					'type'     => 'number',
688
					'validate' => 'absint',
689
					'desc'     => __( 'Limit the number of records that can be pulled from Salesforce in a single query.', 'object-sync-for-salesforce' ),
690
					'constant' => '',
691
					'default'  => $this->default_pull_limit,
692
				),
693
			),
694
			'pull_throttle'                  => array(
695
				'title'    => __( 'Pull throttle (seconds)', 'object-sync-for-salesforce' ),
696
				'callback' => $callbacks['text'],
697
				'page'     => $page,
698
				'section'  => $section,
699
				'args'     => array(
700
					'type'     => 'number',
701
					'validate' => 'sanitize_validate_text',
702
					'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' ),
703
					'constant' => '',
704
					'default'  => $this->default_pull_throttle,
705
				),
706
			),
707
			'use_soap'                       => array(
708
				'title'    => __( 'Enable the Salesforce SOAP API?', 'object-sync-for-salesforce' ),
709
				'callback' => $callbacks['text'],
710
				'page'     => $page,
711
				'section'  => $section,
712
				'args'     => array(
713
					'type'     => 'checkbox',
714
					'validate' => 'sanitize_text_field',
715
					'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' ),
716
					'constant' => '',
717
				),
718
			),
719
			'soap_wsdl_path'                 => array(
720
				'title'    => __( 'Path to SOAP WSDL file', 'object-sync-for-salesforce' ),
721
				'callback' => $callbacks['text'],
722
				'page'     => $page,
723
				'section'  => $section,
724
				'args'     => array(
725
					'type'     => 'text',
726
					'validate' => 'sanitize_text_field',
727
					'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' ),
728
					'constant' => '',
729
				),
730
			),
731
			'debug_mode'                     => array(
732
				'title'    => __( 'Debug mode?', 'object-sync-for-salesforce' ),
733
				'callback' => $callbacks['text'],
734
				'page'     => $page,
735
				'section'  => $section,
736
				'args'     => array(
737
					'type'     => 'checkbox',
738
					'validate' => 'sanitize_text_field',
739
					'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' ),
740
					'constant' => '',
741
				),
742
			),
743
			'delete_data_on_uninstall'       => array(
744
				'title'    => __( 'Delete plugin data on uninstall?', 'object-sync-for-salesforce' ),
745
				'callback' => $callbacks['text'],
746
				'page'     => $page,
747
				'section'  => $section,
748
				'args'     => array(
749
					'type'     => 'checkbox',
750
					'validate' => 'sanitize_text_field',
751
					'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' ),
752
					'constant' => '',
753
				),
754
			),
755
		);
756
757
		if ( true === is_object( $this->salesforce['sfapi'] ) && true === $this->salesforce['sfapi']->is_authorized() ) {
758
			$salesforce_settings['api_version'] = array(
759
				'title'    => __( 'Salesforce API Version', 'object-sync-for-salesforce' ),
760
				'callback' => $callbacks['select'],
761
				'page'     => $page,
762
				'section'  => $section,
763
				'args'     => array(
764
					'type'     => 'select',
765
					'validate' => 'sanitize_text_field',
766
					'desc'     => '',
767
					'constant' => 'OBJECT_SYNC_SF_SALESFORCE_API_VERSION',
768
					'items'    => $this->version_options(),
769
				),
770
			);
771
		}
772
773
		foreach ( $salesforce_settings as $key => $attributes ) {
774
			$id       = $this->option_prefix . $key;
775
			$name     = $this->option_prefix . $key;
776
			$title    = $attributes['title'];
777
			$callback = $attributes['callback'];
778
			$validate = $attributes['args']['validate'];
779
			$page     = $attributes['page'];
780
			$section  = $attributes['section'];
781
			$args     = array_merge(
782
				$attributes['args'],
783
				array(
784
					'title'     => $title,
785
					'id'        => $id,
786
					'label_for' => $id,
787
					'name'      => $name,
788
				)
789
			);
790
791
			// if there is a constant and it is defined, don't run a validate function
792
			if ( isset( $attributes['args']['constant'] ) && defined( $attributes['args']['constant'] ) ) {
793
				$validate = '';
794
			}
795
796
			add_settings_field( $id, $title, $callback, $page, $section, $args );
797
			register_setting( $page, $id, array( $this, $validate ) );
798
		}
799
	}
800
801
	/**
802
	* Fields for the Fieldmaps tab
803
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
804
	*
805
	* @param string $page
806
	* @param string $section
807
	* @param string $input_callback
808
	*/
809
	private function fields_fieldmaps( $page, $section, $input_callback = '' ) {
810
		add_settings_section( $page, ucwords( $page ), null, $page );
811
	}
812
813
	/**
814
	* Fields for the Scheduling tab
815
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
816
	*
817
	* @param string $page
818
	* @param string $section
819
	* @param string $input_callback
820
	*/
821
	private function fields_scheduling( $page, $section, $callbacks ) {
822
823
		add_settings_section( 'batch', __( 'Batch Settings', 'object-sync-for-salesforce' ), null, $page );
824
		$section           = 'batch';
825
		$schedule_settings = array(
826
			'action_scheduler_batch_size'         => array(
827
				'title'    => __( 'Batch size', 'object-sync-for-salesforce' ),
828
				'callback' => $callbacks['text'],
829
				'page'     => $page,
830
				'section'  => $section,
831
				'args'     => array(
832
					'type'     => 'number',
833
					'validate' => 'absint',
834
					'default'  => 5,
835
					'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' ),
836
					'constant' => '',
837
				),
838
839
			),
840
			'action_scheduler_concurrent_batches' => array(
841
				'title'    => __( 'Concurrent batches', 'object-sync-for-salesforce' ),
842
				'callback' => $callbacks['text'],
843
				'page'     => $page,
844
				'section'  => $section,
845
				'args'     => array(
846
					'type'     => 'number',
847
					'validate' => 'absint',
848
					'default'  => 3,
849
					'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' ),
850
					'constant' => '',
851
				),
852
			),
853
		);
854
855
		foreach ( $this->schedulable_classes as $key => $value ) {
856
			add_settings_section( $key, $value['label'], null, $page );
857
			if ( isset( $value['initializer'] ) ) {
858
				$schedule_settings[ $key . '_schedule_number' ] = array(
859
					'title'    => __( 'Run schedule every', 'object-sync-for-salesforce' ),
860
					'callback' => $callbacks['text'],
861
					'page'     => $page,
862
					'section'  => $key,
863
					'args'     => array(
864
						'type'     => 'number',
865
						'validate' => 'absint',
866
						'desc'     => '',
867
						'constant' => '',
868
					),
869
				);
870
				$schedule_settings[ $key . '_schedule_unit' ]   = array(
871
					'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
872
					'callback' => $callbacks['select'],
873
					'page'     => $page,
874
					'section'  => $key,
875
					'args'     => array(
876
						'type'     => 'select',
877
						'validate' => 'sanitize_validate_text',
878
						'desc'     => '',
879
						'items'    => array(
880
							'minutes' => array(
881
								'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
882
								'value' => 'minutes',
883
							),
884
							'hours'   => array(
885
								'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
886
								'value' => 'hours',
887
							),
888
							'days'    => array(
889
								'text'  => __( 'Days', 'object-sync-for-salesforce' ),
890
								'value' => 'days',
891
							),
892
						),
893
					),
894
				);
895
			}
896
			$schedule_settings[ $key . '_clear_button' ] = array(
897
				// translators: $this->get_schedule_count is an integer showing how many items are in the current queue
898
				'title'    => sprintf( 'This queue has ' . _n( '%s item', '%s items', $this->get_schedule_count( $key ), 'object-sync-for-salesforce' ), $this->get_schedule_count( $key ) ),
899
				'callback' => $callbacks['link'],
900
				'page'     => $page,
901
				'section'  => $key,
902
				'args'     => array(
903
					'label'      => __( 'Clear this queue', 'object-sync-for-salesforce' ),
904
					'desc'       => '',
905
					'url'        => esc_url( '?page=object-sync-salesforce-admin&amp;tab=clear_schedule&amp;schedule_name=' . $key ),
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$key'
Loading history...
906
					'link_class' => 'button button-secondary',
907
				),
908
			);
909
			foreach ( $schedule_settings as $key => $attributes ) {
910
				$id       = $this->option_prefix . $key;
911
				$name     = $this->option_prefix . $key;
912
				$title    = $attributes['title'];
913
				$callback = $attributes['callback'];
914
				$page     = $attributes['page'];
915
				$section  = $attributes['section'];
916
				$args     = array_merge(
917
					$attributes['args'],
918
					array(
919
						'title'     => $title,
920
						'id'        => $id,
921
						'label_for' => $id,
922
						'name'      => $name,
923
					)
924
				);
925
				add_settings_field( $id, $title, $callback, $page, $section, $args );
926
				register_setting( $page, $id );
927
			}
928
		} // End foreach().
929
	}
930
931
	/**
932
	* Fields for the Log Settings tab
933
	* This runs add_settings_section once, as well as add_settings_field and register_setting methods for each option
934
	*
935
	* @param string $page
936
	* @param string $section
937
	* @param array $callbacks
938
	*/
939
	private function fields_log_settings( $page, $section, $callbacks ) {
940
		add_settings_section( $page, ucwords( str_replace( '_', ' ', $page ) ), null, $page );
941
		$log_settings = array(
942
			'enable_logging'        => array(
943
				'title'    => __( 'Enable Logging?', 'object-sync-for-salesforce' ),
944
				'callback' => $callbacks['text'],
945
				'page'     => $page,
946
				'section'  => $section,
947
				'args'     => array(
948
					'type'     => 'checkbox',
949
					'validate' => 'absint',
950
					'desc'     => '',
951
					'constant' => '',
952
				),
953
			),
954
			'statuses_to_log'       => array(
955
				'title'    => __( 'Statuses to log', 'object-sync-for-salesforce' ),
956
				'callback' => $callbacks['checkboxes'],
957
				'page'     => $page,
958
				'section'  => $section,
959
				'args'     => array(
960
					'type'     => 'checkboxes',
961
					'validate' => 'sanitize_validate_text',
962
					'desc'     => __( 'these are the statuses to log', 'object-sync-for-salesforce' ),
963
					'items'    => array(
964
						'error'   => array(
965
							'text' => __( 'Error', 'object-sync-for-salesforce' ),
966
							'id'   => 'error',
967
							'desc' => '',
968
						),
969
						'success' => array(
970
							'text' => __( 'Success', 'object-sync-for-salesforce' ),
971
							'id'   => 'success',
972
							'desc' => '',
973
						),
974
						'notice'  => array(
975
							'text' => __( 'Notice', 'object-sync-for-salesforce' ),
976
							'id'   => 'notice',
977
							'desc' => '',
978
						),
979
						'debug'   => array(
980
							'text' => __( 'Debug', 'object-sync-for-salesforce' ),
981
							'id'   => 'debug',
982
							'desc' => '',
983
						),
984
					),
985
				),
986
			),
987
			'prune_logs'            => array(
988
				'title'    => __( 'Automatically delete old log entries?', 'object-sync-for-salesforce' ),
989
				'callback' => $callbacks['text'],
990
				'page'     => $page,
991
				'section'  => $section,
992
				'args'     => array(
993
					'type'     => 'checkbox',
994
					'validate' => 'absint',
995
					'desc'     => '',
996
					'constant' => '',
997
				),
998
			),
999
			'logs_how_old'          => array(
1000
				'title'    => __( 'Age to delete log entries', 'object-sync-for-salesforce' ),
1001
				'callback' => $callbacks['text'],
1002
				'page'     => $page,
1003
				'section'  => $section,
1004
				'args'     => array(
1005
					'type'     => 'text',
1006
					'validate' => 'sanitize_validate_text',
1007
					'desc'     => __( 'If automatic deleting is enabled, it will affect logs this old.', 'object-sync-for-salesforce' ),
1008
					'default'  => '2 weeks',
1009
					'constant' => '',
1010
				),
1011
			),
1012
			'logs_how_often_number' => array(
1013
				'title'    => __( 'Check for old logs every', 'object-sync-for-salesforce' ),
1014
				'callback' => $callbacks['text'],
1015
				'page'     => $page,
1016
				'section'  => $section,
1017
				'args'     => array(
1018
					'type'     => 'number',
1019
					'validate' => 'absint',
1020
					'desc'     => '',
1021
					'default'  => '1',
1022
					'constant' => '',
1023
				),
1024
			),
1025
			'logs_how_often_unit'   => array(
1026
				'title'    => __( 'Time unit', 'object-sync-for-salesforce' ),
1027
				'callback' => $callbacks['select'],
1028
				'page'     => $page,
1029
				'section'  => $section,
1030
				'args'     => array(
1031
					'type'     => 'select',
1032
					'validate' => 'sanitize_validate_text',
1033
					'desc'     => __( 'These two fields are how often the site will check for logs to delete.', 'object-sync-for-salesforce' ),
1034
					'items'    => array(
1035
						'minutes' => array(
1036
							'text'  => __( 'Minutes', 'object-sync-for-salesforce' ),
1037
							'value' => 'minutes',
1038
						),
1039
						'hours'   => array(
1040
							'text'  => __( 'Hours', 'object-sync-for-salesforce' ),
1041
							'value' => 'hours',
1042
						),
1043
						'days'    => array(
1044
							'text'  => __( 'Days', 'object-sync-for-salesforce' ),
1045
							'value' => 'days',
1046
						),
1047
					),
1048
				),
1049
			),
1050
			'triggers_to_log'       => array(
1051
				'title'    => __( 'Triggers to log', 'object-sync-for-salesforce' ),
1052
				'callback' => $callbacks['checkboxes'],
1053
				'page'     => $page,
1054
				'section'  => $section,
1055
				'args'     => array(
1056
					'type'     => 'checkboxes',
1057
					'validate' => 'sanitize_validate_text',
1058
					'desc'     => __( 'these are the triggers to log', 'object-sync-for-salesforce' ),
1059
					'items'    => array(
1060
						$this->mappings->sync_wordpress_create => array(
1061
							'text' => __( 'WordPress create', 'object-sync-for-salesforce' ),
1062
							'id'   => 'wordpress_create',
1063
							'desc' => '',
1064
						),
1065
						$this->mappings->sync_wordpress_update => array(
1066
							'text' => __( 'WordPress update', 'object-sync-for-salesforce' ),
1067
							'id'   => 'wordpress_update',
1068
							'desc' => '',
1069
						),
1070
						$this->mappings->sync_wordpress_delete => array(
1071
							'text' => __( 'WordPress delete', 'object-sync-for-salesforce' ),
1072
							'id'   => 'wordpress_delete',
1073
							'desc' => '',
1074
						),
1075
						$this->mappings->sync_sf_create => array(
1076
							'text' => __( 'Salesforce create', 'object-sync-for-salesforce' ),
1077
							'id'   => 'sf_create',
1078
							'desc' => '',
1079
						),
1080
						$this->mappings->sync_sf_update => array(
1081
							'text' => __( 'Salesforce update', 'object-sync-for-salesforce' ),
1082
							'id'   => 'sf_update',
1083
							'desc' => '',
1084
						),
1085
						$this->mappings->sync_sf_delete => array(
1086
							'text' => __( 'Salesforce delete', 'object-sync-for-salesforce' ),
1087
							'id'   => 'sf_delete',
1088
							'desc' => '',
1089
						),
1090
					),
1091
				),
1092
			),
1093
		);
1094
		foreach ( $log_settings as $key => $attributes ) {
1095
			$id       = $this->option_prefix . $key;
1096
			$name     = $this->option_prefix . $key;
1097
			$title    = $attributes['title'];
1098
			$callback = $attributes['callback'];
1099
			$page     = $attributes['page'];
1100
			$section  = $attributes['section'];
1101
			$args     = array_merge(
1102
				$attributes['args'],
1103
				array(
1104
					'title'     => $title,
1105
					'id'        => $id,
1106
					'label_for' => $id,
1107
					'name'      => $name,
1108
				)
1109
			);
1110
			add_settings_field( $id, $title, $callback, $page, $section, $args );
1111
			register_setting( $page, $id );
1112
		}
1113
	}
1114
1115
	/**
1116
	* Create the notices, settings, and conditions by which admin notices should appear
1117
	*
1118
	*/
1119
	public function notices() {
1120
1121
		// before a notice is displayed, we should make sure we are on a page related to this plugin
1122
		if ( ! isset( $_GET['page'] ) || 'object-sync-salesforce-admin' !== $_GET['page'] ) {
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
1123
			return;
1124
		}
1125
1126
		$get_data = filter_input_array( INPUT_GET, FILTER_SANITIZE_STRING );
1127
		require_once plugin_dir_path( __FILE__ ) . '../classes/admin-notice.php';
1128
1129
		$notices = array(
1130
			'permission'              => array(
1131
				'condition'   => false === $this->check_wordpress_admin_permissions(),
1132
				'message'     => __( "Your account does not have permission to edit the Salesforce REST API plugin's settings.", 'object-sync-for-salesforce' ),
1133
				'type'        => 'error',
1134
				'dismissible' => false,
1135
			),
1136
			'fieldmap'                => array(
1137
				'condition'   => isset( $get_data['transient'] ),
1138
				'message'     => __( 'Errors kept this fieldmap from being saved.', 'object-sync-for-salesforce' ),
1139
				'type'        => 'error',
1140
				'dismissible' => true,
1141
			),
1142
			'object_map'              => array(
1143
				'condition'   => isset( $get_data['map_transient'] ),
1144
				'message'     => __( 'Errors kept this object map from being saved.', 'object-sync-for-salesforce' ),
1145
				'type'        => 'error',
1146
				'dismissible' => true,
1147
			),
1148
			'data_saved'              => array(
1149
				'condition'   => isset( $get_data['data_saved'] ) && 'true' === $get_data['data_saved'],
1150
				'message'     => __( 'This data was successfully saved.', 'object-sync-for-salesforce' ),
1151
				'type'        => 'success',
1152
				'dismissible' => true,
1153
			),
1154
			'data_save_error'         => array(
1155
				'condition'   => isset( $get_data['data_saved'] ) && 'false' === $get_data['data_saved'],
1156
				'message'     => __( 'This data was not successfully saved. Try again.', 'object-sync-for-salesforce' ),
1157
				'type'        => 'error',
1158
				'dismissible' => true,
1159
			),
1160
			'mapping_error_transient' => array(
1161
				'condition'   => isset( $get_data['mapping_error_transient'] ),
1162
				'message'     => __( 'Errors kept these mapping errors from being deleted.', 'object-sync-for-salesforce' ),
1163
				'type'        => 'error',
1164
				'dismissible' => true,
1165
			),
1166
		);
1167
1168
		foreach ( $notices as $key => $value ) {
1169
1170
			$condition = $value['condition'];
1171
			$message   = $value['message'];
1172
1173
			if ( isset( $value['dismissible'] ) ) {
1174
				$dismissible = $value['dismissible'];
1175
			} else {
1176
				$dismissible = false;
1177
			}
1178
1179
			if ( isset( $value['type'] ) ) {
1180
				$type = $value['type'];
1181
			} else {
1182
				$type = '';
1183
			}
1184
1185
			if ( ! isset( $value['template'] ) ) {
1186
				$template = '';
1187
			}
1188
1189
			if ( $condition ) {
1190
				new Object_Sync_Sf_Admin_Notice( $condition, $message, $dismissible, $type, $template );
1191
			}
1192
		}
1193
1194
	}
1195
1196
	/**
1197
	* Get all the Salesforce object settings for fieldmapping
1198
	* This takes either the $_POST array via ajax, or can be directly called with a $data array
1199
	*
1200
	* @param array $data
1201
	* data must contain a salesforce_object
1202
	* can optionally contain a type
1203
	* @return array $object_settings
1204
	*/
1205
	public function get_salesforce_object_description( $data = array() ) {
1206
		$ajax = false;
1207
		if ( empty( $data ) ) {
1208
			$data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1209
			$ajax = true;
1210
		}
1211
1212
		$object_description = array();
1213
1214
		if ( ! empty( $data['salesforce_object'] ) ) {
1215
			$object = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1216
1217
			$object_fields        = array();
1218
			$include_record_types = array();
1219
1220
			// these can come from ajax
1221
			$include = isset( $data['include'] ) ? (array) $data['include'] : array();
1222
			$include = array_map( 'esc_attr', $include );
1223
1224
			if ( in_array( 'fields', $include, true ) || empty( $include ) ) {
1225
				$type = isset( $data['field_type'] ) ? esc_attr( $data['field_type'] ) : ''; // can come from ajax
1226
				foreach ( $object['data']['fields'] as $key => $value ) {
1227
					if ( '' === $type || $type === $value['type'] ) {
1228
						$object_fields[ $key ] = $value;
1229
					}
1230
				}
1231
				$object_description['fields'] = $object_fields;
1232
			}
1233
1234
			if ( in_array( 'recordTypeInfos', $include, true ) ) {
1235
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1236
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1237
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1238
					}
1239
					$object_description['recordTypeInfos'] = $object_record_types;
1240
				}
1241
			}
1242
		}
1243
1244
		if ( true === $ajax ) {
1245
			wp_send_json_success( $object_description );
1246
		} else {
1247
			return $object_description;
1248
		}
1249
	}
1250
1251
	/**
1252
	* Get Salesforce object fields for fieldmapping
1253
	*
1254
	* @param array $data
1255
	* data must contain a salesforce_object
1256
	* can optionally contain a type for the field
1257
	* @return array $object_fields
1258
	*/
1259
	public function get_salesforce_object_fields( $data = array() ) {
1260
1261
		if ( ! empty( $data['salesforce_object'] ) ) {
1262
			$object               = $this->salesforce['sfapi']->object_describe( esc_attr( $data['salesforce_object'] ) );
1263
			$object_fields        = array();
1264
			$type                 = isset( $data['type'] ) ? esc_attr( $data['type'] ) : '';
1265
			$include_record_types = isset( $data['include_record_types'] ) ? esc_attr( $data['include_record_types'] ) : false;
1266
			foreach ( $object['data']['fields'] as $key => $value ) {
1267
				if ( '' === $type || $type === $value['type'] ) {
1268
					$object_fields[ $key ] = $value;
1269
				}
1270
			}
1271
			if ( true === $include_record_types ) {
0 ignored issues
show
introduced by
The condition true === $include_record_types is always false.
Loading history...
1272
				$object_record_types = array();
1273
				if ( isset( $object['data']['recordTypeInfos'] ) && count( $object['data']['recordTypeInfos'] ) > 1 ) {
1274
					foreach ( $object['data']['recordTypeInfos'] as $type ) {
1275
						$object_record_types[ $type['recordTypeId'] ] = $type['name'];
1276
					}
1277
				}
1278
			}
1279
		}
1280
1281
		return $object_fields;
1282
1283
	}
1284
1285
	/**
1286
	* Get WordPress object fields for fieldmapping
1287
	* This takes either the $_POST array via ajax, or can be directly called with a $wordpress_object field
1288
	*
1289
	* @param string $wordpress_object
1290
	* @return array $object_fields
1291
	*/
1292
	public function get_wordpress_object_fields( $wordpress_object = '' ) {
1293
		$ajax      = false;
1294
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1295
		if ( empty( $wordpress_object ) ) {
1296
			$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

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

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

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

1385
			$wordpress_object = isset( $post_data['wordpress_object'] ) ? sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $post_data['wordpress_object'] ) ) : '';
Loading history...
1386
			$salesforce_id    = isset( $post_data['salesforce_id'] ) ? sanitize_text_field( wp_unslash( $post_data['salesforce_id'] ) ) : '';
1387
		}
1388
		$type   = $this->salesforce['sfapi']->get_sobject_type( $salesforce_id );
1389
		$result = $this->pull->manual_pull( $type, $salesforce_id, $wordpress_object ); // we want the wp object to make sure we get the right fieldmap
1390
		if ( ! empty( $post_data ) ) {
1391
			wp_send_json_success( $result );
1392
		} else {
1393
			return $result;
1394
		}
1395
	}
1396
1397
	/**
1398
	* Manually pull the Salesforce object into WordPress
1399
	* This takes an id for a mapping object row
1400
	*
1401
	* @param int $mapping_id
1402
	*/
1403
	public function refresh_mapped_data( $mapping_id = '' ) {
1404
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1405
		if ( empty( $mapping_id ) ) {
1406
			$mapping_id = isset( $post_data['mapping_id'] ) ? absint( $post_data['mapping_id'] ) : '';
1407
		}
1408
		$result = $this->mappings->get_object_maps(
1409
			array(
1410
				'id' => $mapping_id,
1411
			)
1412
		);
1413
		if ( ! empty( $post_data ) ) {
1414
			wp_send_json_success( $result );
1415
		} else {
1416
			return $result;
1417
		}
1418
	}
1419
1420
	/**
1421
	* Prepare fieldmap data and redirect after processing
1422
	* This runs when the create or update forms are submitted
1423
	* It is public because it depends on an admin hook
1424
	* It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1425
	* This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1426
	*
1427
	*/
1428
	public function prepare_fieldmap_data() {
1429
		$error     = false;
1430
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1431
		$cachekey  = md5( wp_json_encode( $post_data ) );
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($post_data) can also be of type false; however, parameter $str of md5() 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

1431
		$cachekey  = md5( /** @scrutinizer ignore-type */ wp_json_encode( $post_data ) );
Loading history...
1432
1433
		if ( ! isset( $post_data['label'] ) || ! isset( $post_data['salesforce_object'] ) || ! isset( $post_data['wordpress_object'] ) ) {
1434
			$error = true;
1435
		}
1436
		if ( true === $error ) {
1437
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1438
			if ( '' !== $cachekey ) {
1439
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1440
			}
1441
		} else { // there are no errors
1442
			// send the row to the fieldmap class
1443
			// if it is add or clone, use the create method
1444
			$method            = esc_attr( $post_data['method'] );
1445
			$salesforce_fields = $this->get_salesforce_object_fields(
1446
				array(
1447
					'salesforce_object' => $post_data['salesforce_object'],
1448
				)
1449
			);
1450
			$wordpress_fields  = $this->get_wordpress_object_fields( $post_data['wordpress_object'] );
1451
			if ( 'add' === $method || 'clone' === $method ) {
1452
				$result = $this->mappings->create_fieldmap( $post_data, $wordpress_fields, $salesforce_fields );
1453
			} elseif ( 'edit' === $method ) { // if it is edit, use the update method
1454
				$id     = esc_attr( $post_data['id'] );
1455
				$result = $this->mappings->update_fieldmap( $post_data, $wordpress_fields, $salesforce_fields, $id );
1456
			}
1457
			if ( false === $result ) { // if the database didn't save, it's still an error
1458
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1459
				if ( '' !== $cachekey ) {
1460
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&transient=' . $cachekey;
1461
				}
1462
			} else {
1463
				if ( isset( $post_data['transient'] ) ) { // there was previously an error saved. can delete it now.
1464
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1465
				}
1466
				// then send the user to the list of fieldmaps
1467
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1468
			}
1469
		}
1470
		wp_safe_redirect( $url );
1471
		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...
1472
	}
1473
1474
	/**
1475
	* Delete fieldmap data and redirect after processing
1476
	* This runs when the delete link is clicked, after the user confirms
1477
	* It is public because it depends on an admin hook
1478
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1479
	*
1480
	*/
1481
	public function delete_fieldmap() {
1482
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1483
		if ( $post_data['id'] ) {
1484
			$result = $this->mappings->delete_fieldmap( $post_data['id'] );
1485
			if ( true === $result ) {
1486
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1487
			} else {
1488
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1489
			}
1490
			wp_safe_redirect( $url );
1491
			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...
1492
		}
1493
	}
1494
1495
	/**
1496
	* Prepare object data and redirect after processing
1497
	* This runs when the update form is submitted
1498
	* It is public because it depends on an admin hook
1499
	* It then calls the Object_Sync_Sf_Mapping class and sends prepared data over to it, then redirects to the correct page
1500
	* This method does include error handling, by loading the submission in a transient if there is an error, and then deleting it upon success
1501
	*
1502
	*/
1503
	public function prepare_object_map_data() {
1504
		$error     = false;
1505
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1506
		$cachekey  = md5( wp_json_encode( $post_data ) );
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($post_data) can also be of type false; however, parameter $str of md5() 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

1506
		$cachekey  = md5( /** @scrutinizer ignore-type */ wp_json_encode( $post_data ) );
Loading history...
1507
1508
		if ( ! isset( $post_data['wordpress_id'] ) || ! isset( $post_data['salesforce_id'] ) ) {
1509
			$error = true;
1510
		}
1511
		if ( true === $error ) {
1512
			$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1513
			if ( '' !== $cachekey ) {
1514
				$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1515
			}
1516
		} else { // there are no errors
1517
			// send the row to the object map class
1518
			$method = esc_attr( $post_data['method'] );
1519
			if ( 'edit' === $method ) { // if it is edit, use the update method
1520
				$id     = esc_attr( $post_data['id'] );
1521
				$result = $this->mappings->update_object_map( $post_data, $id );
1522
			}
1523
			if ( false === $result ) { // if the database didn't save, it's still an error
1524
				$this->sfwp_transients->set( $cachekey, $post_data, $this->wordpress->options['cache_expiration'] );
1525
				if ( '' !== $cachekey ) {
1526
					$url = esc_url_raw( $post_data['redirect_url_error'] ) . '&map_transient=' . $cachekey;
1527
				}
1528
			} else {
1529
				if ( isset( $post_data['map_transient'] ) ) { // there was previously an error saved. can delete it now.
1530
					$this->sfwp_transients->delete( esc_attr( $post_data['map_transient'] ) );
1531
				}
1532
				// then send the user to the success redirect url
1533
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1534
			}
1535
		}
1536
		wp_safe_redirect( $url );
1537
		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...
1538
	}
1539
1540
	/**
1541
	* Delete object map data and redirect after processing
1542
	* This runs when the delete link is clicked on an error row, after the user confirms
1543
	* It is public because it depends on an admin hook
1544
	* It then calls the Object_Sync_Sf_Mapping class and the delete method
1545
	*
1546
	*/
1547
	public function delete_object_map() {
1548
		$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1549
		if ( isset( $post_data['id'] ) ) {
1550
			$result = $this->mappings->delete_object_map( $post_data['id'] );
1551
			if ( true === $result ) {
1552
				$url = esc_url_raw( $post_data['redirect_url_success'] );
1553
			} else {
1554
				$url = esc_url_raw( $post_data['redirect_url_error'] . '&id=' . $post_data['id'] );
1555
			}
1556
			wp_safe_redirect( $url );
1557
			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...
1558
		} elseif ( $post_data['delete'] ) {
1559
			$post_data = filter_input_array( INPUT_POST, FILTER_SANITIZE_STRING );
1560
			$cachekey  = md5( wp_json_encode( $post_data ) );
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($post_data) can also be of type false; however, parameter $str of md5() 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

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

1721
		echo /** @scrutinizer ignore-type */ wp_json_encode( $export );
Loading history...
1722
		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...
1723
	}
1724
1725
	/**
1726
	* Default display for <input> fields
1727
	*
1728
	* @param array $args
1729
	*/
1730
	public function display_input_field( $args ) {
1731
		$type    = $args['type'];
1732
		$id      = $args['label_for'];
1733
		$name    = $args['name'];
1734
		$desc    = $args['desc'];
1735
		$checked = '';
1736
1737
		$class = 'regular-text';
1738
1739
		if ( 'checkbox' === $type ) {
1740
			$class = 'checkbox';
1741
		}
1742
1743
		if ( ! isset( $args['constant'] ) || ! defined( $args['constant'] ) ) {
1744
			$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

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