Passed
Pull Request — master (#386)
by Jonathan
03:05
created

Object_Sync_Sf_Activate::require_ssl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 0
dl 0
loc 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * What to do when the plugin is activated
4
 *
5
 * @class   Object_Sync_Sf_Activate
6
 * @package Object_Sync_Salesforce
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Object_Sync_Sf_Activate class.
13
 */
14
class Object_Sync_Sf_Activate {
15
16
	/**
17
	 * Current version of the plugin
18
	 *
19
	 * @var string
20
	 */
21
	public $version;
22
23
	/**
24
	 * The main plugin file
25
	 *
26
	 * @var string
27
	 */
28
	public $file;
29
30
	/**
31
	 * Global object of `$wpdb`, the WordPress database
32
	 *
33
	 * @var object
34
	 */
35
	public $wpdb;
36
37
	/**
38
	 * The plugin's slug so we can include it when necessary
39
	 *
40
	 * @var string
41
	 */
42
	public $slug;
43
44
	/**
45
	 * The plugin's prefix when saving options to the database
46
	 *
47
	 * @var string
48
	 */
49
	public $option_prefix;
50
51
	/**
52
	 * Suffix for group name in ActionScheduler
53
	 *
54
	 * @var string
55
	 */
56
	public $action_group_suffix;
57
58
	/**
59
	 * Array of what classes in the plugin can be scheduled to occur with `wp_cron` events
60
	 *
61
	 * @var array
62
	 */
63
	public $schedulable_classes;
64
65
	/**
66
	 * Object_Sync_Sf_Queue class
67
	 *
68
	 * @var object
69
	 */
70
	public $queue;
71
72
	/**
73
	 * The version of this plugin's database setup
74
	 *
75
	 * @var string
76
	 */
77
	public $user_installed_version;
78
79
	/**
80
	 * Constructor for activate class
81
	 */
82
	public function __construct() {
83
		$this->version             = object_sync_for_salesforce()->version;
84
		$this->file                = object_sync_for_salesforce()->file;
85
		$this->wpdb                = object_sync_for_salesforce()->wpdb;
86
		$this->slug                = object_sync_for_salesforce()->slug;
87
		$this->option_prefix       = object_sync_for_salesforce()->option_prefix;
88
		$this->action_group_suffix = object_sync_for_salesforce()->action_group_suffix;
89
90
		$this->schedulable_classes = object_sync_for_salesforce()->schedulable_classes;
91
		$this->queue               = object_sync_for_salesforce()->queue;
92
93
		// database version.
94
		$this->user_installed_version = get_option( $this->option_prefix . 'db_version', '' );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_option($this->option...fix . 'db_version', '') can also be of type false. However, the property $user_installed_version is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
95
96
		$this->add_actions();
97
	}
98
99
	/**
100
	 * Activation hooks
101
	 */
102
	private function add_actions() {
103
104
		// on initial activation, run these hooks.
105
		register_activation_hook( dirname( __DIR__ ) . '/' . $this->slug . '.php', array( $this, 'php_requirements' ) );
106
		// to maybe add later:
107
		// register_activation_hook( dirname( __DIR__ ) . '/' . $this->slug . '.php', array( $this, 'require_ssl' ) );
108
		// if we determine we need to check for SSL on activation.
109
		register_activation_hook( dirname( __DIR__ ) . '/' . $this->slug . '.php', array( $this, 'wordpress_salesforce_tables' ) );
110
		register_activation_hook( dirname( __DIR__ ) . '/' . $this->slug . '.php', array( $this, 'add_roles_capabilities' ) );
111
112
		// this should run when the user is in the admin area to make sure the database gets updated.
113
		add_action( 'admin_init', array( $this, 'wordpress_salesforce_update_db_check' ), 10 );
114
115
		// when users upgrade the plugin, run these hooks.
116
		add_action( 'upgrader_process_complete', array( $this, 'check_for_action_scheduler' ), 10, 2 );
117
	}
118
119
	/**
120
	 * Check for the minimum required version of php
121
	 */
122
	public function php_requirements() {
123
		if ( version_compare( PHP_VERSION, '5.6.20', '<' ) ) {
124
			deactivate_plugins( plugin_basename( __FILE__ ) );
125
			wp_die( '<strong>This plugin requires PHP Version 5.6.20</strong> <br />Please contact your host to upgrade PHP on your server, and then retry activating the plugin.' );
126
		}
127
	}
128
129
	/**
130
	 * Require SSL because otherwise the plugin will not authorize
131
	 */
132
	public function require_ssl() {
133
		// although we might instead have to run this on plugin initalization rather than activation.
134
	}
135
136
	/**
137
	 * Create database tables for Salesforce
138
	 * This creates tables for fieldmaps (between types of objects) and object maps (between individual instances of objects).
139
	 * Important requirement for developers: when you update the SQL for either of these tables, also update it in the documentation file located at /docs/troubleshooting-unable-to-create-database-tables.md and make sure that is included in your commit(s).
140
	 */
141
	public function wordpress_salesforce_tables() {
142
143
		$charset_collate = $this->wpdb->get_charset_collate();
144
145
		$field_map_table = $this->wpdb->prefix . 'object_sync_sf_field_map';
146
		$field_map_sql   = "CREATE TABLE $field_map_table (
147
			id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
148
			label varchar(64) NOT NULL DEFAULT '',
149
			name varchar(64) NOT NULL DEFAULT '',
150
			wordpress_object varchar(128) NOT NULL DEFAULT '',
151
			salesforce_object varchar(255) NOT NULL DEFAULT '',
152
			salesforce_record_types_allowed longblob,
153
			salesforce_record_type_default varchar(255) NOT NULL DEFAULT '',
154
			fields longtext NOT NULL,
155
			pull_trigger_field varchar(128) NOT NULL DEFAULT 'LastModifiedDate',
156
			sync_triggers text NOT NULL,
157
			push_async tinyint(1) NOT NULL DEFAULT '0',
158
			push_drafts tinyint(1) NOT NULL DEFAULT '0',
159
			pull_to_drafts tinyint(1) NOT NULL DEFAULT '0',
160
			weight tinyint(1) NOT NULL DEFAULT '0',
161
			version varchar(255) NOT NULL DEFAULT '',
162
			PRIMARY KEY  (id),
163
			UNIQUE KEY name (name),
164
			KEY name_sf_type_wordpress_type (wordpress_object,salesforce_object)
165
		) ENGINE=InnoDB $charset_collate";
166
167
		$object_map_table = $this->wpdb->prefix . 'object_sync_sf_object_map';
168
		$object_map_sql   = "CREATE TABLE $object_map_table (
169
			id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
170
			wordpress_id varchar(32) NOT NULL,
171
			salesforce_id varbinary(32) NOT NULL DEFAULT '',
172
			wordpress_object varchar(128) NOT NULL DEFAULT '',
173
			created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
174
			object_updated datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
175
			last_sync datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
176
			last_sync_action varchar(128) DEFAULT NULL,
177
			last_sync_status tinyint(1) NOT NULL DEFAULT '0',
178
			last_sync_message varchar(255) DEFAULT NULL,
179
			PRIMARY KEY  (id),
180
			KEY wordpress_object (wordpress_object,wordpress_id),
181
			KEY salesforce_object (salesforce_id)
182
		) $charset_collate";
183
184
		if ( ! function_exists( 'dbDelta' ) ) {
185
			if ( ! is_admin() ) {
186
				return false;
187
			}
188
			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
189
		}
190
191
		// Note: see https://wordpress.stackexchange.com/questions/67345/how-to-implement-wordpress-plugin-update-that-modifies-the-database
192
		// When we run the dbDelta method below, "it checks if the table exists. What's more, it checks the column types. So if the table doesn't exist, it creates it, if it does, but some column types have changed it updates them, and if a column doesn't exists - it adds it."
193
		// This does not remove columns if we remove columns, so we'll need to expand beyond this in the future if that happens, although I think the schema is pretty solid now.
194
		$result_field_map  = dbDelta( $field_map_sql );
195
		$result_object_map = dbDelta( $object_map_sql );
196
197
		$remove_key_version = '1.8.0';
198
		if ( '' !== $this->user_installed_version && version_compare( $this->user_installed_version, $remove_key_version, '<' ) ) {
199
			$wpdb = $this->wpdb;
200
			$wpdb->query( $wpdb->prepare( 'ALTER TABLE %s DROP INDEX salesforce', $object_map_table ) );
201
			$wpdb->query( $wpdb->prepare( 'ALTER TABLE %s DROP INDEX salesforce_wordpress', $object_map_table ) );
202
			$result_key = true;
203
		}
204
205
		// store right now as the time for the plugin's activation.
206
		update_option( $this->option_prefix . 'activate_time', time() );
207
208
		// utf8mb4 conversion.
209
		maybe_convert_table_to_utf8mb4( $field_map_table );
210
		maybe_convert_table_to_utf8mb4( $object_map_table );
211
212
		if ( '' === $this->user_installed_version || version_compare( $this->user_installed_version, $this->version, '<' ) ) {
213
			update_option( $this->option_prefix . 'db_version', $this->version );
214
		}
215
216
		if ( ! isset( $result_key ) && empty( $result_field_map ) && empty( $result_object_map ) ) {
217
			// No changes, database already exists and is up-to-date.
218
			return;
219
		}
220
221
	}
222
223
	/**
224
	 * Add roles and capabilities
225
	 * This adds the configure_salesforce capability to the admin role
226
	 *
227
	 * It also allows other plugins to add the capability to other roles
228
	 */
229
	public function add_roles_capabilities() {
230
231
		// by default, only administrators can configure the plugin.
232
		$role = get_role( 'administrator' );
233
		$role->add_cap( 'configure_salesforce' );
234
235
		// hook that allows other roles to configure the plugin as well.
236
		$roles = apply_filters( $this->option_prefix . 'roles_configure_salesforce', null );
237
238
		// for each role that we have, give it the configure salesforce capability.
239
		if ( null !== $roles ) {
240
			foreach ( $roles as $role ) {
241
				$role = get_role( $role );
242
				$role->add_cap( 'configure_salesforce' );
243
			}
244
		}
245
246
	}
247
248
	/**
249
	 * Check for database version
250
	 * When the plugin is loaded in the admin, if the database version does not match the current version, perform these methods
251
	 */
252
	public function wordpress_salesforce_update_db_check() {
253
254
		// user is running a version less than the current one.
255
		if ( version_compare( $this->user_installed_version, $this->version, '<' ) ) {
256
			$this->log_trigger_settings();
257
			$this->wordpress_salesforce_tables();
258
		} else {
259
			return true;
260
		}
261
	}
262
263
	/**
264
	 * Check for log trigger settings based on plugin version
265
	 * When the plugin is loaded in the admin, if the previously installed version is below 2.0.0, update the values for the log trigger settings.
266
	 */
267
	private function log_trigger_settings() {
268
		$triggers_to_log = get_option( $this->option_prefix . 'triggers_to_log', array() );
269
		if ( ! empty( $triggers_to_log ) ) {
270
			// in version 2.0.0 of this plugin, we replaced the bit flags with strings to make them more legible.
271
			// when the database version changes to 2.0.0, we should update the option value to use the new strings.
272
			if ( version_compare( $this->version, '2.0.0', '==' ) && version_compare( $this->user_installed_version, '2.0.0', '<' ) ) {
273
				$mappings                = new Object_Sync_Sf_Mapping();
274
				$updated_triggers_to_log = array();
275
				foreach ( $triggers_to_log as $key => $value ) {
276
					if ( (string) $value === (string) $mappings->sync_off_v1 ) {
277
						$updated_triggers_to_log[] = $mappings->sync_off;
278
					}
279
					if ( (string) $value === (string) $mappings->sync_wordpress_create_v1 ) {
280
						$updated_triggers_to_log[] = $mappings->sync_wordpress_create;
281
					}
282
					if ( (string) $value === (string) $mappings->sync_wordpress_update_v1 ) {
283
						$updated_triggers_to_log[] = $mappings->sync_wordpress_update;
284
					}
285
					if ( (string) $value === (string) $mappings->sync_wordpress_delete_v1 ) {
286
						$updated_triggers_to_log[] = $mappings->sync_wordpress_delete;
287
					}
288
					if ( (string) $value === (string) $mappings->sync_sf_create_v1 ) {
289
						$updated_triggers_to_log[] = $mappings->sync_sf_create;
290
					}
291
					if ( (string) $value === (string) $mappings->sync_sf_update_v1 ) {
292
						$updated_triggers_to_log[] = $mappings->sync_sf_update;
293
					}
294
					if ( (string) $value === (string) $mappings->sync_sf_delete_v1 ) {
295
						$updated_triggers_to_log[] = $mappings->sync_sf_delete;
296
					}
297
				}
298
				$triggers_to_log = $updated_triggers_to_log;
299
				update_option( $this->option_prefix . 'triggers_to_log', $triggers_to_log );
300
			}
301
		}
302
	}
303
304
	/**
305
	 * Check whether the user has action scheduler tasks when they upgrade
306
	 *
307
	 * @param object $upgrader_object this is the WP_Upgrader object.
308
	 * @param array  $hook_extra the array of bulk item update data.
309
	 * @see https://developer.wordpress.org/reference/hooks/upgrader_process_complete/
310
	 * @deprecated since 2.0.0.
311
	 */
312
	public function check_for_action_scheduler( $upgrader_object, $hook_extra ) {
313
314
		// skip if this action isn't this plugin being updated.
315
		if ( 'plugin' !== $hook_extra['type'] && 'update' !== $hook_extra['action'] && $hook_extra['plugin'] !== $this->slug ) {
316
			return;
317
		}
318
319
		// user is running a version of this plugin that is less than 1.4.0.
320
		$action_scheduler_version = '1.4.0';
321
		$previous_version         = get_transient( $this->option_prefix . 'installed_version' );
322
		if ( version_compare( $previous_version, $action_scheduler_version, '<' ) ) {
323
			// delete old options.
324
			delete_option( $this->option_prefix . 'push_schedule_number' );
325
			delete_option( $this->option_prefix . 'push_schedule_unit' );
326
			delete_option( $this->option_prefix . 'salesforce_schedule_number' );
327
			delete_option( $this->option_prefix . 'salesforce_schedule_unit' );
328
			if ( '' === $this->queue ) {
0 ignored issues
show
introduced by
The condition '' === $this->queue is always false.
Loading history...
329
				delete_transient( $this->option_prefix . 'installed_version' );
330
				return;
331
			}
332
			foreach ( $this->schedulable_classes as $key => $schedule ) {
333
				$schedule_name     = $key;
334
				$action_group_name = $schedule_name . $this->action_group_suffix;
335
				// exit if there is no initializer property on this schedule.
336
				if ( ! isset( $this->schedulable_classes[ $schedule_name ]['initializer'] ) ) {
337
					continue;
338
				}
339
				// create new recurring task for ActionScheduler to check for data to pull from Salesforce.
340
				$this->queue->schedule_recurring(
341
					time(), // plugin seems to expect UTC.
342
					$this->queue->get_frequency( $schedule_name, 'seconds' ),
343
					$this->schedulable_classes[ $schedule_name ]['initializer'],
344
					array(),
345
					$action_group_name
346
				);
347
			}
348
			delete_transient( $this->option_prefix . 'installed_version' );
349
		}
350
	}
351
352
}
353