GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( bb6361...a2a740 )
by Brad
04:28
created

FS_Admin_Menu_Manager::init()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 53
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 27
nc 20
nop 2
dl 0
loc 53
rs 7.1199
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
	/**
3
	 * @package     Freemius
4
	 * @copyright   Copyright (c) 2015, Freemius, Inc.
5
	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
	 * @since       1.1.3
7
	 */
8
9
	if ( ! defined( 'ABSPATH' ) ) {
10
		exit;
11
	}
12
13
	class FS_Admin_Menu_Manager {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
14
15
		#region Properties
16
17
		/**
18
		 * @since 1.2.2
19
		 *
20
		 * @var string
21
		 */
22
		protected $_module_unique_affix;
23
24
		/**
25
		 * @since 1.2.2
26
		 *
27
		 * @var number
28
		 */
29
		protected $_module_id;
30
31
		/**
32
		 * @since 1.2.2
33
		 *
34
		 * @var string
35
		 */
36
		protected $_module_type;
37
38
		/**
39
		 * @since 1.0.6
40
		 *
41
		 * @var string
42
		 */
43
		private $_menu_slug;
44
		/**
45
		 * @since 1.1.3
46
		 *
47
		 * @var string
48
		 */
49
		private $_parent_slug;
50
		/**
51
		 * @since 1.1.3
52
		 *
53
		 * @var string
54
		 */
55
		private $_parent_type;
56
		/**
57
		 * @since 1.1.3
58
		 *
59
		 * @var string
60
		 */
61
		private $_type;
62
		/**
63
		 * @since 1.1.3
64
		 *
65
		 * @var bool
66
		 */
67
		private $_is_top_level;
68
		/**
69
		 * @since 1.1.3
70
		 *
71
		 * @var bool
72
		 */
73
		private $_is_override_exact;
74
		/**
75
		 * @since 1.1.3
76
		 *
77
		 * @var array<string,bool>
78
		 */
79
		private $_default_submenu_items;
80
		/**
81
		 * @since 1.1.3
82
		 *
83
		 * @var string
84
		 */
85
		private $_first_time_path;
86
		/**
87
		 * @since 1.2.2
88
		 *
89
		 * @var bool
90
		 */
91
		private $_menu_exists;
92
93
		#endregion Properties
94
95
		/**
96
		 * @var FS_Logger
97
		 */
98
		protected $_logger;
99
100
		#region Singleton
101
102
		/**
103
		 * @var FS_Admin_Menu_Manager[]
104
		 */
105
		private static $_instances = array();
106
107
		/**
108
		 * @param number $module_id
109
		 * @param string $module_type
110
		 * @param string $module_unique_affix
111
		 *
112
		 * @return FS_Admin_Menu_Manager
113
		 */
114
		static function instance( $module_id, $module_type, $module_unique_affix ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
115
			$key = 'm_' . $module_id;
116
117
			if ( ! isset( self::$_instances[ $key ] ) ) {
118
				self::$_instances[ $key ] = new FS_Admin_Menu_Manager( $module_id, $module_type, $module_unique_affix );
119
			}
120
121
			return self::$_instances[ $key ];
122
		}
123
124
		protected function __construct( $module_id, $module_type, $module_unique_affix ) {
125
			$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
126
127
			$this->_module_id           = $module_id;
128
			$this->_module_type         = $module_type;
129
			$this->_module_unique_affix = $module_unique_affix;
130
		}
131
132
		#endregion Singleton
133
134
		#region Helpers
135
136
		private function get_option( &$options, $key, $default = false ) {
137
			return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
138
		}
139
140
		private function get_bool_option( &$options, $key, $default = false ) {
141
			return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
142
		}
143
144
		#endregion Helpers
145
146
		/**
147
		 * @param array $menu
148
		 * @param bool  $is_addon
149
		 */
150
		function init( $menu, $is_addon = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
151
			$this->_menu_exists = ( isset( $menu['slug'] ) && ! empty( $menu['slug'] ) );
152
153
			$this->_menu_slug = ( $this->_menu_exists ? $menu['slug'] : $this->_module_unique_affix );
154
155
			$this->_default_submenu_items = array();
156
			// @deprecated
157
			$this->_type              = 'page';
158
			$this->_is_top_level      = true;
159
			$this->_is_override_exact = false;
160
			$this->_parent_slug       = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_parent_slug was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
161
			// @deprecated
162
			$this->_parent_type = 'page';
163
164
			if ( ! $is_addon && isset( $menu ) ) {
165
				$this->_default_submenu_items = array(
166
					'contact'     => $this->get_bool_option( $menu, 'contact', true ),
167
					'support'     => $this->get_bool_option( $menu, 'support', true ),
168
                    'affiliation' => $this->get_bool_option( $menu, 'affiliation', true ),
169
					'account'     => $this->get_bool_option( $menu, 'account', true ),
170
					'pricing'     => $this->get_bool_option( $menu, 'pricing', true ),
171
					'addons'      => $this->get_bool_option( $menu, 'addons', true ),
172
				);
173
174
				// @deprecated
175
				$this->_type              = $this->get_option( $menu, 'type', 'page' );
0 ignored issues
show
Documentation introduced by
'page' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
176
				$this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' );
177
178
				if ( isset( $menu['parent'] ) ) {
179
					$this->_parent_slug = $this->get_option( $menu['parent'], 'slug' );
180
					// @deprecated
181
					$this->_parent_type = $this->get_option( $menu['parent'], 'type', 'page' );
0 ignored issues
show
Documentation introduced by
'page' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182
183
					// If parent's slug is different, then it's NOT a top level menu item.
184
					$this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug );
185
				} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
186
					/**
187
					 * If no parent then top level if:
188
					 *  - Has custom admin menu ('page')
189
					 *  - CPT menu type ('cpt')
190
					 */
191
//					$this->_is_top_level = in_array( $this->_type, array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
192
//						'cpt',
193
//						'page'
194
//					) );
195
				}
196
197
				$this->_first_time_path = $this->get_option( $menu, 'first-path', false );
198
				if ( ! empty( $this->_first_time_path ) && is_string( $this->_first_time_path ) ) {
199
					$this->_first_time_path = admin_url( $this->_first_time_path, 'admin' );
200
				}
201
			}
202
		}
203
204
		/**
205
		 * Check if top level menu.
206
		 *
207
		 * @author Vova Feldman (@svovaf)
208
		 * @since  1.1.3
209
		 *
210
		 * @return bool False if submenu item.
211
		 */
212
		function is_top_level() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
213
			return $this->_is_top_level;
214
		}
215
216
		/**
217
		 * Check if the page should be override on exact URL match.
218
		 *
219
		 * @author Vova Feldman (@svovaf)
220
		 * @since  1.1.3
221
		 *
222
		 * @return bool False if submenu item.
223
		 */
224
		function is_override_exact() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
225
			return $this->_is_override_exact;
226
		}
227
228
229
		/**
230
		 * Get the path of the page the user should be forwarded to after first activation.
231
		 *
232
		 * @author Vova Feldman (@svovaf)
233
		 * @since  1.1.3
234
		 *
235
		 * @return string
236
		 */
237
		function get_first_time_path() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
238
			return $this->_first_time_path;
239
		}
240
241
		/**
242
		 * Check if plugin's menu item is part of a custom top level menu.
243
		 *
244
		 * @author Vova Feldman (@svovaf)
245
		 * @since  1.1.3
246
		 *
247
		 * @return bool
248
		 */
249
		function has_custom_parent() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
250
			return ! $this->_is_top_level && is_string( $this->_parent_slug );
251
		}
252
253
		/**
254
		 * @author Leo Fajardo (@leorw)
255
		 * @since  1.2.2
256
		 *
257
		 * @return bool
258
		 */
259
		function has_menu() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
260
			return $this->_menu_exists;
261
		}
262
263
		/**
264
		 * @author Vova Feldman (@svovaf)
265
		 * @since  1.1.3
266
		 *
267
		 * @param string $id
268
		 * @param bool   $default
269
		 * @param bool   $ignore_menu_existence Since 1.2.2.7 If true, check if the submenu item visible even if there's no parent menu.
270
		 *
271
		 * @return bool
272
		 */
273
		function is_submenu_item_visible( $id, $default = true, $ignore_menu_existence = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
274
			if ( ! $ignore_menu_existence && ! $this->has_menu() ) {
275
				return false;
276
			}
277
278
			return fs_apply_filter(
279
				$this->_module_unique_affix,
280
				'is_submenu_visible',
281
				$this->get_bool_option( $this->_default_submenu_items, $id, $default ),
282
				$id
283
			);
284
		}
285
286
		/**
287
		 * Calculates admin settings menu slug.
288
		 * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug.
289
		 *
290
		 * @author Vova Feldman (@svovaf)
291
		 * @since  1.1.3
292
		 *
293
		 * @param string $page
294
		 *
295
		 * @return string
296
		 */
297
		function get_slug( $page = '' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
298
			return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ?
299
				$this->_menu_slug :
300
				$this->_module_unique_affix ) . ( empty( $page ) ? '' : ( '-' . $page ) );
301
		}
302
303
		/**
304
		 * @author Vova Feldman (@svovaf)
305
		 * @since  1.1.3
306
		 *
307
		 * @return string
308
		 */
309
		function get_parent_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
310
			return $this->_parent_slug;
311
		}
312
313
		/**
314
		 * @author Vova Feldman (@svovaf)
315
		 * @since  1.1.3
316
		 *
317
		 * @return string
318
		 */
319
		function get_type() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
320
			return $this->_type;
321
		}
322
323
		/**
324
		 * @author Vova Feldman (@svovaf)
325
		 * @since  1.1.3
326
		 *
327
		 * @return bool
328
		 */
329
		function is_cpt() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
330
			return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) ||
331
			         // Back compatibility.
332
			         'cpt' === $this->_type
333
			);
334
		}
335
336
		/**
337
		 * @author Vova Feldman (@svovaf)
338
		 * @since  1.1.3
339
		 *
340
		 * @return string
341
		 */
342
		function get_parent_type() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
343
			return $this->_parent_type;
344
		}
345
346
		/**
347
		 * @author Vova Feldman (@svovaf)
348
		 * @since  1.1.3
349
		 *
350
		 * @return string
351
		 */
352
		function get_raw_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
353
			return $this->_menu_slug;
354
		}
355
356
		/**
357
		 * Get plugin's original menu slug.
358
		 *
359
		 * @author Vova Feldman (@svovaf)
360
		 * @since  1.1.3
361
		 *
362
		 * @return string
363
		 */
364
		function get_original_menu_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
365
			if ( 'cpt' === $this->_type ) {
366
				return add_query_arg( array(
367
					'post_type' => $this->_menu_slug
368
				), 'edit.php' );
369
			}
370
371
			if ( false === strpos( $this->_menu_slug, '.php?' ) ) {
372
				return $this->_menu_slug;
373
			} else {
374
				return $this->_module_unique_affix;
375
			}
376
		}
377
378
		/**
379
		 * @author Vova Feldman (@svovaf)
380
		 * @since  1.1.3
381
		 *
382
		 * @return string
383
		 */
384
		function get_top_level_menu_slug() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
385
			return $this->has_custom_parent() ?
386
				$this->get_parent_slug() :
387
				$this->get_raw_slug();
388
		}
389
390
		/**
391
		 * Is user on plugin's admin activation page.
392
		 *
393
		 * @author Vova Feldman (@svovaf)
394
		 * @since  1.0.8
395
		 *
396
		 * @return bool
397
		 */
398
		function is_main_settings_page() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
399
			if ( $this->_menu_exists &&
400
			     ( fs_is_plugin_page( $this->_menu_slug ) || fs_is_plugin_page( $this->_module_unique_affix ) )
401
			) {
402
				/**
403
				 * Module has a settings menu and the context page is the main settings page, so assume it's in
404
				 * activation (doesn't really check if already opted-in/skipped or not).
405
				 *
406
				 * @since 1.2.2
407
				 */
408
				return true;
409
			}
410
411
			global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
412
			if ( ( WP_FS__MODULE_TYPE_THEME === $this->_module_type ) && Freemius::is_themes_page() ) {
413
				/**
414
				 * In activation only when show_optin query string param is given.
415
				 *
416
				 * @since 1.2.2
417
				 */
418
				return fs_request_get_bool( $this->_module_unique_affix . '_show_optin' );
419
			}
420
421
			return false;
422
		}
423
424
		#region Submenu Override
425
426
		/**
427
		 * Override submenu's action.
428
		 *
429
		 * @author Vova Feldman (@svovaf)
430
		 * @since  1.1.0
431
		 *
432
		 * @param string   $parent_slug
433
		 * @param string   $menu_slug
434
		 * @param callable $function
435
		 *
436
		 * @return false|string If submenu exist, will return the hook name.
437
		 */
438
		function override_submenu_action( $parent_slug, $menu_slug, $function ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
439
			global $submenu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
440
441
			$menu_slug   = plugin_basename( $menu_slug );
442
			$parent_slug = plugin_basename( $parent_slug );
443
444
			if ( ! isset( $submenu[ $parent_slug ] ) ) {
445
				// Parent menu not exist.
446
				return false;
447
			}
448
449
			$found_submenu_item = false;
450
			foreach ( $submenu[ $parent_slug ] as $submenu_item ) {
451
				if ( $menu_slug === $submenu_item[2] ) {
452
					$found_submenu_item = $submenu_item;
453
					break;
454
				}
455
			}
456
457
			if ( false === $found_submenu_item ) {
458
				// Submenu item not found.
459
				return false;
460
			}
461
462
			// Remove current function.
463
			$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
464
			remove_all_actions( $hookname );
465
466
			// Attach new action.
467
			add_action( $hookname, $function );
468
469
			return $hookname;
470
		}
471
472
		#endregion Submenu Override
473
474
		#region Top level menu Override
475
476
		/**
477
		 * Find plugin's admin dashboard main menu item.
478
		 *
479
		 * @author Vova Feldman (@svovaf)
480
		 * @since  1.0.2
481
		 *
482
		 * @return string[]|false
483
		 */
484
		private function find_top_level_menu() {
485
			global $menu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
486
487
			$position   = - 1;
488
			$found_menu = false;
489
490
			$menu_slug = $this->get_raw_slug();
491
492
			$hook_name = get_plugin_page_hookname( $menu_slug, '' );
493
			foreach ( $menu as $pos => $m ) {
494
				if ( $menu_slug === $m[2] ) {
495
					$position   = $pos;
496
					$found_menu = $m;
497
					break;
498
				}
499
			}
500
501
			if ( false === $found_menu ) {
502
				return false;
503
			}
504
505
			return array(
506
				'menu'      => $found_menu,
507
				'position'  => $position,
508
				'hook_name' => $hook_name
509
			);
510
		}
511
512
		/**
513
		 * Find plugin's admin dashboard main submenu item.
514
		 *
515
		 * @author Vova Feldman (@svovaf)
516
		 * @since  1.2.1.6
517
		 *
518
		 * @return array|false
519
		 */
520
		private function find_main_submenu() {
521
			global $submenu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
522
523
			$top_level_menu_slug = $this->get_top_level_menu_slug();
524
525
			if ( ! isset( $submenu[ $top_level_menu_slug ] ) ) {
526
				return false;
527
			}
528
529
			$submenu_slug = $this->get_raw_slug();
530
531
			$position   = - 1;
532
			$found_submenu = false;
533
534
			$hook_name = get_plugin_page_hookname( $submenu_slug, '' );
535
536
			foreach ( $submenu[ $top_level_menu_slug ] as $pos => $sub ) {
537
				if ( $submenu_slug === $sub[2] ) {
538
					$position   = $pos;
539
					$found_submenu = $sub;
540
				}
541
			}
542
543
			if ( false === $found_submenu ) {
544
				return false;
545
			}
546
547
			return array(
548
				'menu'        => $found_submenu,
549
				'parent_slug' => $top_level_menu_slug,
550
				'position'    => $position,
551
				'hook_name'   => $hook_name
552
			);
553
		}
554
555
		/**
556
		 * Remove all sub-menu items.
557
		 *
558
		 * @author Vova Feldman (@svovaf)
559
		 * @since  1.0.7
560
		 *
561
		 * @return bool If submenu with plugin's menu slug was found.
562
		 */
563
		private function remove_all_submenu_items() {
564
			global $submenu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
565
566
			$menu_slug = $this->get_raw_slug();
567
568
			if ( ! isset( $submenu[ $menu_slug ] ) ) {
569
				return false;
570
			}
571
572
			/**
573
			 * This method is NOT executed for WordPress.org themes.
574
			 * Since we maintain only one version of the SDK we added this small
575
			 * hack to avoid the error from Theme Check since it's a false-positive.
576
			 *
577
			 * @author Vova Feldman (@svovaf)
578
			 * @since  1.2.2.7
579
			 */
580
			$submenu_ref               = &$submenu;
581
			$submenu_ref[ $menu_slug ] = array();
582
583
			return true;
584
		}
585
586
		/**
587
		 *
588
		 * @author Vova Feldman (@svovaf)
589
		 * @since  1.0.9
590
		 *
591
		 * @return false|array[string]mixed
0 ignored issues
show
Documentation introduced by
The doc-type false|array[string]mixed could not be parsed: Expected "]" at position 4, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
592
		 */
593
		function remove_menu_item() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
594
			$this->_logger->entrance();
595
596
			// Find main menu item.
597
			$menu = $this->find_top_level_menu();
598
599
			if ( false === $menu ) {
600
				return false;
601
			}
602
603
			// Remove it with its actions.
604
			remove_all_actions( $menu['hook_name'] );
605
606
			// Remove all submenu items.
607
			$this->remove_all_submenu_items();
608
609
			return $menu;
610
		}
611
612
		/**
613
		 * Get module's main admin setting page URL.
614
		 *
615
		 * @todo This method was only tested for wp.org compliant themes with a submenu item. Need to test for plugins with top level, submenu, and CPT top level, menu items.
616
		 *
617
		 * @author Vova Feldman (@svovaf)
618
		 * @since  1.2.2.7
619
		 *
620
		 * @return string
621
		 */
622
		function main_menu_url() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
623
			$this->_logger->entrance();
624
625
			if ( $this->_is_top_level ) {
626
				$menu = $this->find_top_level_menu();
627
			} else {
628
				$menu = $this->find_main_submenu();
629
			}
630
631
			$parent_slug = isset( $menu['parent_slug'] ) ?
632
                $menu['parent_slug'] :
633
                'admin.php';
634
635
			return admin_url( $parent_slug . '?page=' . $menu['menu'][2] );
636
		}
637
638
		/**
639
		 * @author Vova Feldman (@svovaf)
640
		 * @since  1.1.4
641
		 *
642
		 * @param callable $function
643
		 *
644
		 * @return false|array[string]mixed
0 ignored issues
show
Documentation introduced by
The doc-type false|array[string]mixed could not be parsed: Expected "]" at position 4, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
645
		 */
646
		function override_menu_item( $function ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
647
			$found_menu = $this->remove_menu_item();
648
649
			if ( false === $found_menu ) {
650
				return false;
651
			}
652
653
			if ( ! $this->is_top_level() || ! $this->is_cpt() ) {
654
				$menu_slug = plugin_basename( $this->get_slug() );
655
656
				$hookname = get_plugin_page_hookname( $menu_slug, '' );
657
658
				// Override menu action.
659
				add_action( $hookname, $function );
660
			} else {
661
				global $menu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
662
663
				// Remove original CPT menu.
664
				unset( $menu[ $found_menu['position'] ] );
665
666
				// Create new top-level menu action.
667
				$hookname = self::add_page(
668
					$found_menu['menu'][3],
669
					$found_menu['menu'][0],
670
					'manage_options',
671
					$this->get_slug(),
672
					$function,
673
					$found_menu['menu'][6],
674
					$found_menu['position']
675
				);
676
			}
677
678
			return $hookname;
679
		}
680
681
		/**
682
		 * Adds a counter to the module's top level menu item.
683
		 *
684
		 * @author Vova Feldman (@svovaf)
685
		 * @since  1.2.1.5
686
		 *
687
		 * @param int    $counter
688
		 * @param string $class
689
		 */
690
		function add_counter_to_menu_item( $counter = 1, $class = '' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
691
			global $menu, $submenu;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
692
693
			$mask = '%s <span class="update-plugins %s count-%3$s" aria-hidden="true"><span>%3$s<span class="screen-reader-text">%3$s notifications</span></span></span>';
694
695
			/**
696
			 * This method is NOT executed for WordPress.org themes.
697
			 * Since we maintain only one version of the SDK we added this small
698
			 * hack to avoid the error from Theme Check since it's a false-positive.
699
			 *
700
			 * @author Vova Feldman (@svovaf)
701
			 * @since  1.2.2.7
702
			 */
703
			$menu_ref    = &$menu;
704
			$submenu_ref = &$submenu;
705
706
			if ( $this->_is_top_level ) {
707
				// Find main menu item.
708
				$found_menu = $this->find_top_level_menu();
709
710
				if ( false !== $found_menu ) {
711
					// Override menu label.
712
					$menu_ref[ $found_menu['position'] ][0] = sprintf(
713
						$mask,
714
						$found_menu['menu'][0],
715
						$class,
716
						$counter
717
					);
718
				}
719
			} else {
720
				$found_submenu = $this->find_main_submenu();
721
722
				if ( false !== $found_submenu ) {
723
					// Override menu label.
724
					$submenu_ref[ $found_submenu['parent_slug'] ][ $found_submenu['position'] ][0] = sprintf(
725
						$mask,
726
						$found_submenu['menu'][0],
727
						$class,
728
						$counter
729
					);
730
				}
731
			}
732
		}
733
734
		#endregion Top level menu Override
735
736
		/**
737
		 * Add a top-level menu page.
738
		 *
739
		 * Note for WordPress.org Theme/Plugin reviewer:
740
		 *
741
		 *  This is a replication of `add_menu_page()` to avoid Theme Check warning.
742
		 *
743
		 *  Why?
744
		 *  ====
745
		 *  Freemius is an SDK for plugin and theme developers. Since the core
746
		 *  of the SDK is relevant both for plugins and themes, for obvious reasons,
747
		 *  we only develop and maintain one code base.
748
		 *
749
		 *  This method will not run for wp.org themes (only plugins) since theme
750
		 *  admin settings/options are now only allowed in the customizer.
751
		 *
752
		 *  If you have any questions or need clarifications, please don't hesitate
753
		 *  pinging me on slack, my username is @svovaf.
754
		 *
755
		 * @author Vova Feldman (@svovaf)
756
		 * @since  1.2.2
757
		 *
758
		 * @param string          $page_title The text to be displayed in the title tags of the page when the menu is
759
		 *                                    selected.
760
		 * @param string          $menu_title The text to be used for the menu.
761
		 * @param string          $capability The capability required for this menu to be displayed to the user.
762
		 * @param string          $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
763
		 * @param callable|string $function   The function to be called to output the content for this page.
764
		 * @param string          $icon_url   The URL to the icon to be used for this menu.
765
		 *                                    * Pass a base64-encoded SVG using a data URI, which will be colored to
766
		 *                                    match the color scheme. This should begin with
767
		 *                                    'data:image/svg+xml;base64,'.
768
		 *                                    * Pass the name of a Dashicons helper class to use a font icon,
769
		 *                                    e.g. 'dashicons-chart-pie'.
770
		 *                                    * Pass 'none' to leave div.wp-menu-image empty so an icon can be added
771
		 *                                    via CSS.
772
		 * @param int             $position   The position in the menu order this one should appear.
773
		 *
774
		 * @return string The resulting page's hook_suffix.
775
		 */
776
		static function add_page(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
777
			$page_title,
778
			$menu_title,
779
			$capability,
780
			$menu_slug,
781
			$function = '',
782
			$icon_url = '',
783
			$position = null
784
		) {
785
			$fn = 'add_menu' . '_page';
786
787
			return $fn(
788
				$page_title,
789
				$menu_title,
790
				$capability,
791
				$menu_slug,
792
				$function,
793
				$icon_url,
794
				$position
795
			);
796
		}
797
798
		/**
799
		 * Add a submenu page.
800
		 *
801
		 * Note for WordPress.org Theme/Plugin reviewer:
802
		 *
803
		 *  This is a replication of `add_submenu_page()` to avoid Theme Check warning.
804
		 *
805
		 *  Why?
806
		 *  ====
807
		 *  Freemius is an SDK for plugin and theme developers. Since the core
808
		 *  of the SDK is relevant both for plugins and themes, for obvious reasons,
809
		 *  we only develop and maintain one code base.
810
		 *
811
		 *  This method will not run for wp.org themes (only plugins) since theme
812
		 *  admin settings/options are now only allowed in the customizer.
813
		 *
814
		 *  If you have any questions or need clarifications, please don't hesitate
815
		 *  pinging me on slack, my username is @svovaf.
816
		 *
817
		 * @author Vova Feldman (@svovaf)
818
		 * @since  1.2.2
819
		 *
820
		 * @param string          $parent_slug The slug name for the parent menu (or the file name of a standard
821
		 *                                     WordPress admin page).
822
		 * @param string          $page_title  The text to be displayed in the title tags of the page when the menu is
823
		 *                                     selected.
824
		 * @param string          $menu_title  The text to be used for the menu.
825
		 * @param string          $capability  The capability required for this menu to be displayed to the user.
826
		 * @param string          $menu_slug   The slug name to refer to this menu by (should be unique for this menu).
827
		 * @param callable|string $function    The function to be called to output the content for this page.
828
		 *
829
		 * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability
830
		 *                      required.
831
		 */
832
		static function add_subpage(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
833
			$parent_slug,
834
			$page_title,
835
			$menu_title,
836
			$capability,
837
			$menu_slug,
838
			$function = ''
839
		) {
840
			$fn = 'add_submenu' . '_page';
841
842
			return $fn( $parent_slug,
843
				$page_title,
844
				$menu_title,
845
				$capability,
846
				$menu_slug,
847
				$function
848
			);
849
		}
850
	}