FS_Admin_Menu_Manager

The NextGEN Gallery FS Admin Menu Manager class.

Defined (1)

The class is defined in the following location(s).

/freemius/includes/managers/class-fs-admin-menu-manager.php  
  1. class FS_Admin_Menu_Manager { 
  2.  
  3. #region Properties 
  4.  
  5. /** 
  6. * @var string 
  7. */ 
  8. protected $_plugin_slug; 
  9.  
  10. /** 
  11. * @since 1.0.6 
  12. * @var string 
  13. */ 
  14. private $_menu_slug; 
  15. /** 
  16. * @since 1.1.3 
  17. * @var string 
  18. */ 
  19. private $_parent_slug; 
  20. /** 
  21. * @since 1.1.3 
  22. * @var string 
  23. */ 
  24. private $_parent_type; 
  25. /** 
  26. * @since 1.1.3 
  27. * @var string 
  28. */ 
  29. private $_type; 
  30. /** 
  31. * @since 1.1.3 
  32. * @var bool 
  33. */ 
  34. private $_is_top_level; 
  35. /** 
  36. * @since 1.1.3 
  37. * @var bool 
  38. */ 
  39. private $_is_override_exact; 
  40. /** 
  41. * @since 1.1.3 
  42. * @var array<string, bool> 
  43. */ 
  44. private $_default_submenu_items; 
  45. /** 
  46. * @since 1.1.3 
  47. * @var string 
  48. */ 
  49. private $_first_time_path; 
  50.  
  51. #endregion Properties 
  52.  
  53. /** 
  54. * @var FS_Logger 
  55. */ 
  56. protected $_logger; 
  57.  
  58. #region Singleton 
  59.  
  60. /** 
  61. * @var FS_Admin_Menu_Manager[] 
  62. */ 
  63. private static $_instances = array(); 
  64.  
  65. /** 
  66. * @param string $plugin_slug 
  67. * @return FS_Admin_Notice_Manager 
  68. */ 
  69. static function instance( $plugin_slug ) { 
  70. if ( ! isset( self::$_instances[ $plugin_slug ] ) ) { 
  71. self::$_instances[ $plugin_slug ] = new FS_Admin_Menu_Manager( $plugin_slug ); 
  72.  
  73. return self::$_instances[ $plugin_slug ]; 
  74.  
  75. protected function __construct( $plugin_slug ) { 
  76. $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $plugin_slug . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); 
  77.  
  78. $this->_plugin_slug = $plugin_slug; 
  79.  
  80. #endregion Singleton 
  81.  
  82. #region Helpers 
  83.  
  84. private function get_option( &$options, $key, $default = false ) { 
  85. return ! empty( $options[ $key ] ) ? $options[ $key ] : $default; 
  86.  
  87. private function get_bool_option( &$options, $key, $default = false ) { 
  88. return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default; 
  89.  
  90. #endregion Helpers 
  91.  
  92. /** 
  93. * @param array $menu 
  94. * @param bool $is_addon 
  95. */ 
  96. function init( $menu, $is_addon = false ) { 
  97. $this->_menu_slug = $menu['slug']; 
  98.  
  99. $this->_default_submenu_items = array(); 
  100. // @deprecated 
  101. $this->_type = 'page'; 
  102. $this->_is_top_level = true; 
  103. $this->_is_override_exact = false; 
  104. $this->_parent_slug = false; 
  105. // @deprecated 
  106. $this->_parent_type = 'page'; 
  107.  
  108. if ( ! $is_addon && isset( $menu ) ) { 
  109. $this->_default_submenu_items = array( 
  110. 'contact' => $this->get_bool_option( $menu, 'contact', true ),  
  111. 'support' => $this->get_bool_option( $menu, 'support', true ),  
  112. 'account' => $this->get_bool_option( $menu, 'account', true ),  
  113. 'pricing' => $this->get_bool_option( $menu, 'pricing', true ),  
  114. 'addons' => $this->get_bool_option( $menu, 'addons', true ),  
  115. ); 
  116.  
  117. // @deprecated 
  118. $this->_type = $this->get_option( $menu, 'type', 'page' ); 
  119. $this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' ); 
  120.  
  121. if ( isset( $menu['parent'] ) ) { 
  122. $this->_parent_slug = $this->get_option( $menu['parent'], 'slug' ); 
  123. // @deprecated 
  124. $this->_parent_type = $this->get_option( $menu['parent'], 'type', 'page' ); 
  125.  
  126. // If parent's slug is different, then it's NOT a top level menu item. 
  127. $this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug ); 
  128. } else { 
  129. /** 
  130. * If no parent then top level if: 
  131. * - Has custom admin menu ('page') 
  132. * - CPT menu type ('cpt') 
  133. */ 
  134. // $this->_is_top_level = in_array( $this->_type, array( 
  135. // 'cpt',  
  136. // 'page' 
  137. // ) ); 
  138.  
  139. $this->_first_time_path = $this->get_option( $menu, 'first-path', false ); 
  140. if ( ! empty( $this->_first_time_path ) && is_string( $this->_first_time_path ) ) { 
  141. $this->_first_time_path = admin_url( $this->_first_time_path, 'admin' ); 
  142.  
  143. /** 
  144. * Check if top level menu. 
  145. * @author Vova Feldman (@svovaf) 
  146. * @since 1.1.3 
  147. * @return bool False if submenu item. 
  148. */ 
  149. function is_top_level() { 
  150. return $this->_is_top_level; 
  151.  
  152. /** 
  153. * Check if the page should be override on exact URL match. 
  154. * @author Vova Feldman (@svovaf) 
  155. * @since 1.1.3 
  156. * @return bool False if submenu item. 
  157. */ 
  158. function is_override_exact() { 
  159. return $this->_is_override_exact; 
  160.  
  161.  
  162. /** 
  163. * Get the path of the page the user should be forwarded to after first activation. 
  164. * @author Vova Feldman (@svovaf) 
  165. * @since 1.1.3 
  166. * @return string 
  167. */ 
  168. function get_first_time_path() { 
  169. return $this->_first_time_path; 
  170.  
  171. /** 
  172. * Check if plugin's menu item is part of a custom top level menu. 
  173. * @author Vova Feldman (@svovaf) 
  174. * @since 1.1.3 
  175. * @return bool 
  176. */ 
  177. function has_custom_parent() { 
  178. return ! $this->_is_top_level && is_string( $this->_parent_slug ); 
  179.  
  180. /** 
  181. * @author Vova Feldman (@svovaf) 
  182. * @since 1.1.3 
  183. * @return string 
  184. */ 
  185. // function slug() { 
  186. // return $this->_menu_slug; 
  187. // } 
  188.  
  189. /** 
  190. * @author Vova Feldman (@svovaf) 
  191. * @since 1.1.3 
  192. * @param string $id 
  193. * @param bool $default 
  194. * @return bool 
  195. */ 
  196. function is_submenu_item_visible( $id, $default = true ) { 
  197. return fs_apply_filter( 
  198. $this->_plugin_slug,  
  199. 'is_submenu_visible',  
  200. $this->get_bool_option( $this->_default_submenu_items, $id, $default ),  
  201. $id 
  202. ); 
  203.  
  204. /** 
  205. * Calculates admin settings menu slug. 
  206. * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug. 
  207. * @author Vova Feldman (@svovaf) 
  208. * @since 1.1.3 
  209. * @param string $page 
  210. * @return string 
  211. */ 
  212. function get_slug( $page = '' ) { 
  213. return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ? 
  214. $this->_menu_slug : 
  215. $this->_plugin_slug ) . ( empty( $page ) ? '' : ( '-' . $page ) ); 
  216.  
  217. /** 
  218. * @author Vova Feldman (@svovaf) 
  219. * @since 1.1.3 
  220. * @return string 
  221. */ 
  222. function get_parent_slug() { 
  223. return $this->_parent_slug; 
  224.  
  225. /** 
  226. * @author Vova Feldman (@svovaf) 
  227. * @since 1.1.3 
  228. * @return string 
  229. */ 
  230. function get_type() { 
  231. return $this->_type; 
  232.  
  233. /** 
  234. * @author Vova Feldman (@svovaf) 
  235. * @since 1.1.3 
  236. * @return bool 
  237. */ 
  238. function is_cpt() { 
  239. return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) || 
  240. // Back compatibility. 
  241. 'cpt' === $this->_type 
  242. ); 
  243.  
  244. /** 
  245. * @author Vova Feldman (@svovaf) 
  246. * @since 1.1.3 
  247. * @return string 
  248. */ 
  249. function get_parent_type() { 
  250. return $this->_parent_type; 
  251.  
  252. /** 
  253. * @author Vova Feldman (@svovaf) 
  254. * @since 1.1.3 
  255. * @return string 
  256. */ 
  257. function get_raw_slug() { 
  258. return $this->_menu_slug; 
  259.  
  260. /** 
  261. * Get plugin's original menu slug. 
  262. * @author Vova Feldman (@svovaf) 
  263. * @since 1.1.3 
  264. * @return string 
  265. */ 
  266. function get_original_menu_slug() { 
  267. if ( 'cpt' === $this->_type ) { 
  268. return add_query_arg( array( 
  269. 'post_type' => $this->_menu_slug 
  270. ), 'edit.php' ); 
  271.  
  272. if ( false === strpos( $this->_menu_slug, '.php?' ) ) { 
  273. return $this->_menu_slug; 
  274. } else { 
  275. return $this->_plugin_slug; 
  276.  
  277. /** 
  278. * @author Vova Feldman (@svovaf) 
  279. * @since 1.1.3 
  280. * @return string 
  281. */ 
  282. function get_top_level_menu_slug() { 
  283. return $this->has_custom_parent() ? 
  284. $this->get_parent_slug() : 
  285. $this->get_raw_slug(); 
  286.  
  287. /** 
  288. * Is user on plugin's admin activation page. 
  289. * @author Vova Feldman (@svovaf) 
  290. * @since 1.0.8 
  291. * @return bool 
  292. */ 
  293. function is_activation_page() { 
  294. return isset( $_GET['page'] ) && 
  295. ( ( strtolower( $this->_menu_slug ) === strtolower( $_GET['page'] ) ) || 
  296. ( strtolower( $this->_plugin_slug ) === strtolower( $_GET['page'] ) ) ); 
  297.  
  298. #region Submenu Override 
  299.  
  300. /** 
  301. * Override submenu's action. 
  302. * @author Vova Feldman (@svovaf) 
  303. * @since 1.1.0 
  304. * @param string $parent_slug 
  305. * @param string $menu_slug 
  306. * @param callable $function 
  307. * @return false|string If submenu exist, will return the hook name. 
  308. */ 
  309. function override_submenu_action( $parent_slug, $menu_slug, $function ) { 
  310. global $submenu; 
  311.  
  312. $menu_slug = plugin_basename( $menu_slug ); 
  313. $parent_slug = plugin_basename( $parent_slug ); 
  314.  
  315. if ( ! isset( $submenu[ $parent_slug ] ) ) { 
  316. // Parent menu not exist. 
  317. return false; 
  318.  
  319. $found_submenu_item = false; 
  320. foreach ( $submenu[ $parent_slug ] as $submenu_item ) { 
  321. if ( $menu_slug === $submenu_item[2] ) { 
  322. $found_submenu_item = $submenu_item; 
  323. break; 
  324.  
  325. if ( false === $found_submenu_item ) { 
  326. // Submenu item not found. 
  327. return false; 
  328.  
  329. // Remove current function. 
  330. $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug ); 
  331. remove_all_actions( $hookname ); 
  332.  
  333. // Attach new action. 
  334. add_action( $hookname, $function ); 
  335.  
  336. return $hookname; 
  337.  
  338. #endregion Submenu Override 
  339.  
  340. #region Top level menu Override 
  341.  
  342. /** 
  343. * Find plugin's admin dashboard main menu item. 
  344. * @author Vova Feldman (@svovaf) 
  345. * @since 1.0.2 
  346. * @return string[]|false 
  347. */ 
  348. private function find_top_level_menu() { 
  349. global $menu; 
  350.  
  351. $position = - 1; 
  352. $found_menu = false; 
  353.  
  354. $menu_slug = $this->get_raw_slug(); 
  355.  
  356. $hook_name = get_plugin_page_hookname( $menu_slug, '' ); 
  357. foreach ( $menu as $pos => $m ) { 
  358. if ( $menu_slug === $m[2] ) { 
  359. $position = $pos; 
  360. $found_menu = $m; 
  361. break; 
  362.  
  363. if ( false === $found_menu ) { 
  364. return false; 
  365.  
  366. return array( 
  367. 'menu' => $found_menu,  
  368. 'position' => $position,  
  369. 'hook_name' => $hook_name 
  370. ); 
  371.  
  372. /** 
  373. * Remove all sub-menu items. 
  374. * @author Vova Feldman (@svovaf) 
  375. * @since 1.0.7 
  376. * @return bool If submenu with plugin's menu slug was found. 
  377. */ 
  378. private function remove_all_submenu_items() { 
  379. global $submenu; 
  380.  
  381. $menu_slug = $this->get_raw_slug(); 
  382.  
  383. if ( ! isset( $submenu[ $menu_slug ] ) ) { 
  384. return false; 
  385.  
  386. $submenu[ $menu_slug ] = array(); 
  387.  
  388. return true; 
  389.  
  390. /** 
  391. * @author Vova Feldman (@svovaf) 
  392. * @since 1.0.9 
  393. * @return array[string]mixed 
  394. */ 
  395. function remove_menu_item() { 
  396. $this->_logger->entrance(); 
  397.  
  398. // Find main menu item. 
  399. $menu = $this->find_top_level_menu(); 
  400.  
  401. if ( false === $menu ) { 
  402. return false; 
  403.  
  404. // Remove it with its actions. 
  405. remove_all_actions( $menu['hook_name'] ); 
  406.  
  407. // Remove all submenu items. 
  408. $this->remove_all_submenu_items(); 
  409.  
  410. return $menu; 
  411.  
  412. /** 
  413. * @author Vova Feldman (@svovaf) 
  414. * @since 1.1.4 
  415. * @param callable $function 
  416. * @return array[string]mixed 
  417. */ 
  418. function override_menu_item( $function ) { 
  419. $found_menu = $this->remove_menu_item(); 
  420.  
  421. if ( false === $found_menu ) { 
  422. return false; 
  423.  
  424. if ( ! $this->is_top_level() || ! $this->is_cpt() ) { 
  425. $menu_slug = plugin_basename( $this->get_slug() ); 
  426.  
  427. $hookname = get_plugin_page_hookname( $menu_slug, '' ); 
  428.  
  429. // Override menu action. 
  430. add_action( $hookname, $function ); 
  431. } else { 
  432. global $menu; 
  433.  
  434. // Remove original CPT menu. 
  435. unset( $menu[ $found_menu['position'] ] ); 
  436.  
  437. // Create new top-level menu action. 
  438. $hookname = add_menu_page( 
  439. $found_menu['menu'][3],  
  440. $found_menu['menu'][0],  
  441. 'manage_options',  
  442. $this->get_slug(),  
  443. $function,  
  444. $found_menu['menu'][6],  
  445. $found_menu['position'] 
  446. ); 
  447.  
  448. return $hookname; 
  449.  
  450. /** 
  451. * Adds a counter to the module's top level menu item. 
  452. * @author Vova Feldman (@svovaf) 
  453. * @since 1.2.1.5 
  454. * @param int $counter 
  455. * @param string $class 
  456. */ 
  457. function add_counter_to_menu_item( $counter = 1, $class = '' ) { 
  458. global $menu; 
  459.  
  460. // Find main menu item. 
  461. $found_menu = $this->find_top_level_menu(); 
  462.  
  463. if ( false === $menu ) { 
  464. return; 
  465.  
  466. // Override menu label. 
  467. $menu[ $found_menu['position'] ][0] = $found_menu['menu'][0] . ' <span class="update-plugins ' . $class . ' count-' . $counter . '"><span>' . $counter . '</span></span>'; 
  468.  
  469. #endregion Top level menu Override