: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// No submenu items or action links for add-ons.
if ( $this->show_opt_in_on_themes_page() ) {
if ( ! fs_is_network_admin() ) {
// Also add action links or submenu items when running in a free .org theme so the tabs will be visible.
} else if ( $is_activation_mode ) {
// Don't show submenu-items/tabs in activation mode, unless it's a wp.org theme.
if ( fs_is_network_admin() ) {
* Add submenu items or action links to network level when plugin was network activated and the super
* admin did NOT delegate the connection of all sites to site admins.
$this->_is_network_active &&
( WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED ||
! $this->is_network_delegated_connection() )
return ( ! $this->_is_network_active || $this->is_delegated_connection() );
* Add default Freemius menu items.
* @author Vova Feldman (@svovaf)
* @since 1.2.2.7 Also add submenu items when running in a free .org theme so the tabs will be visible.
private function add_submenu_items() {
$this->_logger->entrance();
$is_activation_mode = $this->is_activation_mode();
$add_submenu_items = $this->should_add_submenu_or_action_links( $is_activation_mode );
if ( $add_submenu_items ) {
if ( $this->has_affiliate_program() ) {
$this->get_text_inline( 'Affiliation', 'affiliation' ),
array( &$this, '_affiliation_page_render' ),
$this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Affiliation', 'affiliation' ),
'Freemius::_clean_admin_content_section',
$this->is_submenu_item_visible( 'affiliation' )
if ( $add_submenu_items ||
$this->is_only_premium() &&
$this->is_admin_page( 'account' ) &&
fs_request_is_action( $this->get_unique_affix() . '_sync_license' )
if ( ! WP_FS__DEMO_MODE && $this->is_registered() ) {
$this->is_submenu_item_visible( 'account' ) &&
* @since 1.2.2.7 Don't show the Account for free WP.org themes without any paid plans.
( ! $this->is_free_wp_org_theme() || $this->has_paid_plan() )
// Add user account page.
$this->get_text_inline( 'Account', 'account' ),
array( &$this, '_account_page_render' ),
$this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Account', 'account' ),
array( &$this, '_account_page_load' ),
( $add_submenu_items && $show_account )
if ( $add_submenu_items ) {
if (! WP_FS__DEMO_MODE && ! $this->is_whitelabeled() ) {
$this->get_text_inline( 'Contact Us', 'contact-us' ),
array( &$this, '_contact_page_render' ),
$this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Contact Us', 'contact-us' ),
'Freemius::_clean_admin_content_section',
$this->is_submenu_item_visible( 'contact' )
if ( $this->has_addons() ) {
$this->get_text_inline( 'Add-Ons', 'add-ons' ),
array( &$this, '_addons_page_render' ),
$this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Add-Ons', 'add-ons' ),
array( &$this, '_addons_page_load' ),
WP_FS__LOWEST_PRIORITY - 1,
$this->is_submenu_item_visible( 'addons' )
if ( $add_submenu_items ||
( $is_activation_mode && $this->is_only_premium() && $this->is_admin_page( 'pricing' ) )
if (! WP_FS__DEMO_MODE && ! $this->is_whitelabeled() ) {
$this->is_submenu_item_visible( 'pricing' ) &&
$this->is_pricing_page_visible()
$pricing_cta_text = $this->get_pricing_cta_label();
$pricing_class = 'upgrade-mode';
if ( $this->is_in_trial_promotion() &&
! $this->is_paying_or_trial()
// If running a trial promotion, modify the pricing to load the trial.
$pricing_class = 'trial-mode';
} else if ( $this->is_paying() ) {
// Add upgrade/pricing page.
$pricing_cta_text . ' ' . ( is_rtl() ? $this->get_text_x_inline( '←', 'ASCII arrow left icon', 'symbol_arrow-left' ) : $this->get_text_x_inline( '➤', 'ASCII arrow right icon', 'symbol_arrow-right' ) ),
array( &$this, '_pricing_page_render' ),
$this->get_plugin_name() . ' – ' . $this->get_text_x_inline( 'Pricing', 'noun', 'pricing' ),
'Freemius::_clean_admin_content_section',
( $add_submenu_items && $show_pricing ),
if ( ! $is_activation_mode || ( true !== $this->_storage->require_license_activation ) ) {
* Add the other menu items if there are any when not in activation mode or license activation is not
* required (license activation is required for registered or anonymous users after activating the
* premium version when the site is not in trial mode or there's no active valid license).
* @author Leo Fajardo (@leorw)
if ( 0 < count( $this->_menu_items ) ) {
if ( ! $this->_menu->is_top_level() ) {
fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
// Append submenu items right after the plugin's submenu item.
$this->order_sub_submenu_items();
$this->embed_submenu_items();
* Moved the actual submenu item additions to a separated function,
* in order to support sub-submenu items when the plugin's settings
* only have a submenu and not top-level menu item.
* @author Vova Feldman (@svovaf)
private function embed_submenu_items() {
$item_template = $this->_menu->is_top_level() ?
'<span class="fs-submenu-item %s %s %s">%s</span>' :
'<span class="fs-submenu-item fs-sub %s %s %s">%s</span>';
$top_level_menu_capability = $this->get_top_level_menu_capability();
ksort( $this->_menu_items );
$is_first_submenu_item = true;
foreach ( $this->_menu_items as $priority => $items ) {
foreach ( $items as $item ) {
$capability = ( ! empty( $item['capability'] ) ? $item['capability'] : $top_level_menu_capability );
$this->get_unique_affix(),
! empty( $item['class'] ) ? $item['class'] : '',
$top_level_menu_slug = $this->get_top_level_menu_slug();
$menu_slug = $this->_menu->get_slug( $item['menu_slug'] );
if ( ! isset( $item['url'] ) ) {
$hook = FS_Admin_Menu_Manager::add_subpage(
if ( false !== $item['before_render_function'] ) {
add_action( "load-$hook", $item['before_render_function'] );
FS_Admin_Menu_Manager::add_subpage(
if ( $item['show_submenu'] && $is_first_submenu_item ) {
if ( $this->_is_network_active && ! empty( $this->_dynamically_added_top_level_page_hook_name ) ) {
* If the top-level menu has been dynamically created, remove the first submenu item that
* WordPress automatically creates when there's no submenu item whose slug matches the
* parent's. In the following example, the `Awesome Plugin` submenu item will be removed.
* - Awesome Plugin <-- we want to remove this since there's no real setting page for the top-level
* @author Leo Fajardo (@leorw)
remove_submenu_page( $top_level_menu_slug, $top_level_menu_slug );
$is_first_submenu_item = false;
* Re-order the submenu items so all Freemius added new submenu items
* are added right after the plugin's settings submenu item.
* @author Vova Feldman (@svovaf)
private function order_sub_submenu_items() {
$menu_slug = $this->_menu->get_top_level_menu_slug();
* Before "admin_menu" fires, WordPress will loop over the default submenus and remove pages for which the user
* does not have permissions. So in case a plugin does not have top-level menu but does have submenus under any
* of the default menus, only users that have the right role can access its sub-submenus (Account, Contact Us,
* Support Forum, etc.) since $submenu[ $menu_slug ] will be empty if the user doesn't have permission.
* In case a plugin does not have submenus under any of the default menus but does have submenus under the menu
* of another plugin, only users that have the right role can access its sub-submenus since we will use the
* capability needed to access the parent menu as the capability for the submenus that we will add.
if ( empty( $submenu[ $menu_slug ] ) ) {
$top_level_menu = &$submenu[ $menu_slug ];
$all_submenu_items_after = array();
$found_submenu_item = false;
foreach ( $top_level_menu as $submenu_id => $meta ) {
if ( $found_submenu_item ) {
// Remove all submenu items after the plugin's submenu item.
$all_submenu_items_after[] = $meta;
unset( $top_level_menu[ $submenu_id ] );
if ( $this->_menu->get_raw_slug() === $meta[2] ) {
// Found the submenu item, put all below.
$found_submenu_item = true;
// Embed all plugin's new submenu items.
$this->embed_submenu_items();
// Start with specially high number to make sure it's appended.
$i = max( 10000, max( array_keys( $top_level_menu ) ) + 1 );
foreach ( $all_submenu_items_after as $meta ) {
$top_level_menu[ $i ] = $meta;
ksort( $top_level_menu );
* Helper method to return the module's support forum URL.
* @author Vova Feldman (@svovaf)
function get_support_forum_url() {
return $this->apply_filters( 'support_forum_url', "https://wordpress.org/support/{$this->_module_type}/{$this->_slug}" );
* Displays the Support Forum link when enabled.
* Can be filtered like so:
* function _fs_show_support_menu( $is_visible, $menu_id ) {
* if ( 'support' === $menu_id ) {
* return _fs->is_registered();
* _fs()->add_filter('is_submenu_visible', '_fs_show_support_menu', 10, 2);
function _add_default_submenu_items() {
if ( ! $this->is_on() ) {
if ( ! $this->is_activation_mode() &&
( ( $this->_is_network_active && fs_is_network_admin() ) ||
( ! $this->_is_network_active && is_admin() ) )
$this->add_submenu_link_item(
$this->apply_filters( 'support_forum_submenu', $this->get_text_inline( 'Support Forum', 'support-forum' ) ),
$this->get_support_forum_url(),
$this->is_submenu_item_visible( 'support' )
* @author Vova Feldman (@svovaf)
* @param string $menu_title
* @param callable $render_function
* @param bool|string $page_title
* @param string $capability
* @param bool|string $menu_slug
* @param bool|callable $before_render_function
* @param bool $show_submenu
* @param string $class Since 1.2.1.5 can add custom classes to menu items.
function add_submenu_item(
$capability = 'manage_options',
$before_render_function = false,
$priority = WP_FS__DEFAULT_PRIORITY,
$this->_logger->entrance( 'Title = ' . $menu_title );
if ( $this->is_addon() ) {
$parent_fs = $this->get_parent_instance();
if ( is_object( $parent_fs ) ) {
$parent_fs->add_submenu_item(
if ( ! isset( $this->_menu_items[ $priority ] ) ) {
$this->_menu_items[ $priority ] = array();
$this->_menu_items[ $priority ][] = array(
'page_title' => is_string( $page_title ) ? $page_title : $menu_title,
'menu_title' => $menu_title,
'capability' => $capability,
'menu_slug' => is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ),
'render_function' => $render_function,
'before_render_function' => $before_render_function,
'show_submenu' => $show_submenu,
* @author Vova Feldman (@svovaf)
* @param string $menu_title
* @param string $capability
* @param bool $show_submenu
function add_submenu_link_item(
$priority = WP_FS__DEFAULT_PRIORITY,
$this->_logger->entrance( 'Title = ' . $menu_title . '; Url = ' . $url );
if ( $this->is_addon() ) {
$parent_fs = $this->get_parent_instance();
if ( is_object( $parent_fs ) ) {
$parent_fs->add_submenu_link_item(
if ( ! isset( $this->_menu_items[ $priority ] ) ) {
$this->_menu_items[ $priority ] = array();
$this->_menu_items[ $priority ][] = array(
'menu_title' => $menu_title,
'capability' => $capability,
'menu_slug' => is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ),
'page_title' => $menu_title,
'render_function' => 'fs_dummy',
'before_render_function' => '',
'show_submenu' => $show_submenu,
#endregion ------------------------------------------------------------------
#--------------------------------------------------------------------------------
#--------------------------------------------------------------------------------
* @author Vova Feldman (@svovaf)
* @param string|string[] $ids
* @param int|null $network_level_or_blog_id
* @uses FS_Admin_Notices::remove_sticky()
function remove_sticky( $ids, $network_level_or_blog_id = null ) {