GetPaid_Admin_Setup_Wizard   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 397
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 139
dl 0
loc 397
rs 8.96
c 0
b 0
f 0
wmc 43

23 Methods

Rating   Name   Duplication   Size   Complexity  
A display_footer() 0 9 3
A setup_wizard() 0 7 3
A display_wizard() 0 4 1
A display_header() 0 6 1
A get_setup_steps() 0 43 1
A setup_payments_save() 0 12 2
A setup_currency() 0 6 1
A setup_recommend() 0 4 1
A display_current_step() 0 6 1
A remove_deprecated_functions() 0 4 1
A setup_ready() 0 2 1
A maybe_save_current_step() 0 3 3
A setup_business() 0 6 1
A get_previous_step() 0 13 3
A setup_payments() 0 3 1
A get_current_step() 0 3 4
A add_menu() 0 2 1
A __construct() 0 6 3
A get_next_step() 0 16 4
A get_next_step_link() 0 16 4
A get_recommend_wp_plugins() 0 22 1
A setup_introduction() 0 3 1
A setup_globals() 0 5 1

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
/**
3
 * Setup Wizard Class
4
 *
5
 * Takes new users through some basic steps to setup GetPaid.
6
 *
7
 * @author      AyeCode
8
 * @category    Admin
9
 * @package     GetPaid/Admin
10
 * @version     2.4.0
11
 * @info        GetPaid Setup Wizard.
12
 */
13
defined( 'ABSPATH' ) || exit;
14
15
/**
16
 * GetPaid_Admin_Setup_Wizard class.
17
 */
18
class GetPaid_Admin_Setup_Wizard {
19
20
	/**
21
	 * @var string Current Step
22
	 */
23
	protected $step = '';
24
25
	/**
26
	 * @var string|false Previous Step
27
	 */
28
	protected $previous_step = '';
29
30
	/**
31
	 * @var string|false Next Step
32
	 */
33
	protected $next_step = '';
34
35
	/**
36
	 * @var array All available steps for the setup wizard
37
	 */
38
	protected $steps = array();
39
40
	/**
41
	 * Class constructor.
42
	 *
43
	 * @since 2.4.0
44
	 */
45
	public function __construct() {
46
47
		if ( apply_filters( 'getpaid_enable_setup_wizard', true ) && wpinv_current_user_can_manage_invoicing() ) {
48
			add_action( 'admin_menu', array( $this, 'add_menu' ) );
49
			add_action( 'current_screen', array( $this, 'setup_wizard' ) );
50
			add_action( 'admin_init', array( $this, 'remove_deprecated_functions' ) );
51
		}
52
53
	}
54
55
	/**
56
	 * Add admin menus/screens.
57
	 *
58
	 * @since 2.4.0
59
	 */
60
	public function add_menu() {
61
		add_dashboard_page( '', '', wpinv_get_capability(), 'gp-setup', '' );
62
	}
63
64
	/**
65
	 * Sets up the setup wizard.
66
	 *
67
	 * @since 2.4.0
68
	 */
69
	public function setup_wizard() {
70
71
		if ( isset( $_GET['page'] ) && 'gp-setup' === $_GET['page'] ) {
72
			$this->setup_globals();
73
			$this->maybe_save_current_step();
74
			$this->display_wizard();
75
			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...
76
		}
77
78
	}
79
80
	public function remove_deprecated_functions() {
81
		// removes deprecated warnings from page
82
		remove_action('admin_print_styles', 'print_emoji_styles');
83
		remove_action( 'admin_head', 'wp_admin_bar_header' );
84
	}
85
86
	/**
87
	 * Sets up class variables.
88
	 *
89
	 * @since 2.4.0
90
	 */
91
	protected function setup_globals() {
92
		$this->steps         = $this->get_setup_steps();
93
		$this->step          = $this->get_current_step();
94
		$this->previous_step = $this->get_previous_step();
95
		$this->next_step     = $this->get_next_step();
96
	}
97
98
	/**
99
	 * Saves the current step.
100
	 *
101
	 * @since 2.4.0
102
	 */
103
	protected function maybe_save_current_step() {
104
		if ( ! empty( $_POST['save_step'] ) && is_callable( $this->steps[ $this->step ]['handler'] ) ) {
105
			call_user_func( $this->steps[ $this->step ]['handler'], $this );
106
		}
107
	}
108
109
	/**
110
	 * Returns the setup steps.
111
	 *
112
	 * @since 2.4.0
113
	 * @return array
114
	 */
115
	protected function get_setup_steps() {
116
117
		$steps = array(
118
119
			'introduction'     => array(
120
				'name'    => __( 'Introduction', 'invoicing' ),
121
				'view'    => array( $this, 'setup_introduction' ),
122
				'handler' => '',
123
			),
124
125
			'business_details' => array(
126
				'name'    => __( 'Business Details', 'invoicing' ),
127
				'view'    => array( $this, 'setup_business' ),
128
				'handler' => '',
129
			),
130
131
			'currency'         => array(
132
				'name'    => __( 'Currency', 'invoicing' ),
133
				'view'    => array( $this, 'setup_currency' ),
134
				'handler' => '',
135
			),
136
137
			'payments'         => array(
138
				'name'    => __( 'Payment Gateways', 'invoicing' ),
139
				'view'    => array( $this, 'setup_payments' ),
140
				'handler' => array( $this, 'setup_payments_save' ),
141
			),
142
143
			'recommend'        => array(
144
				'name'    => __( 'Recommend', 'invoicing' ),
145
				'view'    => array( $this, 'setup_recommend' ),
146
				'handler' => '',
147
			),
148
149
			'next_steps'       => array(
150
				'name'    => __( 'Get Paid', 'invoicing' ),
151
				'view'    => array( $this, 'setup_ready' ),
152
				'handler' => '',
153
			),
154
155
		);
156
157
		return apply_filters( 'getpaid_setup_wizard_steps', $steps );
158
159
	}
160
161
	/**
162
	 * Returns the current step.
163
	 *
164
	 * @since 2.4.0
165
	 * @return string
166
	 */
167
	protected function get_current_step() {
168
		$step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : '';
169
		return ! empty( $step ) && in_array( $step, array_keys( $this->steps ) ) ? $step : current( array_keys( $this->steps ) );
170
	}
171
172
	/**
173
	 * Returns the previous step.
174
	 *
175
	 * @since 2.4.0
176
	 * @return string|false
177
	 */
178
	protected function get_previous_step() {
179
180
		$previous = false;
181
		$current  = $this->step;
182
		foreach ( array_keys( $this->steps ) as $step ) {
183
			if ( $current === $step ) {
184
				return $previous;
185
			}
186
187
			$previous = $step;
188
		}
189
190
		return false;
191
	}
192
193
	/**
194
	 * Returns the next step.
195
	 *
196
	 * @since 2.4.0
197
	 * @return string|false
198
	 */
199
	protected function get_next_step() {
200
201
		$on_current = false;
202
		$current    = $this->step;
203
		foreach ( array_keys( $this->steps ) as $step ) {
204
205
			if ( $on_current ) {
206
				return $step;
207
			}
208
209
			if ( $current === $step ) {
210
				return $on_current = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $on_current is dead and can be removed.
Loading history...
Bug Best Practice introduced by
The expression return $on_current = true returns the type true which is incompatible with the documented return type false|string.
Loading history...
211
			}
212
}
213
214
		return false;
215
	}
216
217
	/**
218
	 * Displays the setup wizard.
219
	 *
220
	 * @since 2.4.0
221
	 */
222
	public function display_wizard() {
223
		$this->display_header();
224
		$this->display_current_step();
225
		$this->display_footer();
226
	}
227
228
	/**
229
	 * Displays the Wizard Header.
230
	 *
231
	 * @since 2.0.0
232
	 */
233
	public function display_header() {
234
		$steps     = $this->steps;
235
		$current   = $this->step;
236
		$next_step = $this->next_step;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->next_step can also be of type boolean. However, the property $next_step is declared as type false|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...
237
		array_shift( $steps );
238
		include plugin_dir_path( __FILE__ ) . 'views/wizard-header.php';
239
	}
240
241
	/**
242
	 * Displays the content for the current step.
243
	 *
244
	 * @since 2.4.0
245
	 */
246
	public function display_current_step() {
247
		?>
248
			<div class="gp-setup-content rowx mw-100 text-center mb-3">
249
				<div class="col-12 col-md-5 m-auto">
250
					<?php call_user_func( $this->steps[ $this->step ]['view'], $this ); ?>
251
				</div>
252
			</div>
253
		<?php
254
	}
255
256
	/**
257
	 * Setup Wizard Footer.
258
	 *
259
	 * @since 2.4.0
260
	 */
261
	public function display_footer() {
262
263
		if ( isset( $_GET['step'] ) ) {
264
			$label    = $this->step == 'next_steps' ? __( 'Return to the WordPress Dashboard', 'invoicing' ) : __( 'Skip this step', 'invoicing' );
265
266
			echo '<p class="gd-return-to-dashboard-wrap"> <a href="' . esc_url( $this->get_next_step_link() ) . '" class="gd-return-to-dashboard btn btn-link d-block text-muted">' . esc_html( $label ) . '</a></p>';
267
		}
268
269
		echo '</body></html>';
270
	}
271
272
	/**
273
	 * Introduction step.
274
	 *
275
	 * @since 2.0.0
276
	 */
277
	public function setup_introduction() {
278
		$next_url = $this->get_next_step_link();
279
		include plugin_dir_path( __FILE__ ) . 'views/wizard-introduction.php';
280
	}
281
282
	/**
283
	 * Get the URL for the next step's screen.
284
	 *
285
	 * @param string step   slug (default: current step)
0 ignored issues
show
Bug introduced by
The type step 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...
286
	 *
287
	 * @return string       URL for next step if a next step exists.
288
	 *                      Admin URL if it's the last step.
289
	 *                      Empty string on failure.
290
	 * @since 3.0.0
291
	 */
292
	public function get_next_step_link( $step = '' ) {
293
		if ( ! $step ) {
294
			$step = $this->step;
295
		}
296
297
		$keys = array_keys( $this->steps );
298
		if ( end( $keys ) === $step ) {
299
			return admin_url();
300
		}
301
302
		$step_index = array_search( $step, $keys );
303
		if ( false === $step_index ) {
304
			return '';
305
		}
306
307
		return remove_query_arg( 'settings-updated', add_query_arg( 'step', $keys[ $step_index + 1 ] ) );
308
	}
309
310
	/**
311
	 * Setup maps api.
312
	 *
313
	 * @since 2.0.0
314
	 */
315
	public function setup_business() {
316
		$next_url = $this->get_next_step_link();
317
		$wizard   = $this;
318
		$page     = 'wpinv_settings_general_main';
319
		$section  = 'wpinv_settings_general_main';
320
		include plugin_dir_path( __FILE__ ) . 'views/wizard-settings.php';
321
	}
322
323
	/**
324
	 * Default Location settings.
325
	 *
326
	 * @since 2.0.0
327
	 */
328
	public function setup_currency() {
329
		$next_url = $this->get_next_step_link();
330
		$wizard   = $this;
331
		$page     = 'wpinv_settings_general_currency_section';
332
		$section  = 'wpinv_settings_general_currency_section';
333
		include plugin_dir_path( __FILE__ ) . 'views/wizard-settings.php';
334
	}
335
336
	/**
337
	 * Installation of recommended plugins.
338
	 *
339
	 * @since 1.0.0
340
	 */
341
	public function setup_recommend() {
342
		$next_url            = $this->get_next_step_link();
343
		$recommended_plugins = self::get_recommend_wp_plugins();
344
		include plugin_dir_path( __FILE__ ) . 'views/wizard-plugins.php';
345
	}
346
347
	/**
348
	 * A list of recommended wp.org plugins.
349
	 * @return array
350
	 */
351
	public static function get_recommend_wp_plugins() {
352
		return array(
353
			'ayecode-connect'  => array(
354
				'file' => 'ayecode-connect/ayecode-connect.php',
355
				'url'  => 'https://wordpress.org/plugins/ayecode-connect/',
356
				'slug' => 'ayecode-connect',
357
				'name' => 'AyeCode Connect',
358
				'desc' => __( 'Documentation and Support from within your WordPress admin.', 'invoicing' ),
359
			),
360
			'invoicing-quotes' => array(
361
				'file' => 'invoicing-quotes/wpinv-quote.php',
362
				'url'  => 'https://wordpress.org/plugins/invoicing-quotes/',
363
				'slug' => 'invoicing-quotes',
364
				'name' => 'Customer Quotes',
365
				'desc' => __( 'Create & Send Quotes to Customers and have them accept and pay.', 'invoicing' ),
366
			),
367
			'userswp'          => array(
368
				'file' => 'userswp/userswp.php',
369
				'url'  => 'https://wordpress.org/plugins/userswp/',
370
				'slug' => 'userswp',
371
				'name' => 'UsersWP',
372
				'desc' => __( 'Frontend user login and registration as well as slick profile pages.', 'invoicing' ),
373
			),
374
		);
375
	}
376
377
	/**
378
	 * Dummy Data setup.
379
	 *
380
	 * @since 2.4.0
381
	 */
382
	public function setup_payments() {
383
		$next_url = $this->get_next_step_link();
384
		include plugin_dir_path( __FILE__ ) . 'views/wizard-gateways.php';
385
	}
386
387
	/**
388
	 * Dummy data save.
389
	 *
390
	 * This is done via ajax so we just pass onto the next step.
391
	 *
392
	 * @since 2.0.0
393
	 */
394
	public function setup_payments_save() {
395
		check_admin_referer( 'getpaid-setup-wizard', 'getpaid-setup-wizard' );
396
		wpinv_update_option( 'manual_active', ! empty( $_POST['enable-manual-gateway'] ) );
397
398
		if ( ! empty( $_POST['paypal-email'] ) ) {
399
			wpinv_update_option( 'paypal_email', sanitize_email( $_POST['paypal-email'] ) );
400
			wpinv_update_option( 'paypal_active', 1 );
401
			wpinv_update_option( 'paypal_sandbox', 0 );
402
		}
403
404
		wp_redirect( esc_url_raw( $this->get_next_step_link() ) );
405
		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...
406
	}
407
408
	/**
409
	 * Final step.
410
	 *
411
	 * @since 2.0.0
412
	 */
413
	public function setup_ready() {
414
		include plugin_dir_path( __FILE__ ) . 'views/wizard-thank-you.php';
415
	}
416
417
}
418
419
new GetPaid_Admin_Setup_Wizard();
420