8889841cDashboard.php000064400000005014150515521760007153 0ustar00context = $context; $this->assets = $assets ?: new Assets( $this->context ); $this->modules = $modules ?: new Modules( $this->context ); } /** * Registers functionality through WordPress hooks. * * @since 1.0.0 */ public function register() { add_action( 'wp_dashboard_setup', function () { $this->add_widgets(); } ); } /** * Add a Site Kit by Google widget to the WordPress admin dashboard. * * @since 1.0.0 */ private function add_widgets() { if ( ! current_user_can( Permissions::VIEW_WP_DASHBOARD_WIDGET ) ) { return; } // Enqueue styles. $this->assets->enqueue_asset( 'googlesitekit-wp-dashboard-css' ); // Enqueue scripts. $this->assets->enqueue_asset( 'googlesitekit-wp-dashboard' ); $this->modules->enqueue_assets(); wp_add_dashboard_widget( 'google_dashboard_widget', __( 'Site Kit Summary', 'google-site-kit' ), function () { $this->render_googlesitekit_wp_dashboard(); } ); } /** * Render the Site Kit WordPress Dashboard widget. * * @since 1.0.0 */ private function render_googlesitekit_wp_dashboard() { $this->render_noscript_html(); ?>
slug = $slug; $this->args = wp_parse_args( $args, array( 'title' => '', 'content' => '', 'target_id' => '', 'position' => 'top', 'active_callback' => null, ) ); } /** * Gets the pointer slug. * * @since 1.83.0 * * @return string Unique pointer slug. */ public function get_slug() { return $this->slug; } /** * Gets the pointer title. * * @since 1.83.0 * * @return string Pointer title. */ public function get_title() { return $this->args['title']; } /** * Gets the pointer content. * * @since 1.83.0 * * @return string Pointer content. */ public function get_content() { if ( is_callable( $this->args['content'] ) ) { return call_user_func( $this->args['content'] ); } else { return '

' . wp_kses( $this->args['content'], 'googlesitekit_admin_pointer' ) . '

'; } } /** * Gets the pointer target ID. * * @since 1.83.0 * * @return string Pointer target ID. */ public function get_target_id() { return $this->args['target_id']; } /** * Gets the pointer position. * * @since 1.83.0 * * @return string|array Pointer position. */ public function get_position() { return $this->args['position']; } /** * Checks whether the pointer is active. * * This method executes the active callback in order to determine whether the pointer should be active or not. * * @since 1.83.0 * * @param string $hook_suffix The current admin screen hook suffix. * @return bool True if the pointer is active, false otherwise. */ public function is_active( $hook_suffix ) { if ( empty( $this->args['title'] ) || empty( $this->args['content'] ) || empty( $this->args['target_id'] ) ) { return false; } if ( ! is_callable( $this->args['active_callback'] ) ) { return true; } return (bool) call_user_func( $this->args['active_callback'], $hook_suffix ); } } Screen.php000064400000016352150515521760006512 0ustar00slug = $slug; $this->args = wp_parse_args( $args, array( 'render_callback' => null, 'title' => '', 'capability' => 'manage_options', 'menu_title' => '', 'parent_slug' => self::MENU_SLUG, 'enqueue_callback' => null, 'initialize_callback' => null, ) ); if ( empty( $this->args['menu_title'] ) ) { $this->args['menu_title'] = $this->args['title']; } $this->args['title'] = __( 'Site Kit by Google', 'google-site-kit' ) . ' ' . $this->args['title']; } /** * Gets the unique screen slug. * * @since 1.0.0 * * @return string Unique screen slug. */ public function get_slug() { return $this->slug; } /** * Adds the screen to the WordPress admin backend. * * @since 1.0.0 * * @param Context $context Plugin context, used for URL generation. * @return string Hook suffix of the screen, or empty string if not added. */ public function add( Context $context ) { static $menu_slug = null; if ( ! $this->args['render_callback'] || ! $this->args['title'] ) { return ''; } // A parent slug of null means the screen will not appear in the menu. $parent_slug = null; // If parent slug is provided, use it as parent. if ( ! empty( $this->args['parent_slug'] ) ) { $parent_slug = $this->args['parent_slug']; // If parent slug is 'googlesitekit', append to main Site Kit menu. if ( self::MENU_SLUG === $parent_slug ) { // If this is null, it means no menu has been added yet. if ( null === $menu_slug ) { add_menu_page( $this->args['title'], __( 'Site Kit', 'google-site-kit' ), $this->args['capability'], $this->slug, '', 'data:image/svg+xml;base64,' . Google_Icon::to_base64() ); $menu_slug = $this->slug; /** * An SVG icon file needs to be colored (filled) based on the theme color setting. * * This exists in js as wp.svgPainter() per: * https://github.com/WordPress/WordPress/blob/5.7/wp-admin/js/svg-painter.js * * The downside of the js approach is that we get a brief flash of an unstyled icon * until the JS runs. * * A user can pick a custom Admin Color Scheme, which is only available in admin_init * or later actions. add_menu_page runs on the admin_menu action, which precedes admin_init * per https://codex.wordpress.org/Plugin_API/Action_Reference * * WordPress provides some color schemes out of the box, but they can also be added via * wp_admin_css_color() * * Our workaround is to set the icon and subsequently replace it in current_screen, which is * what we do in the following action. */ add_action( 'current_screen', function() { global $menu, $_wp_admin_css_colors; if ( ! is_array( $menu ) ) { return; } $color_scheme = get_user_option( 'admin_color' ) ?: 'fresh'; // If we're on one of the sitekit pages, use the 'current' color, otherwise use the 'base' color. // @see wp_admin_css_color(). $color_key = false === strpos( get_current_screen()->id, 'googlesitekit' ) ? 'base' : 'current'; if ( empty( $_wp_admin_css_colors[ $color_scheme ]->icon_colors[ $color_key ] ) ) { return; } $color = $_wp_admin_css_colors[ $color_scheme ]->icon_colors[ $color_key ]; foreach ( $menu as &$item ) { if ( 'googlesitekit-dashboard' === $item[2] ) { $item[6] = 'data:image/svg+xml;base64,' . Google_Icon::to_base64( Google_Icon::with_fill( $color ) ); break; } } }, 100 ); } // Set parent slug to actual slug of main Site Kit menu. $parent_slug = $menu_slug; } } // If submenu item or not in menu, use add_submenu_page(). return (string) add_submenu_page( $parent_slug, $this->args['title'], $this->args['menu_title'], $this->args['capability'], $this->slug, function() use ( $context ) { $this->render( $context ); } ); } /** * Runs actions when initializing the screen, before sending headers and generating markup. * * @since 1.0.0 * * @param Context $context Plugin context. */ public function initialize( Context $context ) { if ( ! $this->args['initialize_callback'] ) { return; } call_user_func( $this->args['initialize_callback'], $context ); } /** * Enqueues assets for the screen. * * @since 1.0.0 * * @param Assets $assets Assets instance to rely on for enqueueing assets. */ public function enqueue_assets( Assets $assets ) { // Enqueue base admin screen stylesheet. $assets->enqueue_asset( 'googlesitekit-admin-css' ); if ( $this->args['enqueue_callback'] ) { call_user_func( $this->args['enqueue_callback'], $assets ); } } /** * Renders the screen content. * * @since 1.0.0 * * @param Context $context Plugin context. */ private function render( Context $context ) { if ( ! $this->args['render_callback'] ) { return; } ?>
render_noscript_html(); call_user_func( $this->args['render_callback'], $context ); ?>
slug = $slug; $this->args = wp_parse_args( $args, array( 'content' => '', 'type' => self::TYPE_INFO, 'active_callback' => null, 'dismissible' => false, ) ); } /** * Gets the notice slug. * * @since 1.0.0 * * @return string Unique notice slug. */ public function get_slug() { return $this->slug; } /** * Checks whether the notice is active. * * This method executes the active callback in order to determine whether the notice should be active or not. * * @since 1.0.0 * * @param string $hook_suffix The current admin screen hook suffix. * @return bool True if the notice is active, false otherwise. */ public function is_active( $hook_suffix ) { if ( ! $this->args['content'] ) { return false; } if ( ! $this->args['active_callback'] ) { return true; } return (bool) call_user_func( $this->args['active_callback'], $hook_suffix ); } /** * Renders the notice. * * @since 1.0.0 */ public function render() { if ( is_callable( $this->args['content'] ) ) { $content = call_user_func( $this->args['content'] ); if ( empty( $content ) ) { return; } } else { $content = '

' . wp_kses( $this->args['content'], 'googlesitekit_admin_notice' ) . '

'; } $class = 'notice notice-' . $this->args['type']; if ( $this->args['dismissible'] ) { $class .= ' is-dismissible'; } ?>
render_notices( $hook_suffix ); }; add_action( 'admin_notices', $callback ); add_action( 'network_admin_notices', $callback ); } /** * Renders admin notices. * * @since 1.0.0 * * @param string $hook_suffix The current admin screen hook suffix. */ private function render_notices( $hook_suffix ) { $notices = $this->get_notices(); if ( empty( $notices ) ) { return; } /** * Notice object. * * @var Notice $notice Notice object. */ foreach ( $notices as $notice ) { if ( ! $notice->is_active( $hook_suffix ) ) { continue; } $notice->render(); } } /** * Gets available admin notices. * * @since 1.0.0 * * @return array List of Notice instances. */ private function get_notices() { /** * Filters the list of available admin notices. * * @since 1.0.0 * * @param array $notices List of Notice instances. */ $notices = apply_filters( 'googlesitekit_admin_notices', array() ); return array_filter( $notices, function( $notice ) { return $notice instanceof Notice; } ); } } Plugin_Row_Meta.php000064400000002367150515521760010327 0ustar00get_plugin_row_meta() ); } return $meta; }, 10, 2 ); } /** * Builds an array of anchor elements to be shown in the plugin row. * * @since 1.24.0 * * @return string[] Array of links as HTML strings. */ private function get_plugin_row_meta() { return array( '' . __( 'Rate Site Kit', 'google-site-kit' ) . '', '' . __( 'Support', 'google-site-kit' ) . '', ); } } Standalone.php000064400000004561150515521760007362 0ustar00context = $context; } /** * Standalone mode * * @since 1.8.0 */ public function register() { if ( ! $this->is_standalone() ) { return; } /** * Appends the standalone admin body class. * * @since 1.8.0 * * @param string $admin_body_classes Admin body classes. * @return string Filtered admin body classes. */ add_filter( 'admin_body_class', function( $admin_body_classes ) { return "{$admin_body_classes} googlesitekit-standalone"; } ); remove_action( 'in_admin_header', 'wp_admin_bar_render', 0 ); add_filter( 'admin_footer_text', '__return_empty_string', PHP_INT_MAX ); add_filter( 'update_footer', '__return_empty_string', PHP_INT_MAX ); add_action( 'admin_head', function() { $this->print_standalone_styles(); } ); } /** * Detects if we are in Google Site Kit standalone mode. * * @since 1.8.0 * * @return boolean True when in standalone mode, else false. */ public function is_standalone() { global $pagenow; $page = $this->context->input()->filter( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); $standalone = $this->context->input()->filter( INPUT_GET, 'googlesitekit-standalone', FILTER_VALIDATE_BOOLEAN ); return ( 'admin.php' === $pagenow && false !== strpos( $page, 'googlesitekit' ) && $standalone ); } /** * Enqueues styles for standalone mode. * * @since 1.8.0 */ private function print_standalone_styles() { ?> get_method_proxy( 'enqueue_pointers' ) ); } /** * Enqueues pointer scripts. * * @since 1.83.0 * * @param string $hook_suffix The current admin page. */ private function enqueue_pointers( $hook_suffix ) { if ( empty( $hook_suffix ) ) { return; } $pointers = $this->get_pointers(); if ( empty( $pointers ) ) { return; } $active_pointers = array_filter( $pointers, function( Pointer $pointer ) use ( $hook_suffix ) { return $pointer->is_active( $hook_suffix ); } ); if ( empty( $active_pointers ) ) { return; } wp_enqueue_style( 'wp-pointer' ); wp_enqueue_script( 'wp-pointer' ); add_action( 'admin_print_footer_scripts', function() use ( $active_pointers ) { foreach ( $active_pointers as $pointer ) { $this->print_pointer_script( $pointer ); } } ); } /** * Gets pointers. * * @since 1.83.0 * * @return Pointer[] Array of pointers. */ private function get_pointers() { /** * Filters the list of available pointers. * * @since 1.83.0 * * @param array $pointers List of Pointer instances. */ $pointers = apply_filters( 'googlesitekit_admin_pointers', array() ); return array_filter( $pointers, function( $pointer ) { return $pointer instanceof Pointer; } ); } /** * Prints script for a given pointer. * * @since 1.83.0 * * @param Pointer $pointer Pointer to print. */ private function print_pointer_script( $pointer ) { $content = $pointer->get_content(); if ( empty( $content ) ) { return; } $slug = $pointer->get_slug(); BC_Functions::wp_print_inline_script_tag( sprintf( ' jQuery( function() { var options = { content: "

%s

%s", position: %s, pointerWidth: 420, close: function() { jQuery.post( window.ajaxurl, { pointer: "%s", action: "dismiss-wp-pointer", } ); } }; jQuery( "#%s" ).pointer( options ).pointer( "open" ); } ); ', esc_js( $pointer->get_title() ), $content, wp_json_encode( $pointer->get_position() ), esc_js( $slug ), esc_js( $pointer->get_target_id() ) ), array( 'id' => $slug, ) ); } } Plugin_Action_Links.php000064400000002455150515521760011165 0ustar00context = $context; } /** * Registers functionality through WordPress hooks. * * @since 1.41.0 */ public function register() { add_filter( 'plugin_action_links_' . GOOGLESITEKIT_PLUGIN_BASENAME, function ( $links ) { if ( current_user_can( Permissions::MANAGE_OPTIONS ) ) { $settings_link = sprintf( '%s', esc_url( $this->context->admin_url( 'settings' ) ), esc_html__( 'Settings', 'google-site-kit' ) ); array_unshift( $links, $settings_link ); }; return $links; } ); } } Screens.php000064400000034530150515521760006673 0ustar00 $screen pairs. * * @since 1.0.0 * @var array */ private $screens = array(); /** * Constructor. * * @since 1.0.0 * * @param Context $context Plugin context. * @param Assets $assets Optional. Assets API instance. Default is a new instance. * @param Modules $modules Optional. Modules instance. Default is a new instance. * @param Authentication $authentication Optional. Authentication instance. Default is a new instance. */ public function __construct( Context $context, Assets $assets = null, Modules $modules = null, Authentication $authentication = null ) { $this->context = $context; $this->assets = $assets ?: new Assets( $this->context ); $this->modules = $modules ?: new Modules( $this->context ); $this->authentication = $authentication ?: new Authentication( $this->context ); } /** * Registers functionality through WordPress hooks. * * @since 1.0.0 */ public function register() { if ( $this->context->is_network_mode() ) { add_action( 'network_admin_menu', function() { $this->add_screens(); } ); } add_action( 'admin_menu', function() { $this->add_screens(); } ); add_action( 'admin_enqueue_scripts', function( $hook_suffix ) { $this->enqueue_screen_assets( $hook_suffix ); } ); add_action( 'admin_page_access_denied', function() { // Redirect dashboard to splash if no dashboard access (yet). $this->no_access_redirect_dashboard_to_splash(); // Redirect splash to (shared) dashboard if splash is dismissed. $this->no_access_redirect_splash_to_dashboard(); // Redirect module pages to dashboard. $this->no_access_redirect_module_to_dashboard(); } ); // Ensure the menu icon always is rendered correctly, without enqueueing a global CSS file. add_action( 'admin_head', function() { ?> screens[ $hook_suffix ] ) ) { remove_all_actions( current_action() ); } }; add_action( 'admin_notices', $remove_notices_callback, -9999 ); add_action( 'network_admin_notices', $remove_notices_callback, -9999 ); add_action( 'all_admin_notices', $remove_notices_callback, -9999 ); add_filter( 'custom_menu_order', '__return_true' ); add_filter( 'menu_order', function( array $menu_order ) { // Move the Site Kit dashboard menu item to be one after the index.php item if it exists. $dashboard_index = array_search( 'index.php', $menu_order, true ); $sitekit_index = false; foreach ( $menu_order as $key => $value ) { if ( strpos( $value, self::PREFIX ) === 0 ) { $sitekit_index = $key; $sitekit_value = $value; break; } } if ( false === $dashboard_index || false === $sitekit_index ) { return $menu_order; } unset( $menu_order[ $sitekit_index ] ); array_splice( $menu_order, $dashboard_index + 1, 0, $sitekit_value ); return $menu_order; } ); } /** * Gets the Screen instance for a given hook suffix. * * @since 1.11.0 * * @param string $hook_suffix The hook suffix associated with the screen to retrieve. * @return Screen|null Screen instance if available, otherwise null; */ public function get_screen( $hook_suffix ) { return isset( $this->screens[ $hook_suffix ] ) ? $this->screens[ $hook_suffix ] : null; } /** * Adds all screens to the admin. * * @since 1.0.0 */ private function add_screens() { $screens = $this->get_screens(); array_walk( $screens, array( $this, 'add_screen' ) ); } /** * Adds the given screen to the admin. * * @since 1.0.0 * * @param Screen $screen Screen to add. */ private function add_screen( Screen $screen ) { $hook_suffix = $screen->add( $this->context ); if ( empty( $hook_suffix ) ) { return; } add_action( "load-{$hook_suffix}", function() use ( $screen ) { $screen->initialize( $this->context ); } ); $this->screens[ $hook_suffix ] = $screen; } /** * Enqueues assets if a plugin screen matches the given hook suffix. * * @since 1.0.0 * * @param string $hook_suffix Hook suffix for the current admin screen. */ private function enqueue_screen_assets( $hook_suffix ) { if ( ! isset( $this->screens[ $hook_suffix ] ) ) { return; } $this->screens[ $hook_suffix ]->enqueue_assets( $this->assets ); $this->modules->enqueue_assets(); } /** * Redirects from the dashboard to the splash screen if permissions to access the dashboard are currently not met. * * Dashboard permission access is conditional based on whether the user has successfully authenticated. When * e.g. accessing the dashboard manually or having it open in a separate tab while disconnecting in the other tab, * it is a better user experience to redirect to the splash screen so that the user can re-authenticate. * * The only time the dashboard should fail with the regular WordPress permissions error is when the current user is * not eligible for accessing Site Kit entirely, i.e. if they are not allowed to authenticate. * * @since 1.12.0 */ private function no_access_redirect_dashboard_to_splash() { global $plugin_page; // At this point, our preferred `$hook_suffix` is not set, and the dashboard page will not even be registered, // so we need to rely on the `$plugin_page` global here. if ( ! isset( $plugin_page ) || self::PREFIX . 'dashboard' !== $plugin_page ) { return; } if ( current_user_can( Permissions::VIEW_SPLASH ) ) { wp_safe_redirect( $this->context->admin_url( 'splash' ) ); exit; } } /** * Redirects from the splash to the dashboard screen if permissions to access the splash are currently not met. * * Admins always have the ability to view the splash page, so this redirects non-admins who have access * to view the shared dashboard if the splash has been dismissed. * Currently the dismissal check is built into the capability for VIEW_SPLASH so this is implied. * * @since 1.77.0 */ private function no_access_redirect_splash_to_dashboard() { global $plugin_page; if ( ! isset( $plugin_page ) || self::PREFIX . 'splash' !== $plugin_page ) { return; } if ( current_user_can( Permissions::VIEW_DASHBOARD ) ) { wp_safe_redirect( $this->context->admin_url() ); exit; } } /** * Redirects module pages to the dashboard or splash based on user capability. * * @since 1.69.0 */ private function no_access_redirect_module_to_dashboard() { global $plugin_page; $legacy_module_pages = array( self::PREFIX . 'module-adsense', self::PREFIX . 'module-analytics', self::PREFIX . 'module-search-console', ); if ( ! in_array( $plugin_page, $legacy_module_pages, true ) ) { return; } // Note: the use of add_query_arg is intentional below because it preserves // the current query parameters in the URL. if ( current_user_can( Permissions::VIEW_DASHBOARD ) ) { wp_safe_redirect( add_query_arg( 'page', self::PREFIX . 'dashboard' ) ); exit; } if ( current_user_can( Permissions::VIEW_SPLASH ) ) { wp_safe_redirect( add_query_arg( 'page', self::PREFIX . 'splash' ) ); exit; } } /** * Gets available admin screens. * * @since 1.0.0 * * @return array List of Screen instances. */ private function get_screens() { $screens = array( new Screen( self::PREFIX . 'dashboard', array( 'title' => __( 'Dashboard', 'google-site-kit' ), 'capability' => Permissions::VIEW_DASHBOARD, 'enqueue_callback' => function( Assets $assets ) { if ( $this->context->input()->filter( INPUT_GET, 'permaLink' ) ) { $assets->enqueue_asset( 'googlesitekit-entity-dashboard' ); } else { $assets->enqueue_asset( 'googlesitekit-main-dashboard' ); } }, 'render_callback' => function( Context $context ) { $is_view_only = ! $this->authentication->is_authenticated(); $setup_slug = $context->input()->filter( INPUT_GET, 'slug', FILTER_SANITIZE_STRING ); $reauth = $context->input()->filter( INPUT_GET, 'reAuth', FILTER_VALIDATE_BOOLEAN ); if ( $context->input()->filter( INPUT_GET, 'permaLink' ) ) { ?>
modules->get_active_modules(); if ( ! array_key_exists( $setup_module_slug, $active_modules ) ) { try { $module_details = $this->modules->get_module( $setup_module_slug ); /* translators: %s: The module name */ $message = sprintf( __( 'The %s module cannot be set up as it has not been activated yet.', 'google-site-kit' ), $module_details->name ); } catch ( \Exception $e ) { $message = $e->getMessage(); } wp_die( sprintf( '%s', esc_html( $message ) ), 403 ); } } ?>
__( 'Dashboard', 'google-site-kit' ), 'capability' => Permissions::VIEW_SPLASH, 'parent_slug' => $show_splash_in_menu ? Screen::MENU_SLUG : null, // This callback will redirect to the dashboard on successful authentication. 'initialize_callback' => function( Context $context ) { if ( Feature_Flags::enabled( 'dashboardSharing' ) ) { // Get the dismissed items for this user. $user_options = new User_Options( $context ); $dismissed_items = new Dismissed_Items( $user_options ); } $splash_context = $context->input()->filter( INPUT_GET, 'googlesitekit_context' ); $reset_session = $context->input()->filter( INPUT_GET, 'googlesitekit_reset_session', FILTER_VALIDATE_BOOLEAN ); // If the user is authenticated, redirect them to the disconnect URL and then send them back here. if ( ! $reset_session && 'revoked' === $splash_context && $this->authentication->is_authenticated() ) { $this->authentication->disconnect(); wp_safe_redirect( add_query_arg( array( 'googlesitekit_reset_session' => 1 ) ) ); exit; } // Don't consider redirect if the current user cannot access the dashboard (yet). if ( ! current_user_can( Permissions::VIEW_DASHBOARD ) ) { return; } // Redirect to dashboard if user is authenticated or if // they have already accessed the shared dashboard. if ( $this->authentication->is_authenticated() || ( Feature_Flags::enabled( 'dashboardSharing' ) && ! current_user_can( Permissions::AUTHENTICATE ) && $dismissed_items->is_dismissed( 'shared_dashboard_splash' ) && current_user_can( Permissions::VIEW_SHARED_DASHBOARD ) ) ) { wp_safe_redirect( $context->admin_url( 'dashboard', array( // Pass through the notification parameter, or removes it if none. 'notification' => $context->input()->filter( INPUT_GET, 'notification' ), ) ) ); exit; } }, 'enqueue_callback' => function( Assets $assets ) { $assets->enqueue_asset( 'googlesitekit-splash' ); }, 'render_callback' => function( Context $context ) { ?>
__( 'Settings', 'google-site-kit' ), 'capability' => Permissions::MANAGE_OPTIONS, 'enqueue_callback' => function( Assets $assets ) { $assets->enqueue_asset( 'googlesitekit-settings' ); }, 'render_callback' => function( Context $context ) { ?>
__( 'User Input', 'google-site-kit' ), 'capability' => Permissions::MANAGE_OPTIONS, 'parent_slug' => null, 'enqueue_callback' => function( Assets $assets ) { $assets->enqueue_asset( 'googlesitekit-user-input' ); }, 'render_callback' => function( Context $context ) { ?>
get_method_proxy( 'render_tool_box' ) ); } /** * Renders tool box output. * * @since 1.30.0 */ private function render_tool_box() { if ( ! current_user_can( Permissions::SETUP ) ) { return; } ?>