Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Authorize_Application
*
* @package Google\Site_Kit
* @copyright 2024 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Assets\Assets;
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
/**
* Class to handle all wp-admin Authorize Application related functionality.
*
* @since 1.126.0
* @access private
* @ignore
*/
final class Authorize_Application {
use Method_Proxy_Trait;
/**
* Plugin context.
*
* @since 1.126.0
* @var Context
*/
private $context;
/**
* Assets instance.
*
* @since 1.126.0
* @var Assets
*/
private $assets;
/**
* Constructor.
*
* @since 1.126.0
*
* @param Context $context Plugin context.
* @param Assets $assets Optional. Assets API instance. Default is a new instance.
*/
public function __construct(
Context $context,
?Assets $assets = null
) {
$this->context = $context;
$this->assets = $assets ?: new Assets( $this->context );
}
/**
* Registers functionality through WordPress hooks.
*
* @since 1.126.0
*/
public function register() {
add_action( 'admin_enqueue_scripts', $this->get_method_proxy( 'enqueue_assets' ) );
add_action( 'admin_footer', $this->get_method_proxy( 'render_custom_footer' ) );
}
/**
* Checks if the current screen is the Authorize Application screen.
*
* @since 1.126.0
*
* @return bool True if the current screen is the Authorize Application screen, false otherwise.
*/
protected function is_authorize_application_screen() {
$current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( $current_screen instanceof \WP_Screen && 'authorize-application' === $current_screen->id ) {
return true;
}
return false;
}
/**
* Checks if the current service is a Google service.
*
* @since 1.126.0
*
* @return bool True if the current service is a Google service, false otherwise.
*/
protected function is_google_service() {
$success_url = isset( $_GET['success_url'] ) ? esc_url_raw( wp_unslash( $_GET['success_url'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
$success_url = sanitize_text_field( $success_url );
$parsed_url = wp_parse_url( $success_url );
if ( empty( $parsed_url['host'] ) ) {
return false;
}
// Check if the domain is a '*.google.com' domain.
return preg_match( '/\.google\.com$/', $parsed_url['host'] ) === 1;
}
/**
* Enqueues assets for the Authorize Application screen.
*
* @since 1.126.0
*/
private function enqueue_assets() {
if ( $this->is_authorize_application_screen() && $this->is_google_service() ) {
$this->assets->enqueue_asset( 'googlesitekit-authorize-application-css' );
}
}
/**
* Renders custom footer for the Authorize Application screen if the service is a Google service.
*
* @since 1.126.0
*/
private function render_custom_footer() {
if ( $this->is_authorize_application_screen() && $this->is_google_service() ) {
echo '<div class="googlesitekit-authorize-application__footer"><p>' . esc_html__( 'Powered by Site Kit', 'google-site-kit' ) . '</p></div>';
}
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Available_Tools
*
* @package Google\Site_Kit\Core\Admin
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Core\Permissions\Permissions;
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
use Google\Site_Kit\Core\Util\Reset;
/**
* Class for extending available tools for Site Kit.
*
* @since 1.30.0
* @access private
* @ignore
*/
class Available_Tools {
use Method_Proxy_Trait;
/**
* Registers functionality through WordPress hooks.
*
* @since 1.30.0
*/
public function register() {
add_action( 'tool_box', $this->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;
}
?>
<div class="card">
<h2 class="title"><?php esc_html_e( 'Reset Site Kit', 'google-site-kit' ); ?></h2>
<p>
<?php
esc_html_e(
'Resetting will disconnect all users and remove all Site Kit settings and data within WordPress. You and any other users who wish to use Site Kit will need to reconnect to restore access.',
'google-site-kit'
)
?>
</p>
<p>
<a
class="button button-primary"
href="<?php echo esc_url( Reset::url() ); ?>"
>
<?php esc_html_e( 'Reset Site Kit', 'google-site-kit' ); ?>
</a>
</p>
</div>
<?php
}
}

View File

@@ -0,0 +1,203 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Dashboard
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Assets\Assets;
use Google\Site_Kit\Core\Authentication\Authentication;
use Google\Site_Kit\Core\Modules\Modules;
use Google\Site_Kit\Core\Permissions\Permissions;
use Google\Site_Kit\Core\Util\Requires_Javascript_Trait;
/**
* Class to handle all wp-admin Dashboard related functionality.
*
* @since 1.0.0
* @access private
* @ignore
*/
final class Dashboard {
use Requires_Javascript_Trait;
/**
* Plugin context.
*
* @since 1.0.0
* @var Context
*/
private $context;
/**
* Assets Instance.
*
* @since 1.0.0
* @var Assets
*/
private $assets;
/**
* Modules instance.
*
* @since 1.7.0
* @var Modules
*/
private $modules;
/**
* Authentication instance.
*
* @since 1.120.0
* @var Authentication
*/
private $authentication;
/**
* 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.
*/
public function __construct(
Context $context,
?Assets $assets = null,
?Modules $modules = null
) {
$this->context = $context;
$this->assets = $assets ?: new Assets( $this->context );
$this->modules = $modules ?: new Modules( $this->context );
$this->authentication = new Authentication( $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
* @since 1.120.0 Added the `data-view-only` attribute.
*/
private function render_googlesitekit_wp_dashboard() {
$active_modules = $this->modules->get_active_modules();
$analytics_connected = isset( $active_modules['analytics-4'] ) && $active_modules['analytics-4']->is_connected();
$search_console_connected = isset( $active_modules['search-console'] ) && $active_modules['search-console']->is_connected();
$is_view_only = ! $this->authentication->is_authenticated();
$can_view_shared_analytics = current_user_can( Permissions::READ_SHARED_MODULE_DATA, 'analytics-4' );
$can_view_shared_search_console = current_user_can( Permissions::READ_SHARED_MODULE_DATA, 'search-console' );
$display_analytics_data = ( ! $is_view_only && $analytics_connected ) || ( $is_view_only && $can_view_shared_analytics );
$display_search_console_data = ( ! $is_view_only && $search_console_connected ) || ( $is_view_only && $can_view_shared_search_console );
$class_names = array();
if ( $analytics_connected && $display_analytics_data ) {
$class_names[] = 'googlesitekit-wp-dashboard-analytics_active_and_connected';
}
if ( $search_console_connected && $display_search_console_data ) {
$class_names[] = 'googlesitekit-wp-dashboard-search_console_active_and_connected';
}
if ( ! $analytics_connected && ! $is_view_only ) {
$class_names[] = 'googlesitekit-wp-dashboard-analytics-activate-cta';
}
$class_names = implode( ' ', $class_names );
$this->render_noscript_html();
?>
<div id="js-googlesitekit-wp-dashboard" data-view-only="<?php echo esc_attr( $is_view_only ); ?>" class="googlesitekit-plugin <?php echo esc_attr( $class_names ); ?>">
<div class="googlesitekit-wp-dashboard googlesitekit-wp-dashboard-loading">
<?php
$this->render_loading_container( 'googlesitekit-wp-dashboard__cta' );
?>
<div class="googlesitekit-wp-dashboard-stats">
<?php
if ( $display_analytics_data ) {
$this->render_loading_container( 'googlesitekit-wp-dashboard-loading__can_view_analytics' );
}
if ( $display_search_console_data ) {
$this->render_loading_container( 'googlesitekit-wp-dashboard-loading__search_console_active_and_connected' );
}
if ( ! $analytics_connected && ! $is_view_only ) {
$this->render_loading_container( 'googlesitekit-wp-dashboard-stats__cta' );
}
if ( $display_analytics_data ) {
$this->render_loading_container( 'googlesitekit-unique-visitors-chart-widget' );
$this->render_loading_container( 'googlesitekit-search-console-widget' );
}
?>
</div>
</div>
</div>
<?php
}
/**
* Render the loading container when data is not available and being fetched.
*
* @since 1.144.0
* @param string $class_names Class names to add to the container.
* @return void
*/
private function render_loading_container( $class_names ) {
?>
<div class="googlesitekit-preview-block <?php echo esc_attr( $class_names ); ?>">
<div class="googlesitekit-preview-block__wrapper"></div>
</div>
<?php
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Notice
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
/**
* Class representing a single notice.
*
* @since 1.0.0
* @access private
* @ignore
*/
final class Notice {
const TYPE_SUCCESS = 'success';
const TYPE_INFO = 'info';
const TYPE_WARNING = 'warning';
const TYPE_ERROR = 'error';
/**
* Unique notice slug.
*
* @since 1.0.0
* @var string
*/
private $slug;
/**
* Notice arguments.
*
* @since 1.0.0
* @var array
*/
private $args = array();
/**
* Constructor.
*
* @since 1.0.0
*
* @param string $slug Unique notice slug.
* @param array $args {
* Associative array of notice arguments.
*
* @type string $content Required notice content. May contain inline HTML tags.
* @type string $type Notice type. Either 'success', 'info', 'warning', 'error'. Default 'info'.
* @type callable $active_callback Callback function to determine whether the notice is active in the
* current context. The current admin screen's hook suffix is passed to
* the callback. Default is that the notice is active unconditionally.
* @type bool $dismissible Whether the notice should be dismissible. Default false.
* }
*/
public function __construct( $slug, array $args ) {
$this->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 = '<p>' . wp_kses( $this->args['content'], 'googlesitekit_admin_notice' ) . '</p>';
}
$class = 'notice notice-' . $this->args['type'];
if ( $this->args['dismissible'] ) {
$class .= ' is-dismissible';
}
?>
<div id="<?php echo esc_attr( 'googlesitekit-notice-' . $this->slug ); ?>" class="<?php echo esc_attr( $class ); ?>">
<?php echo $content; /* phpcs:ignore WordPress.Security.EscapeOutput */ ?>
</div>
<?php
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Notices
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
/**
* Class managing admin notices.
*
* @since 1.0.0
* @access private
* @ignore
*/
final class Notices {
/**
* Registers functionality through WordPress hooks.
*
* @since 1.0.0
*/
public function register() {
$callback = function () {
global $hook_suffix;
if ( empty( $hook_suffix ) ) {
return;
}
$this->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;
}
);
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Plugin_Action_Links
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Permissions\Permissions;
/**
* Class for managing plugin action links.
*
* @since 1.41.0
* @access private
* @ignore
*/
class Plugin_Action_Links {
/**
* Plugin context.
*
* @since 1.41.0
* @var Context
*/
private $context;
/**
* Constructor.
*
* @since 1.41.0
*
* @param Context $context Plugin context.
*/
public function __construct(
Context $context
) {
$this->context = $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(
'<a href="%s">%s</a>',
esc_url( $this->context->admin_url( 'settings' ) ),
esc_html__( 'Settings', 'google-site-kit' )
);
array_unshift( $links, $settings_link );
}
return $links;
}
);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Plugin_Row_Meta
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
/**
* Class for managing plugin row meta.
*
* @since 1.24.0
* @access private
* @ignore
*/
class Plugin_Row_Meta {
/**
* Registers functionality through WordPress hooks.
*
* @since 1.24.0
*/
public function register() {
add_filter(
'plugin_row_meta',
function ( $meta, $plugin_file ) {
if ( GOOGLESITEKIT_PLUGIN_BASENAME === $plugin_file ) {
return array_merge( $meta, $this->get_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(
'<a href="https://wordpress.org/support/plugin/google-site-kit/reviews/#new-post">' . __( 'Rate Site Kit', 'google-site-kit' ) . '</a>',
'<a href="https://wordpress.org/support/plugin/google-site-kit/#new-post">' . __( 'Support', 'google-site-kit' ) . '</a>',
);
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Pointer
*
* @package Google\Site_Kit
* @copyright 2022 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
/**
* Class representing a single pointer.
*
* @since 1.83.0
* @access private
* @ignore
*/
final class Pointer {
/**
* Unique pointer slug.
*
* @since 1.83.0
* @var string
*/
private $slug;
/**
* Pointer arguments.
*
* @since 1.83.0
* @var array
*/
private $args = array();
/**
* Constructor.
*
* @since 1.83.0
*
* @param string $slug Unique pointer slug.
* @param array $args {
* Associative array of pointer arguments.
*
* @type string $title Required. Pointer title.
* @type string $content Required. Pointer content. May contain inline HTML tags.
* @type string $target_id Required. ID of the element the pointer should be attached to.
* @type string|array $position Optional. Position of the pointer. Can be 'top', 'bottom', 'left', 'right',
* or an array of `edge` and `align`. Default 'top'.
* @type callable $active_callback Optional. Callback function to determine whether the pointer is active in
* the current context. The current admin screen's hook suffix is passed to
* the callback. Default is that the pointer is active unconditionally.
* }
*/
public function __construct( $slug, array $args ) {
$this->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 '<p>' . wp_kses( $this->args['content'], 'googlesitekit_admin_pointer' ) . '</p>';
}
}
/**
* 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 );
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Pointers
*
* @package Google\Site_Kit\Core\Admin
* @copyright 2022 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Core\Util\BC_Functions;
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
/**
* Class for managing pointers.
*
* @since 1.83.0
* @access private
* @ignore
*/
class Pointers {
use Method_Proxy_Trait;
/**
* Registers functionality through WordPress hooks.
*
* @since 1.83.0
*/
public function register() {
add_action( 'admin_enqueue_scripts', $this->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: "<h3>%s</h3>%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,
)
);
}
}

View File

@@ -0,0 +1,260 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Screen
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Assets\Assets;
use Google\Site_Kit\Core\Util\Google_Icon;
use Google\Site_Kit\Core\Util\Requires_Javascript_Trait;
/**
* Class representing a single screen.
*
* @since 1.0.0
* @access private
* @ignore
*/
final class Screen {
use Requires_Javascript_Trait;
const MENU_SLUG = 'googlesitekit';
/**
* Unique screen slug.
*
* @since 1.0.0
* @var string
*/
private $slug;
/**
* Screen arguments.
*
* @since 1.0.0
* @var array
*/
private $args = array();
/**
* Constructor.
*
* @since 1.0.0
*
* @param string $slug Unique screen slug.
* @param array $args {
* Associative array of screen arguments.
*
* @type callable $render_callback Required callback to render the page content.
* @type string $title Required screen title.
* @type string $capability Capability required to access the screen. Default is 'manage_options'.
* @type string $menu_title Title to display in the menu (only if $add_to_menu is true). Default is
* the value of $title.
* @type string $parent_slug Slug of the parent menu screen (only if $add_to_menu is true). Default
* empty string (which means it will be a top-level page).
* @type callable $enqueue_callback Callback to enqueue additional scripts or stylesheets. The base admin
* script and stylesheet will always be enqueued. Default null.
* @type callable $initialize_callback Callback to run actions when initializing the screen, before headers are
* sent and markup is generated. Default null.
* }
*/
public function __construct( $slug, array $args ) {
$this->slug = $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['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' );
$cb = is_callable( $this->args['enqueue_callback'] )
? $this->args['enqueue_callback']
: function ( Assets $assets ) {
$assets->enqueue_asset( $this->slug );
};
call_user_func( $cb, $assets );
}
/**
* Renders the screen content.
*
* @since 1.0.0
*
* @param Context $context Plugin context.
*/
private function render( Context $context ) {
$cb = is_callable( $this->args['render_callback'] )
? $this->args['render_callback']
: function () {
printf( '<div id="js-%s" class="googlesitekit-page"></div>', esc_attr( $this->slug ) );
};
echo '<div class="googlesitekit-plugin">';
$this->render_noscript_html();
call_user_func( $cb, $context );
echo '</div>';
}
}

View File

@@ -0,0 +1,517 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Screens
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Assets\Assets;
use Google\Site_Kit\Core\Authentication\Authentication;
use Google\Site_Kit\Core\Dismissals\Dismissed_Items;
use Google\Site_Kit\Core\Key_Metrics\Key_Metrics_Setup_Completed_By;
use Google\Site_Kit\Core\Modules\Modules;
use Google\Site_Kit\Core\Permissions\Permissions;
use Google\Site_Kit\Core\Storage\Options;
use Google\Site_Kit\Core\Storage\User_Options;
/**
* Class managing admin screens.
*
* @since 1.0.0
* @access private
* @ignore
*/
final class Screens {
const PREFIX = 'googlesitekit-';
const PARENT_SLUG_NULL = self::PREFIX . 'null';
/**
* Plugin context.
*
* @since 1.0.0
* @var Context
*/
private $context;
/**
* Assets API instance.
*
* @since 1.0.0
* @var Assets
*/
private $assets;
/**
* Modules instance.
*
* @since 1.7.0
* @var Modules
*/
private $modules;
/**
* Authentication instance.
*
* @since 1.72.0
* @var Authentication
*/
private $authentication;
/**
* Associative array of $hook_suffix => $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 () {
?>
<style type="text/css">
#adminmenu .toplevel_page_googlesitekit-dashboard img {
width: 16px;
}
#adminmenu .toplevel_page_googlesitekit-dashboard.current img,
#adminmenu .toplevel_page_googlesitekit-dashboard.wp-has-current-submenu img {
opacity: 1;
}
</style>
<?php
}
);
$remove_notices_callback = function () {
global $hook_suffix;
if ( empty( $hook_suffix ) ) {
return;
}
if ( isset( $this->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() {
$show_splash_in_menu = current_user_can( Permissions::VIEW_SPLASH ) && ! current_user_can( Permissions::VIEW_DASHBOARD );
$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 = htmlspecialchars( $context->input()->filter( INPUT_GET, 'slug' ) ?: '' );
$reauth = $context->input()->filter( INPUT_GET, 'reAuth', FILTER_VALIDATE_BOOLEAN );
if ( $context->input()->filter( INPUT_GET, 'permaLink' ) ) {
?>
<div id="js-googlesitekit-entity-dashboard" data-view-only="<?php echo esc_attr( $is_view_only ); ?>" class="googlesitekit-page"></div>
<?php
} else {
$setup_module_slug = $setup_slug && $reauth ? $setup_slug : '';
if ( $setup_module_slug ) {
$active_modules = $this->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( '<span class="googlesitekit-notice">%s</span>', esc_html( $message ) ), 403 );
}
}
?>
<div id="js-googlesitekit-main-dashboard" data-view-only="<?php echo esc_attr( $is_view_only ); ?>" data-setup-module-slug="<?php echo esc_attr( $setup_module_slug ); ?>" class="googlesitekit-page"></div>
<?php
}
},
)
),
new Screen(
self::PREFIX . 'splash',
array(
'title' => __( 'Dashboard', 'google-site-kit' ),
'capability' => Permissions::VIEW_SPLASH,
'parent_slug' => $show_splash_in_menu ? Screen::MENU_SLUG : self::PARENT_SLUG_NULL,
// This callback will redirect to the dashboard on successful authentication.
'initialize_callback' => function ( Context $context ) {
// 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() ||
(
! 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;
}
},
)
),
new Screen(
self::PREFIX . 'settings',
array(
'title' => __( 'Settings', 'google-site-kit' ),
'capability' => Permissions::MANAGE_OPTIONS,
)
),
);
$screens[] = new Screen(
self::PREFIX . 'user-input',
array(
'title' => __( 'User Input', 'google-site-kit' ),
'capability' => Permissions::MANAGE_OPTIONS,
'parent_slug' => self::PARENT_SLUG_NULL,
)
);
$screens[] = new Screen(
self::PREFIX . 'ad-blocking-recovery',
array(
'title' => __( 'Ad Blocking Recovery', 'google-site-kit' ),
'capability' => Permissions::MANAGE_OPTIONS,
'parent_slug' => self::PARENT_SLUG_NULL,
)
);
$screens[] = new Screen(
self::PREFIX . 'metric-selection',
array(
'title' => __( 'Select Key Metrics', 'google-site-kit' ),
'capability' => Permissions::MANAGE_OPTIONS,
'parent_slug' => self::PARENT_SLUG_NULL,
// This callback will redirect to the dashboard if key metrics is already set up.
'initialize_callback' => function ( Context $context ) {
$options = new Options( $context );
$is_key_metrics_setup = ( new Key_Metrics_Setup_Completed_By( $options ) )->get();
if ( $is_key_metrics_setup ) {
wp_safe_redirect(
$context->admin_url( 'dashboard' )
);
exit;
}
},
)
);
$screens[] = new Screen(
self::PREFIX . 'key-metrics-setup',
array(
'title' => __( 'Key Metrics Setup', 'google-site-kit' ),
'capability' => Permissions::MANAGE_OPTIONS,
'parent_slug' => self::PARENT_SLUG_NULL,
)
);
return $screens;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Class Google\Site_Kit\Core\Admin\Standalone
*
* @package Google\Site_Kit
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/
namespace Google\Site_Kit\Core\Admin;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Assets\Stylesheet;
/**
* Class managing standalone mode.
*
* @since 1.8.0
* @access private
* @ignore
*/
final class Standalone {
/**
* Plugin context.
*
* @since 1.8.0
*
* @var Context
*/
private $context;
/**
* Constructor.
*
* @since 1.8.0
*
* @param Context $context Plugin context.
*/
public function __construct( Context $context ) {
$this->context = $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 = htmlspecialchars( $this->context->input()->filter( INPUT_GET, 'page' ) ?: '' );
$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() {
?>
<style type="text/css">
html {
padding-top: 0 !important;
}
body.googlesitekit-standalone #adminmenumain {
display: none;
}
body.googlesitekit-standalone #wpcontent {
margin-left: 0;
}
</style>
<?php
}
}