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 ( 74a577...62d274 )
by Brad
02:30
created

FS_Admin_Menu_Manager::is_cpt()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
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     http://opensource.org/licenses/gpl-2.0.php GNU Public License
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
		 * @var string
19
		 */
20
		protected $_plugin_slug;
21
22
		/**
23
		 * @since 1.0.6
24
		 *
25
		 * @var string
26
		 */
27
		private $_menu_slug;
28
		/**
29
		 * @since 1.1.3
30
		 *
31
		 * @var string
32
		 */
33
		private $_parent_slug;
34
		/**
35
		 * @since 1.1.3
36
		 *
37
		 * @var string
38
		 */
39
		private $_parent_type;
40
		/**
41
		 * @since 1.1.3
42
		 *
43
		 * @var string
44
		 */
45
		private $_type;
46
		/**
47
		 * @since 1.1.3
48
		 *
49
		 * @var bool
50
		 */
51
		private $_is_top_level;
52
		/**
53
		 * @since 1.1.3
54
		 *
55
		 * @var bool
56
		 */
57
		private $_is_override_exact;
58
		/**
59
		 * @since 1.1.3
60
		 *
61
		 * @var array<string,bool>
62
		 */
63
		private $_default_submenu_items;
64
		/**
65
		 * @since 1.1.3
66
		 *
67
		 * @var string
68
		 */
69
		private $_first_time_path;
70
		/**
71
		 * @since 1.2.2
72
		 *
73
		 * @var bool
74
		 */
75
		private $_menu_exists;
76
77
		#endregion Properties
78
79
		/**
80
		 * @var FS_Logger
81
		 */
82
		protected $_logger;
83
84
		#region Singleton
85
86
		/**
87
		 * @var FS_Admin_Menu_Manager[]
88
		 */
89
		private static $_instances = array();
90
91
		/**
92
		 * @param string $plugin_slug
93
		 *
94
		 * @return FS_Admin_Menu_Manager
95
		 */
96
		static function instance( $plugin_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...
97
			if ( ! isset( self::$_instances[ $plugin_slug ] ) ) {
98
				self::$_instances[ $plugin_slug ] = new FS_Admin_Menu_Manager( $plugin_slug );
99
			}
100
101
			return self::$_instances[ $plugin_slug ];
102
		}
103
104
		protected function __construct( $plugin_slug ) {
105
			$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $plugin_slug . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
106
107
			$this->_plugin_slug = $plugin_slug;
108
		}
109
110
		#endregion Singleton
111
112
		#region Helpers
113
114
		private function get_option( &$options, $key, $default = false ) {
115
			return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
116
		}
117
118
		private function get_bool_option( &$options, $key, $default = false ) {
119
			return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
120
		}
121
122
		#endregion Helpers
123
124
		/**
125
		 * @param array $menu
126
		 * @param bool  $is_addon
127
		 */
128
		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...
129
			$this->_menu_exists = ( isset( $menu['slug'] ) && ! empty( $menu['slug'] ) );
130
131
			$this->_menu_slug = ! empty( $menu['slug'] ) ? $menu['slug'] : null;
132
133
			$this->_default_submenu_items = array();
134
			// @deprecated
135
			$this->_type              = 'page';
136
			$this->_is_top_level      = true;
137
			$this->_is_override_exact = false;
138
			$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...
139
			// @deprecated
140
			$this->_parent_type = 'page';
141
142
			if ( ! $is_addon && isset( $menu ) ) {
143
				$this->_default_submenu_items = array(
144
					'contact' => $this->get_bool_option( $menu, 'contact', true ),
145
					'support' => $this->get_bool_option( $menu, 'support', true ),
146
					'account' => $this->get_bool_option( $menu, 'account', true ),
147
					'pricing' => $this->get_bool_option( $menu, 'pricing', true ),
148
					'addons'  => $this->get_bool_option( $menu, 'addons', true ),
149
				);
150
151
				// @deprecated
152
				$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...
153
				$this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' );
154
155
				if ( isset( $menu['parent'] ) ) {
156
					$this->_parent_slug = $this->get_option( $menu['parent'], 'slug' );
157
					// @deprecated
158
					$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...
159
160
					// If parent's slug is different, then it's NOT a top level menu item.
161
					$this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug );
162
				} 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...
163
					/**
164
					 * If no parent then top level if:
165
					 *  - Has custom admin menu ('page')
166
					 *  - CPT menu type ('cpt')
167
					 */
168
//					$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...
169
//						'cpt',
170
//						'page'
171
//					) );
172
				}
173
174
				$this->_first_time_path = $this->get_option( $menu, 'first-path', false );
175
				if ( ! empty( $this->_first_time_path ) && is_string( $this->_first_time_path ) ) {
176
					$this->_first_time_path = admin_url( $this->_first_time_path, 'admin' );
177
				}
178
			}
179
		}
180
181
		/**
182
		 * Check if top level menu.
183
		 *
184
		 * @author Vova Feldman (@svovaf)
185
		 * @since  1.1.3
186
		 *
187
		 * @return bool False if submenu item.
188
		 */
189
		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...
190
			return $this->_is_top_level;
191
		}
192
193
		/**
194
		 * Check if the page should be override on exact URL match.
195
		 *
196
		 * @author Vova Feldman (@svovaf)
197
		 * @since  1.1.3
198
		 *
199
		 * @return bool False if submenu item.
200
		 */
201
		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...
202
			return $this->_is_override_exact;
203
		}
204
205
206
		/**
207
		 * Get the path of the page the user should be forwarded to after first activation.
208
		 *
209
		 * @author Vova Feldman (@svovaf)
210
		 * @since  1.1.3
211
		 *
212
		 * @return string
213
		 */
214
		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...
215
			return $this->_first_time_path;
216
		}
217
218
		/**
219
		 * Check if plugin's menu item is part of a custom top level menu.
220
		 *
221
		 * @author Vova Feldman (@svovaf)
222
		 * @since  1.1.3
223
		 *
224
		 * @return bool
225
		 */
226
		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...
227
			return ! $this->_is_top_level && is_string( $this->_parent_slug );
228
		}
229
230
		/**
231
		 * @author Leo Fajardo (@leorw)
232
		 * @since  1.2.2
233
		 *
234
		 * @return bool
235
		 */
236
		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...
237
			return $this->_menu_exists;
238
		}
239
240
		/**
241
		 * @author Vova Feldman (@svovaf)
242
		 * @since  1.1.3
243
		 *
244
		 * @param string $id
245
		 * @param bool   $default
246
		 *
247
		 * @return bool
248
		 */
249
		function is_submenu_item_visible( $id, $default = true ) {
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
			if ( ! $this->has_menu() ) {
251
				return false;
252
			}
253
254
			return fs_apply_filter(
255
				$this->_plugin_slug,
256
				'is_submenu_visible',
257
				$this->get_bool_option( $this->_default_submenu_items, $id, $default ),
258
				$id
259
			);
260
		}
261
262
		/**
263
		 * Calculates admin settings menu slug.
264
		 * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug.
265
		 *
266
		 * @author Vova Feldman (@svovaf)
267
		 * @since  1.1.3
268
		 *
269
		 * @param string $page
270
		 *
271
		 * @return string
272
		 */
273
		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...
274
			return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ?
275
				$this->_menu_slug :
276
				$this->_plugin_slug ) . ( empty( $page ) ? '' : ( '-' . $page ) );
277
		}
278
279
		/**
280
		 * Check if module has a menu slug set.
281
		 *
282
		 * @author Vova Feldman (@svovaf)
283
		 * @since  1.2.1.6
284
		 *
285
		 * @return bool
286
		 */
287
		function has_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...
288
			return $this->has_menu();
289
		}
290
291
		/**
292
		 * @author Vova Feldman (@svovaf)
293
		 * @since  1.1.3
294
		 *
295
		 * @return string
296
		 */
297
		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...
298
			return $this->_parent_slug;
299
		}
300
301
		/**
302
		 * @author Vova Feldman (@svovaf)
303
		 * @since  1.1.3
304
		 *
305
		 * @return string
306
		 */
307
		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...
308
			return $this->_type;
309
		}
310
311
		/**
312
		 * @author Vova Feldman (@svovaf)
313
		 * @since  1.1.3
314
		 *
315
		 * @return bool
316
		 */
317
		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...
318
			return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) ||
319
			         // Back compatibility.
320
			         'cpt' === $this->_type
321
			);
322
		}
323
324
		/**
325
		 * @author Vova Feldman (@svovaf)
326
		 * @since  1.1.3
327
		 *
328
		 * @return string
329
		 */
330
		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...
331
			return $this->_parent_type;
332
		}
333
334
		/**
335
		 * @author Vova Feldman (@svovaf)
336
		 * @since  1.1.3
337
		 *
338
		 * @return string
339
		 */
340
		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...
341
			return $this->_menu_slug;
342
		}
343
344
		/**
345
		 * Get plugin's original menu slug.
346
		 *
347
		 * @author Vova Feldman (@svovaf)
348
		 * @since  1.1.3
349
		 *
350
		 * @return string
351
		 */
352
		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...
353
			if ( 'cpt' === $this->_type ) {
354
				return add_query_arg( array(
355
					'post_type' => $this->_menu_slug
356
				), 'edit.php' );
357
			}
358
359
			if ( false === strpos( $this->_menu_slug, '.php?' ) ) {
360
				return $this->_menu_slug;
361
			} else {
362
				return $this->_plugin_slug;
363
			}
364
		}
365
366
		/**
367
		 * @author Vova Feldman (@svovaf)
368
		 * @since  1.1.3
369
		 *
370
		 * @return string
371
		 */
372
		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...
373
			return $this->has_custom_parent() ?
374
				$this->get_parent_slug() :
375
				$this->get_raw_slug();
376
		}
377
378
		/**
379
		 * Is user on plugin's admin activation page.
380
		 *
381
		 * @author Vova Feldman (@svovaf)
382
		 * @since  1.0.8
383
		 *
384
		 * @return bool
385
		 */
386
		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...
387
			if ( $this->_menu_exists &&
388
			     ( fs_is_plugin_page( $this->_menu_slug ) || fs_is_plugin_page( $this->_plugin_slug ) )
389
			) {
390
				/**
391
				 * Module has a settings menu and the context page is the main settings page, so assume it's in
392
				 * activation (doesn't really check if already opted-in/skipped or not).
393
				 *
394
				 * @since 1.2.2
395
				 */
396
				return true;
397
			}
398
399
			return false;
400
		}
401
402
		#region Submenu Override
403
404
		/**
405
		 * Override submenu's action.
406
		 *
407
		 * @author Vova Feldman (@svovaf)
408
		 * @since  1.1.0
409
		 *
410
		 * @param string   $parent_slug
411
		 * @param string   $menu_slug
412
		 * @param callable $function
413
		 *
414
		 * @return false|string If submenu exist, will return the hook name.
415
		 */
416
		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...
417
			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...
418
419
			$menu_slug   = plugin_basename( $menu_slug );
420
			$parent_slug = plugin_basename( $parent_slug );
421
422
			if ( ! isset( $submenu[ $parent_slug ] ) ) {
423
				// Parent menu not exist.
424
				return false;
425
			}
426
427
			$found_submenu_item = false;
428
			foreach ( $submenu[ $parent_slug ] as $submenu_item ) {
429
				if ( $menu_slug === $submenu_item[2] ) {
430
					$found_submenu_item = $submenu_item;
431
					break;
432
				}
433
			}
434
435
			if ( false === $found_submenu_item ) {
436
				// Submenu item not found.
437
				return false;
438
			}
439
440
			// Remove current function.
441
			$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
442
			remove_all_actions( $hookname );
443
444
			// Attach new action.
445
			add_action( $hookname, $function );
446
447
			return $hookname;
448
		}
449
450
		#endregion Submenu Override
451
452
		#region Top level menu Override
453
454
		/**
455
		 * Find plugin's admin dashboard main menu item.
456
		 *
457
		 * @author Vova Feldman (@svovaf)
458
		 * @since  1.0.2
459
		 *
460
		 * @return string[]|false
461
		 */
462
		private function find_top_level_menu() {
463
			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...
464
465
			$position   = - 1;
466
			$found_menu = false;
467
468
			$menu_slug = $this->get_raw_slug();
469
470
			$hook_name = get_plugin_page_hookname( $menu_slug, '' );
471
			foreach ( $menu as $pos => $m ) {
472
				if ( $menu_slug === $m[2] ) {
473
					$position   = $pos;
474
					$found_menu = $m;
475
					break;
476
				}
477
			}
478
479
			if ( false === $found_menu ) {
480
				return false;
481
			}
482
483
			return array(
484
				'menu'      => $found_menu,
485
				'position'  => $position,
486
				'hook_name' => $hook_name
487
			);
488
		}
489
490
		/**
491
		 * Find plugin's admin dashboard main submenu item.
492
		 *
493
		 * @author Vova Feldman (@svovaf)
494
		 * @since  1.2.1.6
495
		 *
496
		 * @return array|false
497
		 */
498
		private function find_main_submenu() {
499
			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...
500
501
			$top_level_menu_slug = $this->get_top_level_menu_slug();
502
503
			if ( ! isset( $submenu[ $top_level_menu_slug ] ) ) {
504
				return false;
505
			}
506
507
			$submenu_slug = $this->get_raw_slug();
508
509
			$position      = - 1;
510
			$found_submenu = false;
511
512
			$hook_name = get_plugin_page_hookname( $submenu_slug, '' );
513
514
			foreach ( $submenu[ $top_level_menu_slug ] as $pos => $sub ) {
515
				if ( $submenu_slug === $sub[2] ) {
516
					$position      = $pos;
517
					$found_submenu = $sub;
518
				}
519
			}
520
521
			if ( false === $found_submenu ) {
522
				return false;
523
			}
524
525
			return array(
526
				'menu'        => $found_submenu,
527
				'parent_slug' => $top_level_menu_slug,
528
				'position'    => $position,
529
				'hook_name'   => $hook_name
530
			);
531
		}
532
533
		/**
534
		 * Remove all sub-menu items.
535
		 *
536
		 * @author Vova Feldman (@svovaf)
537
		 * @since  1.0.7
538
		 *
539
		 * @return bool If submenu with plugin's menu slug was found.
540
		 */
541
		private function remove_all_submenu_items() {
542
			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...
543
544
			$menu_slug = $this->get_raw_slug();
545
546
			if ( ! isset( $submenu[ $menu_slug ] ) ) {
547
				return false;
548
			}
549
550
			$submenu[ $menu_slug ] = array();
551
552
			return true;
553
		}
554
555
		/**
556
		 *
557
		 * @author Vova Feldman (@svovaf)
558
		 * @since  1.0.9
559
		 *
560
		 * @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...
561
		 */
562
		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...
563
			$this->_logger->entrance();
564
565
			// Find main menu item.
566
			$menu = $this->find_top_level_menu();
567
568
			if ( false === $menu ) {
569
				return false;
570
			}
571
572
			// Remove it with its actions.
573
			remove_all_actions( $menu['hook_name'] );
574
575
			// Remove all submenu items.
576
			$this->remove_all_submenu_items();
577
578
			return $menu;
579
		}
580
581
		/**
582
		 * @author Vova Feldman (@svovaf)
583
		 * @since  1.1.4
584
		 *
585
		 * @param callable $function
586
		 *
587
		 * @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...
588
		 */
589
		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...
590
			$found_menu = $this->remove_menu_item();
591
592
			if ( false === $found_menu ) {
593
				return false;
594
			}
595
596
			if ( ! $this->is_top_level() || ! $this->is_cpt() ) {
597
				$menu_slug = plugin_basename( $this->get_slug() );
598
599
				$hookname = get_plugin_page_hookname( $menu_slug, '' );
600
601
				// Override menu action.
602
				add_action( $hookname, $function );
603
			} else {
604
				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...
605
606
				// Remove original CPT menu.
607
				unset( $menu[ $found_menu['position'] ] );
608
609
				// Create new top-level menu action.
610
				$hookname = self::add_page(
611
					$found_menu['menu'][3],
612
					$found_menu['menu'][0],
613
					'manage_options',
614
					$this->get_slug(),
615
					$function,
616
					$found_menu['menu'][6],
617
					$found_menu['position']
618
				);
619
			}
620
621
			return $hookname;
622
		}
623
624
		/**
625
		 * Adds a counter to the module's top level menu item.
626
		 *
627
		 * @author Vova Feldman (@svovaf)
628
		 * @since  1.2.1.5
629
		 *
630
		 * @param int    $counter
631
		 * @param string $class
632
		 */
633
		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...
634
			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...
635
636
			$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>';
637
638
			if ( $this->_is_top_level ) {
639
				// Find main menu item.
640
				$found_menu = $this->find_top_level_menu();
641
642
				if ( false !== $found_menu ) {
643
					// Override menu label.
644
					$menu[ $found_menu['position'] ][0] = sprintf(
645
						$mask,
646
						$found_menu['menu'][0],
647
						$class,
648
						$counter
649
					);
650
				}
651
			} else {
652
				$found_submenu = $this->find_main_submenu();
653
654
				if ( false !== $found_submenu ) {
655
					// Override menu label.
656
					$submenu[ $found_submenu['parent_slug'] ][ $found_submenu['position'] ][0] = sprintf(
657
						$mask,
658
						$found_submenu['menu'][0],
659
						$class,
660
						$counter
661
					);
662
				}
663
			}
664
		}
665
666
		#endregion Top level menu Override
667
668
		/**
669
		 * Add a top-level menu page.
670
		 *
671
		 * Note for WordPress.org Theme/Plugin reviewer:
672
		 *
673
		 *  This is a replication of `add_menu_page()` to avoid Theme Check warning.
674
		 *
675
		 *  Why?
676
		 *  ====
677
		 *  Freemius is an SDK for plugin and theme developers. Since the core
678
		 *  of the SDK is relevant both for plugins and themes, for obvious reasons,
679
		 *  we only develop and maintain one code base.
680
		 *
681
		 *  This method will not run for wp.org themes (only plugins) since theme
682
		 *  admin settings/options are now only allowed in the customizer.
683
		 *
684
		 *  If you have any questions or need clarifications, please don't hesitate
685
		 *  pinging me on slack, my username is @svovaf.
686
		 *
687
		 * @author Vova Feldman (@svovaf)
688
		 * @since  1.2.2
689
		 *
690
		 * @param string          $page_title The text to be displayed in the title tags of the page when the menu is
691
		 *                                    selected.
692
		 * @param string          $menu_title The text to be used for the menu.
693
		 * @param string          $capability The capability required for this menu to be displayed to the user.
694
		 * @param string          $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
695
		 * @param callable|string $function   The function to be called to output the content for this page.
696
		 * @param string          $icon_url   The URL to the icon to be used for this menu.
697
		 *                                    * Pass a base64-encoded SVG using a data URI, which will be colored to
698
		 *                                    match the color scheme. This should begin with
699
		 *                                    'data:image/svg+xml;base64,'.
700
		 *                                    * Pass the name of a Dashicons helper class to use a font icon,
701
		 *                                    e.g. 'dashicons-chart-pie'.
702
		 *                                    * Pass 'none' to leave div.wp-menu-image empty so an icon can be added
703
		 *                                    via CSS.
704
		 * @param int             $position   The position in the menu order this one should appear.
705
		 *
706
		 * @return string The resulting page's hook_suffix.
707
		 */
708
		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...
709
			$page_title,
710
			$menu_title,
711
			$capability,
712
			$menu_slug,
713
			$function = '',
714
			$icon_url = '',
715
			$position = null
716
		) {
717
			$fn = 'add_menu' . '_page';
718
719
			return $fn(
720
				$page_title,
721
				$menu_title,
722
				$capability,
723
				$menu_slug,
724
				$function,
725
				$icon_url,
726
				$position
727
			);
728
		}
729
730
		/**
731
		 * Add a submenu page.
732
		 *
733
		 * Note for WordPress.org Theme/Plugin reviewer:
734
		 *
735
		 *  This is a replication of `add_submenu_page()` to avoid Theme Check warning.
736
		 *
737
		 *  Why?
738
		 *  ====
739
		 *  Freemius is an SDK for plugin and theme developers. Since the core
740
		 *  of the SDK is relevant both for plugins and themes, for obvious reasons,
741
		 *  we only develop and maintain one code base.
742
		 *
743
		 *  This method will not run for wp.org themes (only plugins) since theme
744
		 *  admin settings/options are now only allowed in the customizer.
745
		 *
746
		 *  If you have any questions or need clarifications, please don't hesitate
747
		 *  pinging me on slack, my username is @svovaf.
748
		 *
749
		 * @author Vova Feldman (@svovaf)
750
		 * @since  1.2.2
751
		 *
752
		 * @param string          $parent_slug The slug name for the parent menu (or the file name of a standard
753
		 *                                     WordPress admin page).
754
		 * @param string          $page_title  The text to be displayed in the title tags of the page when the menu is
755
		 *                                     selected.
756
		 * @param string          $menu_title  The text to be used for the menu.
757
		 * @param string          $capability  The capability required for this menu to be displayed to the user.
758
		 * @param string          $menu_slug   The slug name to refer to this menu by (should be unique for this menu).
759
		 * @param callable|string $function    The function to be called to output the content for this page.
760
		 *
761
		 * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability
762
		 *                      required.
763
		 */
764
		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...
765
			$parent_slug,
766
			$page_title,
767
			$menu_title,
768
			$capability,
769
			$menu_slug,
770
			$function = ''
771
		) {
772
			$fn = 'add_submenu' . '_page';
773
774
			return $fn( $parent_slug,
775
				$page_title,
776
				$menu_title,
777
				$capability,
778
				$menu_slug,
779
				$function
780
			);
781
		}
782
	}