Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/google-site-kit/includes/Core/Admin/Screens.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + /**
3 + * Class Google\Site_Kit\Core\Admin\Screens
4 + *
5 + * @package Google\Site_Kit
6 + * @copyright 2021 Google LLC
7 + * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
8 + * @link https://sitekit.withgoogle.com
9 + */
10 +
11 + namespace Google\Site_Kit\Core\Admin;
12 +
13 + use Google\Site_Kit\Context;
14 + use Google\Site_Kit\Core\Assets\Assets;
15 + use Google\Site_Kit\Core\Authentication\Authentication;
16 + use Google\Site_Kit\Core\Dismissals\Dismissed_Items;
17 + use Google\Site_Kit\Core\Key_Metrics\Key_Metrics_Setup_Completed_By;
18 + use Google\Site_Kit\Core\Modules\Modules;
19 + use Google\Site_Kit\Core\Permissions\Permissions;
20 + use Google\Site_Kit\Core\Storage\Options;
21 + use Google\Site_Kit\Core\Storage\User_Options;
22 + use Google\Site_Kit\Core\User\Initial_Setup_Settings;
23 + use Google\Site_Kit\Core\Util\Feature_Flags;
24 +
25 + /**
26 + * Class managing admin screens.
27 + *
28 + * @since 1.0.0
29 + * @access private
30 + * @ignore
31 + */
32 + final class Screens {
33 +
34 + const PREFIX = 'googlesitekit-';
35 + const PARENT_SLUG_NULL = self::PREFIX . 'null';
36 +
37 + /**
38 + * Plugin context.
39 + *
40 + * @since 1.0.0
41 + * @var Context
42 + */
43 + private $context;
44 +
45 + /**
46 + * Assets API instance.
47 + *
48 + * @since 1.0.0
49 + * @var Assets
50 + */
51 + private $assets;
52 +
53 + /**
54 + * Modules instance.
55 + *
56 + * @since 1.7.0
57 + * @var Modules
58 + */
59 + private $modules;
60 +
61 + /**
62 + * Authentication instance.
63 + *
64 + * @since 1.72.0
65 + * @var Authentication
66 + */
67 + private $authentication;
68 +
69 + /**
70 + * User_Options instance.
71 + *
72 + * @since 1.167.0
73 + * @var User_Options
74 + */
75 + private $user_options;
76 +
77 + /**
78 + * Associative array of $hook_suffix => $screen pairs.
79 + *
80 + * @since 1.0.0
81 + * @var array
82 + */
83 + private $screens = array();
84 +
85 + /**
86 + * Constructor.
87 + *
88 + * @since 1.0.0
89 + *
90 + * @param Context $context Plugin context.
91 + * @param Assets $assets Optional. Assets API instance. Default is a new instance.
92 + * @param Modules $modules Optional. Modules instance. Default is a new instance.
93 + * @param Authentication $authentication Optional. Authentication instance. Default is a new instance.
94 + * @param User_Options $user_options Optional. User_Options instance. Default is a new instance.
95 + */
96 + public function __construct(
97 + Context $context,
98 + ?Assets $assets = null,
99 + ?Modules $modules = null,
100 + ?Authentication $authentication = null,
101 + ?User_Options $user_options = null
102 + ) {
103 + $this->context = $context;
104 + $this->assets = $assets ?: new Assets( $this->context );
105 + $this->modules = $modules ?: new Modules( $this->context );
106 + $this->authentication = $authentication ?: new Authentication( $this->context );
107 + $this->user_options = $user_options ?: new User_Options( $this->context );
108 + }
109 +
110 + /**
111 + * Registers functionality through WordPress hooks.
112 + *
113 + * @since 1.0.0
114 + */
115 + public function register() {
116 + if ( $this->context->is_network_mode() ) {
117 + add_action(
118 + 'network_admin_menu',
119 + function () {
120 + $this->add_screens();
121 + }
122 + );
123 + }
124 +
125 + add_action(
126 + 'admin_menu',
127 + function () {
128 + $this->add_screens();
129 + }
130 + );
131 +
132 + add_action(
133 + 'admin_enqueue_scripts',
134 + function ( $hook_suffix ) {
135 + $this->enqueue_screen_assets( $hook_suffix );
136 + }
137 + );
138 +
139 + add_action(
140 + 'admin_page_access_denied',
141 + function () {
142 + // Redirect dashboard to splash if no dashboard access (yet).
143 + $this->no_access_redirect_dashboard_to_splash();
144 + // Redirect splash to (shared) dashboard if splash is dismissed.
145 + $this->no_access_redirect_splash_to_dashboard();
146 +
147 + // Redirect module pages to dashboard.
148 + $this->no_access_redirect_module_to_dashboard();
149 + }
150 + );
151 +
152 + // Ensure the menu icon always is rendered correctly, without enqueueing a global CSS file.
153 + add_action(
154 + 'admin_head',
155 + function () {
156 + ?>
157 + <style type="text/css">
158 + #adminmenu .toplevel_page_googlesitekit-dashboard img {
159 + width: 16px;
160 + }
161 + #adminmenu .toplevel_page_googlesitekit-dashboard.current img,
162 + #adminmenu .toplevel_page_googlesitekit-dashboard.wp-has-current-submenu img {
163 + opacity: 1;
164 + }
165 + </style>
166 + <?php
167 + }
168 + );
169 +
170 + $remove_notices_callback = function () {
171 + global $hook_suffix;
172 +
173 + if ( empty( $hook_suffix ) ) {
174 + return;
175 + }
176 +
177 + if ( isset( $this->screens[ $hook_suffix ] ) ) {
178 + remove_all_actions( current_action() );
179 + }
180 + };
181 + add_action( 'admin_notices', $remove_notices_callback, -9999 );
182 + add_action( 'network_admin_notices', $remove_notices_callback, -9999 );
183 + add_action( 'all_admin_notices', $remove_notices_callback, -9999 );
184 +
185 + add_filter( 'custom_menu_order', '__return_true' );
186 + add_filter(
187 + 'menu_order',
188 + function ( array $menu_order ) {
189 + // Move the Site Kit dashboard menu item to be one after the index.php item if it exists.
190 + $dashboard_index = array_search( 'index.php', $menu_order, true );
191 +
192 + $sitekit_index = false;
193 + foreach ( $menu_order as $key => $value ) {
194 + if ( strpos( $value, self::PREFIX ) === 0 ) {
195 + $sitekit_index = $key;
196 + $sitekit_value = $value;
197 + break;
198 + }
199 + }
200 +
201 + if ( false === $dashboard_index || false === $sitekit_index ) {
202 + return $menu_order;
203 + }
204 + unset( $menu_order[ $sitekit_index ] );
205 + array_splice( $menu_order, $dashboard_index + 1, 0, $sitekit_value );
206 + return $menu_order;
207 + }
208 + );
209 + }
210 +
211 + /**
212 + * Gets the Screen instance for a given hook suffix.
213 + *
214 + * @since 1.11.0
215 + *
216 + * @param string $hook_suffix The hook suffix associated with the screen to retrieve.
217 + * @return Screen|null Screen instance if available, otherwise null;
218 + */
219 + public function get_screen( $hook_suffix ) {
220 + return isset( $this->screens[ $hook_suffix ] ) ? $this->screens[ $hook_suffix ] : null;
221 + }
222 +
223 + /**
224 + * Adds all screens to the admin.
225 + *
226 + * @since 1.0.0
227 + */
228 + private function add_screens() {
229 + $screens = $this->get_screens();
230 +
231 + array_walk( $screens, array( $this, 'add_screen' ) );
232 + }
233 +
234 + /**
235 + * Adds the given screen to the admin.
236 + *
237 + * @since 1.0.0
238 + *
239 + * @param Screen $screen Screen to add.
240 + */
241 + private function add_screen( Screen $screen ) {
242 + $hook_suffix = $screen->add( $this->context );
243 + if ( empty( $hook_suffix ) ) {
244 + return;
245 + }
246 +
247 + add_action(
248 + "load-{$hook_suffix}",
249 + function () use ( $screen ) {
250 + $screen->initialize( $this->context );
251 + }
252 + );
253 +
254 + $this->screens[ $hook_suffix ] = $screen;
255 + }
256 +
257 + /**
258 + * Enqueues assets if a plugin screen matches the given hook suffix.
259 + *
260 + * @since 1.0.0
261 + *
262 + * @param string $hook_suffix Hook suffix for the current admin screen.
263 + */
264 + private function enqueue_screen_assets( $hook_suffix ) {
265 + if ( ! isset( $this->screens[ $hook_suffix ] ) ) {
266 + return;
267 + }
268 +
269 + $this->screens[ $hook_suffix ]->enqueue_assets( $this->assets );
270 + $this->modules->enqueue_assets();
271 + }
272 +
273 + /**
274 + * Redirects from the dashboard to the splash screen if permissions to access the dashboard are currently not met.
275 + *
276 + * Dashboard permission access is conditional based on whether the user has successfully authenticated. When
277 + * e.g. accessing the dashboard manually or having it open in a separate tab while disconnecting in the other tab,
278 + * it is a better user experience to redirect to the splash screen so that the user can re-authenticate.
279 + *
280 + * The only time the dashboard should fail with the regular WordPress permissions error is when the current user is
281 + * not eligible for accessing Site Kit entirely, i.e. if they are not allowed to authenticate.
282 + *
283 + * @since 1.12.0
284 + */
285 + private function no_access_redirect_dashboard_to_splash() {
286 + global $plugin_page;
287 +
288 + // At this point, our preferred `$hook_suffix` is not set, and the dashboard page will not even be registered,
289 + // so we need to rely on the `$plugin_page` global here.
290 + if ( ! isset( $plugin_page ) || self::PREFIX . 'dashboard' !== $plugin_page ) {
291 + return;
292 + }
293 +
294 + if ( current_user_can( Permissions::VIEW_SPLASH ) ) {
295 + wp_safe_redirect(
296 + $this->context->admin_url( 'splash' )
297 + );
298 + exit;
299 + }
300 + }
301 +
302 + /**
303 + * Redirects from the splash to the dashboard screen if permissions to access the splash are currently not met.
304 + *
305 + * Admins always have the ability to view the splash page, so this redirects non-admins who have access
306 + * to view the shared dashboard if the splash has been dismissed.
307 + * Currently the dismissal check is built into the capability for VIEW_SPLASH so this is implied.
308 + *
309 + * @since 1.77.0
310 + */
311 + private function no_access_redirect_splash_to_dashboard() {
312 + global $plugin_page;
313 +
314 + if ( ! isset( $plugin_page ) || self::PREFIX . 'splash' !== $plugin_page ) {
315 + return;
316 + }
317 +
318 + if ( current_user_can( Permissions::VIEW_DASHBOARD ) ) {
319 + wp_safe_redirect(
320 + $this->context->admin_url()
321 + );
322 + exit;
323 + }
324 + }
325 +
326 + /**
327 + * Redirects module pages to the dashboard or splash based on user capability.
328 + *
329 + * @since 1.69.0
330 + */
331 + private function no_access_redirect_module_to_dashboard() {
332 + global $plugin_page;
333 +
334 + $legacy_module_pages = array(
335 + self::PREFIX . 'module-adsense',
336 + self::PREFIX . 'module-analytics',
337 + self::PREFIX . 'module-search-console',
338 + );
339 +
340 + if ( ! in_array( $plugin_page, $legacy_module_pages, true ) ) {
341 + return;
342 + }
343 +
344 + // Note: the use of add_query_arg is intentional below because it preserves
345 + // the current query parameters in the URL.
346 + if ( current_user_can( Permissions::VIEW_DASHBOARD ) ) {
347 + wp_safe_redirect(
348 + add_query_arg( 'page', self::PREFIX . 'dashboard' )
349 + );
350 + exit;
351 + }
352 +
353 + if ( current_user_can( Permissions::VIEW_SPLASH ) ) {
354 + wp_safe_redirect(
355 + add_query_arg( 'page', self::PREFIX . 'splash' )
356 + );
357 + exit;
358 + }
359 + }
360 +
361 + /**
362 + * Gets available admin screens.
363 + *
364 + * @since 1.0.0
365 + *
366 + * @return array List of Screen instances.
367 + */
368 + private function get_screens() {
369 + $show_splash_in_menu = current_user_can( Permissions::VIEW_SPLASH ) && ! current_user_can( Permissions::VIEW_DASHBOARD );
370 +
371 + $screens = array(
372 + new Screen(
373 + self::PREFIX . 'dashboard',
374 + array(
375 + 'title' => __( 'Dashboard', 'google-site-kit' ),
376 + 'capability' => Permissions::VIEW_DASHBOARD,
377 + 'enqueue_callback' => function ( Assets $assets ) {
378 + if ( $this->context->input()->filter( INPUT_GET, 'permaLink' ) ) {
379 + $assets->enqueue_asset( 'googlesitekit-entity-dashboard' );
380 + } else {
381 + $assets->enqueue_asset( 'googlesitekit-main-dashboard' );
382 + }
383 + },
384 + 'initialize_callback' => function ( Context $context ) {
385 + if ( ! Feature_Flags::enabled( 'setupFlowRefresh' ) ) {
386 + return;
387 + }
388 +
389 + $is_view_only = ! $this->authentication->is_authenticated();
390 +
391 + if ( ! $is_view_only ) {
392 + $initial_setup_settings = ( new Initial_Setup_Settings( $this->user_options ) )->get();
393 + $is_analytics_setup_complete = $initial_setup_settings['isAnalyticsSetupComplete'];
394 +
395 + if ( false === $is_analytics_setup_complete ) {
396 + $is_analytics_connected = $this->modules->is_module_connected( 'analytics-4' );
397 +
398 + if ( $is_analytics_connected ) {
399 + wp_safe_redirect(
400 + $context->admin_url(
401 + 'key-metrics-setup',
402 + array(
403 + 'showProgress' => 'true',
404 + )
405 + )
406 + );
407 +
408 + exit;
409 + } else {
410 + $slug = $context->input()->filter( INPUT_GET, 'slug' );
411 + $show_progress = $context->input()->filter( INPUT_GET, 'showProgress', FILTER_VALIDATE_BOOLEAN );
412 + $re_auth = $context->input()->filter( INPUT_GET, 'reAuth', FILTER_VALIDATE_BOOLEAN );
413 +
414 + if ( 'analytics-4' === $slug && $re_auth && $show_progress ) {
415 + return;
416 + }
417 +
418 + wp_safe_redirect(
419 + $context->admin_url(
420 + 'dashboard',
421 + array(
422 + 'slug' => 'analytics-4',
423 + 'showProgress' => 'true',
424 + 'reAuth' => 'true',
425 + )
426 + )
427 + );
428 +
429 + exit;
430 + }
431 + }
432 + }
433 + },
434 + 'render_callback' => function ( Context $context ) {
435 + $is_view_only = ! $this->authentication->is_authenticated();
436 +
437 + $setup_slug = htmlspecialchars( $context->input()->filter( INPUT_GET, 'slug' ) ?: '' );
438 + $reauth = $context->input()->filter( INPUT_GET, 'reAuth', FILTER_VALIDATE_BOOLEAN );
439 + if ( $context->input()->filter( INPUT_GET, 'permaLink' ) ) {
440 + ?>
441 + <div id="js-googlesitekit-entity-dashboard" data-view-only="<?php echo esc_attr( $is_view_only ); ?>" class="googlesitekit-page"></div>
442 + <?php
443 + } else {
444 + $setup_module_slug = $setup_slug && $reauth ? $setup_slug : '';
445 +
446 + if ( $setup_module_slug ) {
447 + $active_modules = $this->modules->get_active_modules();
448 +
449 + if ( ! array_key_exists( $setup_module_slug, $active_modules ) ) {
450 + try {
451 + $module_details = $this->modules->get_module( $setup_module_slug );
452 + /* translators: %s: The module name */
453 + $message = sprintf( __( 'The %s module cannot be set up as it has not been activated yet.', 'google-site-kit' ), $module_details->name );
454 + } catch ( \Exception $e ) {
455 + $message = $e->getMessage();
456 + }
457 +
458 + wp_die( sprintf( '<span class="googlesitekit-notice">%s</span>', esc_html( $message ) ), 403 );
459 + }
460 + }
461 + ?>
462 + <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>
463 + <?php
464 + }
465 + },
466 + )
467 + ),
468 + new Screen(
469 + self::PREFIX . 'splash',
470 + array(
471 + 'title' => __( 'Dashboard', 'google-site-kit' ),
472 + 'capability' => Permissions::VIEW_SPLASH,
473 + 'parent_slug' => $show_splash_in_menu ? Screen::MENU_SLUG : self::PARENT_SLUG_NULL,
474 + // This callback will redirect to the dashboard on successful authentication.
475 + 'initialize_callback' => function ( Context $context ) {
476 + // Get the dismissed items for this user.
477 + $user_options = new User_Options( $context );
478 + $dismissed_items = new Dismissed_Items( $user_options );
479 +
480 + $splash_context = $context->input()->filter( INPUT_GET, 'googlesitekit_context' );
481 + $reset_session = $context->input()->filter( INPUT_GET, 'googlesitekit_reset_session', FILTER_VALIDATE_BOOLEAN );
482 +
483 + // If the user is authenticated, redirect them to the disconnect URL and then send them back here.
484 + if ( ! $reset_session && 'revoked' === $splash_context && $this->authentication->is_authenticated() ) {
485 + $this->authentication->disconnect();
486 +
487 + wp_safe_redirect( add_query_arg( array( 'googlesitekit_reset_session' => 1 ) ) );
488 + exit;
489 + }
490 +
491 + // Don't consider redirect if the current user cannot access the dashboard (yet).
492 + if ( ! current_user_can( Permissions::VIEW_DASHBOARD ) ) {
493 + return;
494 + }
495 +
496 + // Redirect to dashboard if user is authenticated or if
497 + // they have already accessed the shared dashboard.
498 + if (
499 + $this->authentication->is_authenticated() ||
500 + (
501 + ! current_user_can( Permissions::AUTHENTICATE ) &&
502 + $dismissed_items->is_dismissed( 'shared_dashboard_splash' ) &&
503 + current_user_can( Permissions::VIEW_SHARED_DASHBOARD )
504 + )
505 + ) {
506 + wp_safe_redirect(
507 + $context->admin_url(
508 + 'dashboard',
509 + array(
510 + // Pass through the notification parameter, or removes it if none.
511 + 'notification' => $context->input()->filter( INPUT_GET, 'notification' ),
512 + )
513 + )
514 + );
515 + exit;
516 + }
517 + },
518 + )
519 + ),
520 + new Screen(
521 + self::PREFIX . 'settings',
522 + array(
523 + 'title' => __( 'Settings', 'google-site-kit' ),
524 + 'capability' => Permissions::MANAGE_OPTIONS,
525 + )
526 + ),
527 + );
528 +
529 + $screens[] = new Screen(
530 + self::PREFIX . 'user-input',
531 + array(
532 + 'title' => __( 'User Input', 'google-site-kit' ),
533 + 'capability' => Permissions::MANAGE_OPTIONS,
534 + 'parent_slug' => self::PARENT_SLUG_NULL,
535 + )
536 + );
537 +
538 + $screens[] = new Screen(
539 + self::PREFIX . 'ad-blocking-recovery',
540 + array(
541 + 'title' => __( 'Ad Blocking Recovery', 'google-site-kit' ),
542 + 'capability' => Permissions::MANAGE_OPTIONS,
543 + 'parent_slug' => self::PARENT_SLUG_NULL,
544 + )
545 + );
546 +
547 + $screens[] = new Screen(
548 + self::PREFIX . 'metric-selection',
549 + array(
550 + 'title' => __( 'Select Key Metrics', 'google-site-kit' ),
551 + 'capability' => Permissions::MANAGE_OPTIONS,
552 + 'parent_slug' => self::PARENT_SLUG_NULL,
553 + // This callback will redirect to the dashboard if key metrics is already set up.
554 + 'initialize_callback' => function ( Context $context ) {
555 + $options = new Options( $context );
556 + $is_key_metrics_setup = ( new Key_Metrics_Setup_Completed_By( $options ) )->get();
557 +
558 + if ( $is_key_metrics_setup ) {
559 + wp_safe_redirect(
560 + $context->admin_url( 'dashboard' )
561 + );
562 +
563 + exit;
564 + }
565 + },
566 + )
567 + );
568 +
569 + $screens[] = new Screen(
570 + self::PREFIX . 'key-metrics-setup',
571 + array(
572 + 'title' => __( 'Key Metrics Setup', 'google-site-kit' ),
573 + 'capability' => Permissions::MANAGE_OPTIONS,
574 + 'parent_slug' => self::PARENT_SLUG_NULL,
575 + )
576 + );
577 +
578 + return $screens;
579 + }
580 + }
581 +