JoryHogeveen/view-admin-as

View on GitHub
includes/class-api.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * View Admin As - Class API
 *
 * @author  Jory Hogeveen <info@keraweb.nl>
 * @package View_Admin_As
 */

if ( ! defined( 'VIEW_ADMIN_AS_DIR' ) ) {
    die();
}

/**
 * API class that is also extends utility functions.
 *
 * @author  Jory Hogeveen <info@keraweb.nl>
 * @package View_Admin_As
 * @since   1.6.0
 * @version 1.8.7
 * @uses    \VAA_Util Extends class
 */
final class VAA_API extends VAA_Util
{
    /**
     * Check if a user has full access to this plugin.
     *
     * @since   1.8.0
     * @access  public
     * @static
     * @api
     *
     * @param   \WP_User|int  $user  The user to check.
     * @return  bool
     */
    public static function user_has_full_access( $user ) {
        if ( ! $user instanceof WP_User ) {
            $user = get_user_by( 'ID', $user );
            if ( ! $user ) {
                return false;
            }
        }

        if ( is_multisite() ) {
            return is_super_admin( $user->ID );
        }

        /**
         * For single installations is_super_admin() isn't enough since it only checks for `delete_users`.
         * @since  1.7.6
         * @link   https://wordpress.org/support/topic/required-capabilities-2/
         */
        $caps = array(
            'edit_users',
            'delete_plugins',
        );

        /**
         * Filter the capabilities required to gain full access to this plugin.
         * Note: Single site only!
         * Note: is_super_admin() is always checked!
         *
         * @since  1.8.0
         * @param  array     $caps  The default capabilities.
         * @param  \WP_User  $user  The user that is being validated.
         * @return array
         */
        $caps = apply_filters( 'view_admin_as_full_access_capabilities', $caps, $user );

        foreach ( $caps as $cap ) {
            if ( ! $user->has_cap( $cap ) ) {
                return false;
            }
        }

        return is_super_admin( $user->ID );
    }

    /**
     * Check if the user is a super admin.
     * This check is more strict for single installations since it checks VAA_API::user_has_full_access.
     * It will validate the original user while in a view and no parameter is passed.
     *
     * @see  \VAA_API::user_has_full_access()
     * @see  \VAA_View_Admin_As_Store::cur_user_has_full_access()
     *
     * @since   1.6.3
     * @since   1.8.0  Check full access.
     * @access  public
     * @static
     * @api
     *
     * @param   int|\WP_User  $user  (optional) Default: current user.
     * @return  bool
     */
    public static function is_super_admin( $user = null ) {
        if ( null === $user || view_admin_as()->store()->is_curUser( $user ) ) {
            return view_admin_as()->store()->cur_user_has_full_access();
        }

        return self::user_has_full_access( $user );
    }

    /**
     * Check if the user is a superior admin.
     * It will validate the original user while in a view and no parameter is passed.
     *
     * @since   1.5.3
     * @since   1.6.0  Moved from `VAA_View_Admin_As`.
     * @since   1.6.3  Improve is_super_admin() check
     * @since   1.8.0  Enhance code to reflect VAA_API::is_super_admin() changes.
     * @access  public
     * @static
     * @api
     *
     * @param   int|\WP_User  $user_id  (optional) Default: current user.
     * @return  bool
     */
    public static function is_superior_admin( $user_id = null ) {

        // If it's the current user or null, don't pass the user ID to make sure we check the original user status.
        $is_super_admin = self::is_super_admin(
            ( null !== $user_id && view_admin_as()->store()->is_curUser( $user_id ) ) ? null : $user_id
        );

        // Full access is required.
        if ( ! $is_super_admin ) {
            return false;
        }

        if ( null === $user_id ) {
            $user_id = view_admin_as()->store()->get_originalUserData( 'ID' );
        } elseif ( $user_id instanceof WP_User ) {
            $user_id = $user_id->ID;
        }

        // Is it one of the manually configured superior admins?
        return (bool) ( in_array( (int) $user_id, self::get_superior_admins(), true ) );
    }

    /**
     * Get the superior admin ID's (filter since 1.5.2).
     *
     * @since   1.5.3
     * @since   1.6.0  Moved from `VAA_View_Admin_As`.
     * @access  public
     * @static
     * @api
     *
     * @return int[]
     */
    public static function get_superior_admins() {
        static $superior_admins;
        if ( ! is_null( $superior_admins ) ) return $superior_admins;

        /**
         * Grant admins the capability to view other admins. There is no UI for this!
         *
         * @since  1.5.2
         * @param  array
         * @return int[] Requires a returned array of user ID's
         */
        $superior_admins = (array) apply_filters( 'view_admin_as_superior_admins', array() );

        // Only allow unique  numeric values (user id's).
        $superior_admins = array_unique( array_map( 'absint', array_filter( $superior_admins, 'is_numeric' ) ) );

        return $superior_admins;
    }

    /**
     * Get the current active view. Returns `null` if no view (type) is active.
     *
     * @see  \VAA_View_Admin_As_Store::get_view()
     *
     * @since   1.8.3
     * @access  public
     * @static
     * @api
     *
     * @param   string  $type  (optional) A view type. Will return `null` if this view type is not active.
     * @return  mixed
     */
    public static function get_current_view( $type = null ) {
        $store = view_admin_as()->store();
        if ( $store ) {
            return $store->get_view( $type );
        }
        return null;
    }

    /**
     * Get the human readable name(s) of a view.
     *
     * @since   1.8.7
     * @access  public
     * @static
     * @api
     *
     * @param   array         $view  (optional) View data. Will use the current view if omitted.
     * @param   string|array  $type  (optional) A view type. Will return `null` if this view type is not active.
     * @return  array
     */
    public static function get_view_titles( $view = array(), $type = null ) {
        if ( ! $view ) {
            $view = self::get_current_view();
        }
        $view = self::get_array_data( $view, $type );

        if ( ! $view ) {
            return array();
        }

        $titles = array();

        /**
         * Filter what to show when a view is applied.
         *
         * @hooked
         * 5:   user
         * 8:   role
         * 10:  group (Groups)
         * 10:  rua_level (Restrict User Access)
         * 80:  caps
         * 90:  locale (Languages)
         * 999: role defaults (appends an icon)
         *
         * @since  1.8.7
         *
         * @param  array  $titles   The current title(s).
         * @param  array  $view     The view data.
         *
         * @return array
         */
        $titles = apply_filters( 'vaa_view_admin_as_view_titles', $titles, $view );

        return $titles;
    }

    /**
     * Is the current user in an active view.
     *
     * @since   1.8.4
     * @access  public
     * @static
     * @api
     *
     * @param  string  $type  (optional) Check for a single view type.
     * @return bool
     */
    public static function is_view_active( $type = null ) {
        return (bool) self::get_current_view( $type );
    }

    /**
     * Check if the provided data is the same as the current view.
     *
     * @see  \VAA_View_Admin_As_Controller::is_current_view()
     *
     * @since   1.7.1
     * @access  public
     * @static
     * @api
     *
     * @param   mixed  $data
     * @param   bool   $type  Only compare a single view type instead of all view data?
     *                        If set, the data value should be the single view type data.
     *                        If data is `null` then it will return true if that view type is active.
     *                        If data is `false` then it will return true if this is the only active view type.
     * @return  bool
     */
    public static function is_current_view( $data, $type = null ) {
        $controller = view_admin_as()->controller();
        if ( $controller ) {
            return $controller->is_current_view( $data, $type );
        }
        return false;
    }

    /**
     * Is the current user modified?
     * Returns true if the currently active user's capabilities or roles are changed by the selected view.
     *
     * @see  \VAA_View_Admin_As_View::current_view_can()
     *
     * @since   1.7.2
     * @access  public
     * @static
     * @api
     *
     * @return  bool
     */
    public static function is_user_modified() {
        $view = view_admin_as()->view();
        if ( $view ) {
            return $view->is_user_modified();
        }
        return false;
    }

    /**
     * Similar function to current_user_can() but applies to the currently active view.
     *
     * @see  \VAA_View_Admin_As_View::current_view_can()
     *
     * @since   1.7.2
     * @access  public
     * @static
     * @api
     *
     * @param   string  $cap   The capability.
     * @param   array   $caps  (optional) Capabilities to compare to.
     *                         Defaults to the selected caps for the current view.
     * @return  bool
     */
    public static function current_view_can( $cap, $caps = array() ) {
        $view = view_admin_as()->view();
        if ( $view ) {
            return $view->current_view_can( $cap, $caps );
        }
        return false;
    }

    /**
     * Set the current view.
     *
     * @see  \VAA_View_Admin_As_Controller::update()
     * @see  \VAA_View_Admin_As_Controller::update_view()
     *
     * @since   1.8.3
     * @access  public
     * @static
     * @api
     *
     * @param   array  $view  The view.
     * @return  bool
     */
    public static function update_view( $view ) {
        $controller = view_admin_as()->controller();
        if ( $controller ) {
            $view    = array_intersect_key( $view, array_flip( $controller->get_view_types() ) );
            $success = $controller->update( $view );
            return ( true === $success );
        }
        return false;
    }

    /**
     * Check if a view type is enabled. Pass an array to check multiple view types.
     *
     * @since   1.8.0
     * @access  public
     * @static
     * @api
     *
     * @param   string|array  $type  The view type key.
     * @return  bool
     */
    public static function is_view_type_enabled( $type ) {
        $type = view_admin_as()->get_view_types( $type );
        if ( is_array( $type ) ) {
            foreach ( $type as $view_type ) {
                if ( ! $view_type instanceof VAA_View_Admin_As_Type || ! $view_type->is_enabled() ) {
                    return false;
                }
            }
            return true;
        }
        if ( $type instanceof VAA_View_Admin_As_Type ) {
            return $type->is_enabled();
        }
        return false;
    }

    /**
     * Generate a VAA action link.
     *
     * @since   1.7.0
     * @since   1.8.2  Switched $nonce and $url parameters order.
     * @access  public
     * @static
     * @api
     *
     * @param   array   $data   View type data.
     * @param   string  $url    (optional) A URL. Of not passed it will generate a link from the current URL.
     * @param   string  $nonce  (optional) Use a different nonce. Pass `false` to omit nonce.
     * @return  string
     */
    public static function get_vaa_action_link( $data, $url = null, $nonce = null ) {

        $params = array(
            'action'        => 'view_admin_as',
            'view_admin_as' => $data, // wp_json_encode( array( $type, $data ) ),
        );

        if ( null === $nonce ) {
            $nonce = view_admin_as()->store()->get_nonce( true );
        }
        if ( $nonce ) {
            $params['_vaa_nonce'] = (string) $nonce;
        }

        // @todo fix WP referrer/nonce checks and allow switching on any page without ajax.
        // @see https://codex.wordpress.org/Function_Reference/check_admin_referer
        if ( null === $url ) {
            if ( is_admin() ) {
                $url = is_network_admin() ? network_admin_url() : admin_url();
            } else {
                // Since  1.7.5  Frontend url.
                $url = get_site_url();
            }
        }

        $url = add_query_arg( $params, ( $url ) ? $url : false );

        return esc_url( $url, array( 'http', 'https' ) );
    }

    /**
     * Appends the "reset-view" parameter to the current URL.
     *
     * @since   1.6.0
     * @access  public
     * @static
     * @api
     *
     * @param   string  $url  (optional) Supply the URL to create the reset link.
     * @param   bool    $all  (optional) Reset all views link?
     * @return  string
     */
    public static function get_reset_link( $url = '', $all = false ) {
        $params = 'reset-view';
        if ( $all ) {
            $params = 'reset-all-views';
        }
        $url = add_query_arg( $params, '', ( $url ) ? $url : false );
        return esc_url( $url, array( 'http', 'https' ) );
    }

    /**
     * Removes the "reset-view" or "reset-all-views" parameter to the current URL.
     *
     * @since   1.6.0
     * @access  public
     * @static
     * @api
     *
     * @param   string  $url  (optional) Supply the URL to remove the reset link.
     * @return  string
     */
    public static function remove_reset_link( $url = '' ) {
        $url = remove_query_arg( array( 'reset-view', 'reset-all-views' ), ( $url ) ? $url : false );
        return esc_url( $url, array( 'http', 'https' ) );
    }

    /**
     * Is any toolbar showing?
     * Do not use this before the `init` hook.
     *
     * @since   1.7.0
     * @access  public
     * @static
     * @api
     *
     * @return  bool
     */
    public static function is_toolbar_showing() {
        return (bool) ( is_admin_bar_showing() || self::is_vaa_toolbar_showing() );
    }

    /**
     * Is our custom toolbar showing?
     * Do not use this before the `init` hook.
     *
     * @since   1.6.0
     * @access  public
     * @static
     * @api
     *
     * @return  bool
     */
    public static function is_vaa_toolbar_showing() {

        if ( class_exists( 'VAA_View_Admin_As_Toolbar' ) ) {
            return (bool) VAA_View_Admin_As_Toolbar::$showing;
        }
        return false;
    }

    /**
     * Enhanced is_admin() function with AJAX support.
     *
     * @see is_admin()
     *
     * @since   1.7.4
     * @access  public
     * @static
     * @api
     *
     * @return  bool
     */
    public static function is_admin() {
        if ( ! VAA_Util::doing_ajax() ) {
            return is_admin();
        }
        // It's an ajax call, is_admin() would always return `true`. Compare the referrer url with the admin url.
        return ( false !== strpos( (string) wp_get_referer(), admin_url() ) );
    }

    /**
     * Is the customizer admin container currently rendering?
     *
     * @since   1.7.6
     * @access  public
     * @static
     * @api
     *
     * @return  bool
     */
    public static function is_customizer_admin() {
        return (bool) ( is_customize_preview() && is_admin() );
    }


    /**
     * Is this a block editor page?
     *
     * @since   1.8.7
     * @access  public
     * @static
     * @api
     *
     * @return bool
     */
    public static function is_block_editor() {
        if ( ! function_exists( 'get_current_screen' ) ) {
            return false;
        }
        $screen = get_current_screen();
        if ( method_exists( $screen, 'is_block_editor' ) ) {
            return $screen->is_block_editor();
        }
        return false;
    }

    /**
     * Backwards compat method for apply_shortcodes() since WP 5.4.
     * @todo  deprecate when 5.4 is the minimum version of WP.
     *
     * @since  1.8.6
     * @param  string  $content
     * @param  bool    $ignore_html
     *
     * @return string
     */
    public static function apply_shortcodes( $content, $ignore_html = false ) {
        if ( function_exists( 'apply_shortcodes' ) ) {
            return apply_shortcodes( $content, $ignore_html );
        }
        return do_shortcode( $content, $ignore_html );
    }

    /**
     * Compare with the current WordPress version.
     * Returns true when it's the provided version or newer.
     *
     * @since   1.6.4
     * @since   1.7.2  Only check full version numbers by default.
     * @access  public
     * @static
     * @api
     *
     * @global  string      $wp_version          WordPress version.
     * @param   int|string  $version             The WP version to check.
     * @param   bool        $only_full_versions  Only validate full versions without dev notes (RC1, dev, etc).
     * @return  bool
     */
    public static function validate_wp_version( $version, $only_full_versions = true ) {
        global $wp_version;
        $version = strtolower( $version );
        $compare = strtolower( $wp_version );
        if ( $only_full_versions ) {
            // Only leave the version numbers.
            $version = explode( '-', $version );
            $version = $version[0];
            $compare = explode( '-', $compare );
            $compare = $compare[0];
        }
        return (bool) version_compare( $version, $compare, '<=' );
    }

} // End class VAA_API.