���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC PK!;cd PageSpeed_Insights/Settings.phpnu[ 0 ); } } PK!`fAnalytics/Tag_Interface.phpnu[2Analytics/Google_Service_AnalyticsProvisioning.phpnu[provisioning = new Proxy_Provisioning( $this, $this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName 'provisioning', array( 'methods' => array( 'createAccountTicket' => array( 'path' => 'provisioning/createAccountTicket', 'httpMethod' => 'POST', 'parameters' => array(), ), ), ) ); } } PK!t Analytics/Account_Ticket.phpnu[ $value ) { if ( property_exists( $this, $key ) ) { $this->{"set_$key"}( $value ); } } } /** * Gets the account ticket ID. * * @since 1.98.0 * * @return string */ public function get_id() { return $this->id; } /** * Sets the account ticket ID. * * @since 1.98.0 * * @param string $id Account ticket ID. */ public function set_id( $id ) { $this->id = (string) $id; } /** * Gets the property name. * * @since 1.98.0 * * @return string */ public function get_property_name() { return $this->property_name; } /** * Sets the property name. * * @since 1.98.0 * * @param string $property_name Property name. */ public function set_property_name( $property_name ) { $this->property_name = (string) $property_name; } /** * Gets the data stream name. * * @since 1.98.0 * * @return string */ public function get_data_stream_name() { return $this->data_stream_name; } /** * Sets the data stream name. * * @since 1.98.0 * * @param string $data_stream_name Data stream name. */ public function set_data_stream_name( $data_stream_name ) { $this->data_stream_name = (string) $data_stream_name; } /** * Gets the timezone. * * @since 1.98.0 * * @return string */ public function get_timezone() { return $this->timezone; } /** * Sets the timezone. * * @since 1.98.0 * * @param string $timezone Timezone. */ public function set_timezone( $timezone ) { $this->timezone = (string) $timezone; } /** * Gets the array representation of the instance values. * * @since 1.98.0 * * @return array */ public function to_array() { return get_object_vars( $this ); } } PK!wffAnalytics/AMP_Tag.phpnu[home_domain = $domain; } /** * Sets whether or not to anonymize IP addresses. * * @since 1.32.0 * * @param bool $anonymize_ip Whether to anonymize IP addresses or not. */ public function set_anonymize_ip( $anonymize_ip ) { // Data from AMP documents is always IP anonymized. // See https://support.google.com/analytics/answer/6343176. } /** * Sets the ads conversion ID. * * @since 1.32.0 * * @param string $ads_conversion_id Ads ID. */ public function set_ads_conversion_id( $ads_conversion_id ) { $this->ads_conversion_id = $ads_conversion_id; } /** * Registers tag hooks. * * @since 1.24.0 */ public function register() { $render = $this->get_method_proxy_once( 'render' ); // Which actions are run depends on the version of the AMP Plugin // (https://amp-wp.org/) available. Version >=1.3 exposes a // new, `amp_print_analytics` action. // For all AMP modes, AMP plugin version >=1.3. add_action( 'amp_print_analytics', $render ); // For AMP Standard and Transitional, AMP plugin version <1.3. add_action( 'wp_footer', $render, 20 ); // For AMP Reader, AMP plugin version <1.3. add_action( 'amp_post_template_footer', $render, 20 ); // For Web Stories plugin. add_action( 'web_stories_print_analytics', $render ); // Load amp-analytics component for AMP Reader. $this->enqueue_amp_reader_component_script( 'amp-analytics', 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js' ); $this->do_init_tag_action(); } /** * Outputs gtag tag. * * @since 1.24.0 */ protected function render() { $config = $this->get_tag_config(); if ( ! empty( $this->ads_conversion_id ) ) { $config[ $this->ads_conversion_id ] = array( 'groups' => 'default', ); } $gtag_amp_opt = array( 'optoutElementId' => '__gaOptOutExtension', 'vars' => array( 'gtag_id' => $this->tag_id, 'config' => $config, ), ); /** * Filters the gtag configuration options for the amp-analytics tag. * * You can use the {@see 'googlesitekit_gtag_opt'} filter to do the same for gtag in non-AMP. * * @since 1.24.0 * @see https://developers.google.com/gtagjs/devguide/amp * * @param array $gtag_amp_opt gtag config options for AMP. */ $gtag_amp_opt_filtered = apply_filters( 'googlesitekit_amp_gtag_opt', $gtag_amp_opt ); // Ensure gtag_id is set to the correct value. if ( ! is_array( $gtag_amp_opt_filtered ) ) { $gtag_amp_opt_filtered = $gtag_amp_opt; } if ( ! isset( $gtag_amp_opt_filtered['vars'] ) || ! is_array( $gtag_amp_opt_filtered['vars'] ) ) { $gtag_amp_opt_filtered['vars'] = $gtag_amp_opt['vars']; } printf( "\n\n", esc_html__( 'Google Analytics AMP snippet added by Site Kit', 'google-site-kit' ) ); printf( '', $this->get_tag_blocked_on_consent_attribute(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_json_encode( $gtag_amp_opt_filtered ) ); printf( "\n\n", esc_html__( 'End Google Analytics AMP snippet added by Site Kit', 'google-site-kit' ) ); } /** * Gets the tag config as used in the gtag data vars. * * @return array[] */ protected function get_tag_config() { return array( $this->tag_id => array( 'groups' => 'default', 'linker' => array( 'domains' => array( $this->home_domain ), ), ), ); } } PK!vzz!Analytics/Proxy_AccountTicket.phpnu[site_id; } /** * Sets the site ID. * * @since 1.9.0 * * @param string $id The site id. */ public function setSiteId( $id ) { $this->site_id = $id; } /** * Gets the site secret. * * @since 1.9.0 */ public function getSiteSecret() { return $this->site_secret; } /** * Sets the site secret. * * @since 1.9.0 * * @param string $secret The site secret. */ public function setSiteSecret( $secret ) { $this->site_secret = $secret; } } PK!KOAnalytics/Web_Tag.phpnu[home_domain = $domain; } /** * Sets whether or not to anonymize IP addresses. * * @since 1.24.0 * * @param bool $anonymize_ip Whether to anonymize IP addresses or not. */ public function set_anonymize_ip( $anonymize_ip ) { $this->anonymize_ip = (bool) $anonymize_ip; } /** * Sets the ads conversion ID. * * @since 1.32.0 * * @param string $ads_conversion_id Ads ID. */ public function set_ads_conversion_id( $ads_conversion_id ) { $this->ads_conversion_id = $ads_conversion_id; } /** * Registers tag hooks. * * @since 1.24.0 */ public function register() { add_action( 'wp_enqueue_scripts', $this->get_method_proxy( 'enqueue_gtag_script' ) ); add_filter( 'wp_resource_hints', $this->get_dns_prefetch_hints_callback( '//www.googletagmanager.com' ), 10, 2 ); $this->do_init_tag_action(); } /** * Outputs gtag snippet. * * @since 1.24.0 */ protected function render() { // Do nothing, gtag script is enqueued. } /** * Enqueues gtag script. * * @since 1.24.0 */ protected function enqueue_gtag_script() { $gtag_opt = array(); $gtag_src = 'https://www.googletagmanager.com/gtag/js?id=' . rawurlencode( $this->tag_id ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion wp_enqueue_script( 'google_gtagjs', $gtag_src, false, null, false ); wp_script_add_data( 'google_gtagjs', 'script_execution', 'async' ); wp_add_inline_script( 'google_gtagjs', 'window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}' ); if ( ! empty( $this->home_domain ) ) { $gtag_opt['linker'] = array( 'domains' => array( $this->home_domain ) ); } if ( $this->anonymize_ip ) { // See https://developers.google.com/analytics/devguides/collection/gtagjs/ip-anonymization. $gtag_opt['anonymize_ip'] = true; } /** * Filters the gtag configuration options for the Analytics snippet. * * You can use the {@see 'googlesitekit_amp_gtag_opt'} filter to do the same for gtag in AMP. * * @since 1.24.0 * * @see https://developers.google.com/gtagjs/devguide/configure * * @param array $gtag_opt gtag config options. */ $gtag_opt = apply_filters( 'googlesitekit_gtag_opt', $gtag_opt ); if ( ! empty( $gtag_opt['linker'] ) ) { $linker = wp_json_encode( $gtag_opt['linker'] ); $linker = sprintf( "gtag('set', 'linker', %s );", $linker ); wp_add_inline_script( 'google_gtagjs', $linker ); } unset( $gtag_opt['linker'] ); wp_add_inline_script( 'google_gtagjs', 'gtag("js", new Date());' ); wp_add_inline_script( 'google_gtagjs', 'gtag("set", "developer_id.dZTNiMT", true);' ); // Site Kit developer ID. if ( empty( $gtag_opt ) ) { $config = sprintf( 'gtag("config", "%s");', esc_js( $this->tag_id ) ); wp_add_inline_script( 'google_gtagjs', $config ); } else { $config = sprintf( 'gtag("config", "%s", %s);', esc_js( $this->tag_id ), wp_json_encode( $gtag_opt ) ); wp_add_inline_script( 'google_gtagjs', $config ); } $this->add_inline_ads_conversion_id_config(); $block_on_consent_attrs = $this->get_tag_blocked_on_consent_attribute(); $filter_google_gtagjs = function ( $tag, $handle ) use ( $block_on_consent_attrs, $gtag_src ) { if ( 'google_gtagjs' !== $handle ) { return $tag; } $snippet_comment_begin = sprintf( "\n\n", esc_html__( 'Google Analytics snippet added by Site Kit', 'google-site-kit' ) ); $snippet_comment_end = sprintf( "\n\n", esc_html__( 'End Google Analytics snippet added by Site Kit', 'google-site-kit' ) ); if ( $block_on_consent_attrs ) { $tag = str_replace( array( " get_settings()->get(); return array( 'optimize_id' => array( 'label' => __( 'Optimize Container ID', 'google-site-kit' ), 'value' => $settings['optimizeID'], 'debug' => Debug_Data::redact_debug_value( $settings['optimizeID'], 7 ), ), ); } /** * Adds AMP experiment script if opted in. * * @since 1.0.0 * * @param array $data AMP template data. * @return array Filtered $data. */ protected function amp_data_load_experiment_component( $data ) { $settings = $this->get_settings()->get(); if ( $settings['ampExperimentJSON'] ) { $data['amp_component_scripts']['amp-experiment'] = 'https://cdn.ampproject.org/v0/amp-experiment-0.1.js'; } return $data; } /** * Sets up information about the module. * * @since 1.0.0 * * @return array Associative array of module info. */ protected function setup_info() { return array( 'slug' => 'optimize', 'name' => _x( 'Optimize', 'Service name', 'google-site-kit' ), 'description' => __( 'Create free A/B tests that help you drive metric-based design solutions to your site', 'google-site-kit' ), 'order' => 5, 'homepage' => __( 'https://optimize.google.com/optimize/home/', 'google-site-kit' ), 'depends_on' => array( 'analytics' ), ); } /** * Sets up the Google services the module should use. * * This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested * for the first time. * * @since 1.0.0 * @since 1.2.0 Now requires Google_Site_Kit_Client instance. * * @param Google_Site_Kit_Client $client Google client instance. * @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an * instance of Google_Service. */ protected function setup_services( Google_Site_Kit_Client $client ) { return array(); } /** * Sets up the module's settings instance. * * @since 1.2.0 * * @return Module_Settings */ protected function setup_settings() { return new Settings( $this->options ); } /** * Sets up the module's assets to register. * * @since 1.10.0 * * @return Asset[] List of Asset objects. */ protected function setup_assets() { $base_url = $this->context->url( 'dist/assets/' ); return array( new Script( 'googlesitekit-modules-optimize', array( 'src' => $base_url . 'js/googlesitekit-modules-optimize.js', 'dependencies' => array( 'googlesitekit-vendor', 'googlesitekit-api', 'googlesitekit-data', 'googlesitekit-modules', 'googlesitekit-datastore-site', 'googlesitekit-datastore-forms', 'googlesitekit-components', ), ) ), ); } /** * Registers the Optimize tag. * * @since 1.39.0 */ private function register_tag() { $is_amp = $this->context->is_amp(); $module_settings = $this->get_settings(); $settings = $module_settings->get(); if ( $is_amp ) { return false; } $tag = new Web_Tag( $settings['optimizeID'], self::MODULE_SLUG ); if ( ! $tag->is_tag_blocked() ) { $tag->use_guard( new Tag_Guard( $module_settings ) ); if ( $tag->can_register() ) { $tag->register(); } } } } PK!P  Tag_Manager/AMP_Tag.phpnu[get_method_proxy_once( 'render' ); // Which actions are run depends on the version of the AMP Plugin // (https://amp-wp.org/) available. Version >=1.3 exposes a // new, `amp_print_analytics` action. // For all AMP modes, AMP plugin version >=1.3. add_action( 'amp_print_analytics', $render ); // For AMP Standard and Transitional, AMP plugin version <1.3. add_action( 'wp_footer', $render, 20 ); // For AMP Reader, AMP plugin version <1.3. add_action( 'amp_post_template_footer', $render, 20 ); // For Web Stories plugin. add_action( 'web_stories_print_analytics', $render ); // Load amp-analytics component for AMP Reader. $this->enqueue_amp_reader_component_script( 'amp-analytics', 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js' ); $this->do_init_tag_action(); } /** * Outputs Tag Manager tag. * * @since 1.24.0 */ protected function render() { // Add the optoutElementId for compatibility with our Analytics opt-out mechanism. // This configuration object will be merged with the configuration object returned // by the `config` attribute URL. $gtm_amp_opt = array( 'optoutElementId' => '__gaOptOutExtension', ); printf( "\n\n", esc_html__( 'Google Tag Manager AMP snippet added by Site Kit', 'google-site-kit' ) ); printf( '', esc_url( 'https://www.googletagmanager.com/amp.json?id=' . rawurlencode( $this->tag_id ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $this->get_tag_blocked_on_consent_attribute(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_json_encode( $gtm_amp_opt ) ); printf( "\n\n", esc_html__( 'End Google Tag Manager AMP snippet added by Site Kit', 'google-site-kit' ) ); } } PK!v  Tag_Manager/Web_Tag.phpnu[get_method_proxy_once( 'render_no_js' ); add_action( 'wp_head', $this->get_method_proxy( 'render' ) ); // For non-AMP (if `wp_body_open` supported). add_action( 'wp_body_open', $render_no_js, -9999 ); // For non-AMP (as fallback). add_action( 'wp_footer', $render_no_js ); add_filter( 'wp_resource_hints', $this->get_dns_prefetch_hints_callback( '//www.googletagmanager.com' ), 10, 2 ); $this->do_init_tag_action(); } /** * Outputs Tag Manager script. * * @since 1.24.0 */ protected function render() { $tag_manager_inline_script = sprintf( " ( function( w, d, s, l, i ) { w[l] = w[l] || []; w[l].push( {'gtm.start': new Date().getTime(), event: 'gtm.js'} ); var f = d.getElementsByTagName( s )[0], j = d.createElement( s ), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore( j, f ); } )( window, document, 'script', 'dataLayer', '%s' ); ", esc_js( $this->tag_id ) ); $tag_manager_consent_attribute = $this->get_tag_blocked_on_consent_attribute_array(); printf( "\n\n", esc_html__( 'Google Tag Manager snippet added by Site Kit', 'google-site-kit' ) ); BC_Functions::wp_print_inline_script_tag( $tag_manager_inline_script, $tag_manager_consent_attribute ); printf( "\n\n", esc_html__( 'End Google Tag Manager snippet added by Site Kit', 'google-site-kit' ) ); } /** * Outputs Tag Manager iframe for when the browser has JavaScript disabled. * * @since 1.24.0 */ private function render_no_js() { // Consent-based blocking requires JS to be enabled so we need to bail here if present. if ( $this->get_tag_blocked_on_consent_attribute() ) { return; } $iframe_src = 'https://www.googletagmanager.com/ns.html?id=' . rawurlencode( $this->tag_id ); ?> register_legacy_keys_migration( array( 'account_id' => 'accountID', 'accountId' => 'accountID', 'container_id' => 'containerID', 'containerId' => 'containerID', ) ); $this->register_owned_keys(); } /** * Returns keys for owned settings. * * @since 1.16.0 * * @return array An array of keys for owned settings. */ public function get_owned_keys() { return array( 'accountID', 'ampContainerID', 'containerID', 'internalAMPContainerID', 'internalContainerID', ); } /** * Gets the default value. * * @since 1.2.0 * * @return array */ protected function get_default() { return array( 'ownerID' => 0, 'accountID' => '', 'ampContainerID' => '', 'containerID' => '', 'internalContainerID' => '', 'internalAMPContainerID' => '', 'useSnippet' => true, 'gaPropertyID' => '', ); } /** * Gets the callback for sanitizing the setting's value before saving. * * @since 1.6.0 * * @return callable|null */ protected function get_sanitize_callback() { return function( $option ) { if ( is_array( $option ) ) { if ( isset( $option['useSnippet'] ) ) { $option['useSnippet'] = (bool) $option['useSnippet']; } } return $option; }; } } PK!;ppTag_Manager/Tag_Guard.phpnu[is_amp = $is_amp; } /** * Determines whether the guarded tag can be activated or not. * * @since 1.24.0 * * @return bool|WP_Error TRUE if guarded tag can be activated, otherwise FALSE or an error. */ public function can_activate() { $settings = $this->settings->get(); $container_id = $this->is_amp ? $settings['ampContainerID'] : $settings['containerID']; return ! empty( $settings['useSnippet'] ) && ! empty( $container_id ); } } PK!›PageSpeed_Insights.phpnu[get_settings()->delete(); } /** * Gets map of datapoint to definition data for each. * * @since 1.12.0 * * @return array Map of datapoints to their definitions. */ protected function get_datapoint_definitions() { return array( 'GET:pagespeed' => array( 'service' => 'pagespeedonline', 'shareable' => Feature_Flags::enabled( 'dashboardSharing' ), ), ); } /** * Creates a request object for the given datapoint. * * @since 1.0.0 * * @param Data_Request $data Data request object. * @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure. * * @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist. */ protected function create_data_request( Data_Request $data ) { switch ( "{$data->method}:{$data->datapoint}" ) { case 'GET:pagespeed': if ( empty( $data['strategy'] ) ) { return new WP_Error( 'missing_required_param', sprintf( /* translators: %s: Missing parameter name */ __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'strategy' ), array( 'status' => 400 ) ); } $valid_strategies = array( 'mobile', 'desktop' ); if ( ! in_array( $data['strategy'], $valid_strategies, true ) ) { return new WP_Error( 'invalid_param', sprintf( /* translators: 1: Invalid parameter name, 2: list of valid values */ __( 'Request parameter %1$s is not one of %2$s', 'google-site-kit' ), 'strategy', implode( ', ', $valid_strategies ) ), array( 'status' => 400 ) ); } if ( ! empty( $data['url'] ) ) { $page_url = $data['url']; } else { $page_url = $this->context->get_reference_site_url(); } $service = $this->get_service( 'pagespeedonline' ); return $service->pagespeedapi->runpagespeed( $page_url, array( 'locale' => $this->context->get_locale( 'site', 'language-code' ), 'strategy' => $data['strategy'], ) ); } return parent::create_data_request( $data ); } /** * Sets up the module's assets to register. * * @since 1.9.0 * * @return Asset[] List of Asset objects. */ protected function setup_assets() { $base_url = $this->context->url( 'dist/assets/' ); return array( new Script( 'googlesitekit-modules-pagespeed-insights', array( 'src' => $base_url . 'js/googlesitekit-modules-pagespeed-insights.js', 'dependencies' => array( 'googlesitekit-vendor', 'googlesitekit-api', 'googlesitekit-data', 'googlesitekit-modules', 'googlesitekit-datastore-site', 'googlesitekit-components', ), ) ), ); } /** * Sets up information about the module. * * @since 1.0.0 * * @return array Associative array of module info. */ protected function setup_info() { return array( 'slug' => 'pagespeed-insights', 'name' => _x( 'PageSpeed Insights', 'Service name', 'google-site-kit' ), 'description' => __( 'Google PageSpeed Insights gives you metrics about performance, accessibility, SEO and PWA', 'google-site-kit' ), 'order' => 4, 'homepage' => __( 'https://pagespeed.web.dev', 'google-site-kit' ), ); } /** * Sets up the module's settings instance. * * @since 1.49.0 * * @return Module_Settings */ protected function setup_settings() { return new Settings( $this->options ); } /** * Sets up the Google services the module should use. * * This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested * for the first time. * * @since 1.0.0 * @since 1.2.0 Now requires Google_Site_Kit_Client instance. * * @param Google_Site_Kit_Client $client Google client instance. * @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an * instance of Google_Service. */ protected function setup_services( Google_Site_Kit_Client $client ) { return array( 'pagespeedonline' => new Google_Service_PagespeedInsights( $client ), ); } /** * Gets required Google OAuth scopes for the module. * * @return array List of Google OAuth scopes. * @since 1.0.0 */ public function get_scopes() { return array( 'openid', ); } } PK!'oLLSearch_Console.phpnu[register_scopes_hook(); // Detect and store Search Console property when receiving token for the first time. add_action( 'googlesitekit_authorize_user', function( array $token_response ) { if ( ! current_user_can( Permissions::SETUP ) ) { return; } // If the response includes the Search Console property, set that. // But only if it is being set for the first time or if Search Console // has no owner or the current user is the owner. if ( ! empty( $token_response['search_console_property'] ) && ( empty( $this->get_property_id() ) || ( in_array( $this->get_owner_id(), array( 0, get_current_user_id() ), true ) ) ) ) { $this->get_settings()->merge( array( 'propertyID' => $token_response['search_console_property'] ) ); return; } // Otherwise try to detect if there isn't one set already. $property_id = $this->get_property_id() ?: $this->detect_property_id(); if ( ! $property_id ) { return; } $this->get_settings()->merge( array( 'propertyID' => $property_id ) ); } ); // Ensure that the data available state is reset when the property changes. add_action( 'update_option_googlesitekit_search-console_settings', function( $old_value, $new_value ) { if ( $old_value['propertyID'] !== $new_value['propertyID'] ) { $this->reset_data_available(); } }, 10, 2 ); // Ensure that a Search Console property must be set at all times. add_filter( 'googlesitekit_setup_complete', function( $complete ) { if ( ! $complete ) { return $complete; } return (bool) $this->get_property_id(); } ); // Provide Search Console property information to JavaScript. add_filter( 'googlesitekit_setup_data', function ( $data ) { $data['hasSearchConsoleProperty'] = (bool) $this->get_property_id(); return $data; }, 11 ); } /** * Gets required Google OAuth scopes for the module. * * @since 1.0.0 * * @return array List of Google OAuth scopes. */ public function get_scopes() { return array( 'https://www.googleapis.com/auth/webmasters', // The scope for the Search Console remains the legacy webmasters scope. ); } /** * Gets an array of debug field definitions. * * @since 1.5.0 * * @return array */ public function get_debug_fields() { return array( 'search_console_property' => array( 'label' => __( 'Search Console property', 'google-site-kit' ), 'value' => $this->get_property_id(), ), ); } /** * Gets map of datapoint to definition data for each. * * @since 1.12.0 * * @return array Map of datapoints to their definitions. */ protected function get_datapoint_definitions() { return array( 'GET:matched-sites' => array( 'service' => 'searchconsole' ), 'GET:searchanalytics' => array( 'service' => 'searchconsole', 'shareable' => Feature_Flags::enabled( 'dashboardSharing' ), ), 'POST:site' => array( 'service' => 'searchconsole' ), 'GET:sites' => array( 'service' => 'searchconsole' ), ); } /** * Creates a request object for the given datapoint. * * @since 1.0.0 * * @param Data_Request $data Data request object. * @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure. * * @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist. */ protected function create_data_request( Data_Request $data ) { switch ( "{$data->method}:{$data->datapoint}" ) { case 'GET:matched-sites': return $this->get_searchconsole_service()->sites->listSites(); case 'GET:searchanalytics': $start_date = $data['startDate']; $end_date = $data['endDate']; if ( ! strtotime( $start_date ) || ! strtotime( $end_date ) ) { list ( $start_date, $end_date ) = Date::parse_date_range( 'last-28-days', 1, 1 ); } $data_request = array( 'start_date' => $start_date, 'end_date' => $end_date, ); if ( ! empty( $data['url'] ) ) { $data_request['page'] = ( new Google_URL_Normalizer() )->normalize_url( $data['url'] ); } if ( isset( $data['limit'] ) ) { $data_request['row_limit'] = $data['limit']; } $dimensions = $this->parse_string_list( $data['dimensions'] ); if ( is_array( $dimensions ) && ! empty( $dimensions ) ) { $data_request['dimensions'] = $dimensions; } return $this->create_search_analytics_data_request( $data_request ); case 'POST:site': if ( empty( $data['siteURL'] ) ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'siteURL' ), array( 'status' => 400 ) ); } $url_normalizer = new Google_URL_Normalizer(); $site_url = $data['siteURL']; if ( 0 === strpos( $site_url, 'sc-domain:' ) ) { // Domain property. $site_url = 'sc-domain:' . $url_normalizer->normalize_url( str_replace( 'sc-domain:', '', $site_url, 1 ) ); } else { // URL property. $site_url = $url_normalizer->normalize_url( trailingslashit( $site_url ) ); } return function () use ( $site_url ) { $restore_defer = $this->with_client_defer( false ); try { // If the site does not exist in the account, an exception will be thrown. $site = $this->get_searchconsole_service()->sites->get( $site_url ); } catch ( Google_Service_Exception $exception ) { // If we got here, the site does not exist in the account, so we will add it. /* @var ResponseInterface $response Response object. */ $response = $this->get_searchconsole_service()->sites->add( $site_url ); if ( 204 !== $response->getStatusCode() ) { return new WP_Error( 'failed_to_add_site_to_search_console', __( 'Error adding the site to Search Console.', 'google-site-kit' ), array( 'status' => 500 ) ); } // Fetch the site again now that it exists. $site = $this->get_searchconsole_service()->sites->get( $site_url ); } $restore_defer(); $this->get_settings()->merge( array( 'propertyID' => $site_url ) ); return array( 'siteURL' => $site->getSiteUrl(), 'permissionLevel' => $site->getPermissionLevel(), ); }; case 'GET:sites': return $this->get_searchconsole_service()->sites->listSites(); } return parent::create_data_request( $data ); } /** * Parses a response for the given datapoint. * * @since 1.0.0 * * @param Data_Request $data Data request object. * @param mixed $response Request response. * * @return mixed Parsed response data on success, or WP_Error on failure. */ protected function parse_data_response( Data_Request $data, $response ) { switch ( "{$data->method}:{$data->datapoint}" ) { case 'GET:matched-sites': /* @var Google_Service_SearchConsole_SitesListResponse $response Response object. */ $entries = Sort::case_insensitive_list_sort( $this->map_sites( (array) $response->getSiteEntry() ), 'name' ); $strict = filter_var( $data['strict'], FILTER_VALIDATE_BOOLEAN ); $current_url = $this->context->get_reference_site_url(); if ( ! $strict ) { $current_url = untrailingslashit( $current_url ); $current_url = $this->strip_url_scheme( $current_url ); $current_url = $this->strip_domain_www( $current_url ); } $sufficient_permission_levels = array( 'siteRestrictedUser', 'siteOwner', 'siteFullUser', ); return array_values( array_filter( $entries, function ( array $entry ) use ( $current_url, $sufficient_permission_levels, $strict ) { if ( 0 === strpos( $entry['siteURL'], 'sc-domain:' ) ) { $match = $this->is_domain_match( substr( $entry['siteURL'], strlen( 'sc-domain:' ) ), $current_url ); } else { $site_url = untrailingslashit( $entry['siteURL'] ); if ( ! $strict ) { $site_url = $this->strip_url_scheme( $site_url ); $site_url = $this->strip_domain_www( $site_url ); } $match = $this->is_url_match( $site_url, $current_url ); } return $match && in_array( $entry['permissionLevel'], $sufficient_permission_levels, true ); } ) ); case 'GET:searchanalytics': return $response->getRows(); case 'GET:sites': /* @var Google_Service_SearchConsole_SitesListResponse $response Response object. */ return $this->map_sites( (array) $response->getSiteEntry() ); } return parent::parse_data_response( $data, $response ); } /** * Map Site model objects to associative arrays used for API responses. * * @param array $sites Site objects. * * @return array */ private function map_sites( $sites ) { return array_map( function ( Google_Service_SearchConsole_WmxSite $site ) { return array( 'siteURL' => $site->getSiteUrl(), 'permissionLevel' => $site->getPermissionLevel(), ); }, $sites ); } /** * Creates a new Search Console analytics request for the current site and given arguments. * * @since 1.0.0 * * @param array $args { * Optional. Additional arguments. * * @type array $dimensions List of request dimensions. Default empty array. * @type string $start_date Start date in 'Y-m-d' format. Default empty string. * @type string $end_date End date in 'Y-m-d' format. Default empty string. * @type string $page Specific page URL to filter by. Default empty string. * @type int $row_limit Limit of rows to return. Default 1000. * } * @return RequestInterface Search Console analytics request instance. */ protected function create_search_analytics_data_request( array $args = array() ) { $args = wp_parse_args( $args, array( 'dimensions' => array(), 'start_date' => '', 'end_date' => '', 'page' => '', 'row_limit' => 1000, ) ); $property_id = $this->get_property_id(); $request = new Google_Service_SearchConsole_SearchAnalyticsQueryRequest(); if ( ! empty( $args['dimensions'] ) ) { $request->setDimensions( (array) $args['dimensions'] ); } if ( ! empty( $args['start_date'] ) ) { $request->setStartDate( $args['start_date'] ); } if ( ! empty( $args['end_date'] ) ) { $request->setEndDate( $args['end_date'] ); } $request->setDataState( 'all' ); $filters = array(); // If domain property, limit data to URLs that are part of the current site. if ( 0 === strpos( $property_id, 'sc-domain:' ) ) { $scope_site_filter = new Google_Service_SearchConsole_ApiDimensionFilter(); $scope_site_filter->setDimension( 'page' ); $scope_site_filter->setOperator( 'contains' ); $scope_site_filter->setExpression( esc_url_raw( $this->context->get_reference_site_url() ) ); $filters[] = $scope_site_filter; } // If specific URL requested, limit data to that URL. if ( ! empty( $args['page'] ) ) { $single_url_filter = new Google_Service_SearchConsole_ApiDimensionFilter(); $single_url_filter->setDimension( 'page' ); $single_url_filter->setOperator( 'equals' ); $single_url_filter->setExpression( rawurldecode( esc_url_raw( $args['page'] ) ) ); $filters[] = $single_url_filter; } // If there are relevant filters, add them to the request. if ( ! empty( $filters ) ) { $filter_group = new Google_Service_SearchConsole_ApiDimensionFilterGroup(); $filter_group->setGroupType( 'and' ); $filter_group->setFilters( $filters ); $request->setDimensionFilterGroups( array( $filter_group ) ); } if ( ! empty( $args['row_limit'] ) ) { $request->setRowLimit( $args['row_limit'] ); } return $this->get_searchconsole_service() ->searchanalytics ->query( $property_id, $request ); } /** * Gets the property ID. * * @since 1.3.0 * * @return string Property ID URL if set, or empty string. */ protected function get_property_id() { $option = $this->get_settings()->get(); return $option['propertyID']; } /** * Detects the property ID to use for this site. * * This method runs a Search Console API request. The determined ID should therefore be stored and accessed through * {@see Search_Console::get_property_id()} instead. * * @since 1.3.0 * * @return string Property ID, or empty string if none found. */ protected function detect_property_id() { $properties = $this->get_data( 'matched-sites', array( 'strict' => 'yes' ) ); if ( is_wp_error( $properties ) || ! $properties ) { return ''; } // If there are multiple, prefer URL property over domain property. if ( count( $properties ) > 1 ) { $url_properties = array_filter( $properties, function( $property ) { return 0 !== strpos( $property['siteURL'], 'sc-domain:' ); } ); if ( count( $url_properties ) > 0 ) { $properties = $url_properties; } } $property = array_shift( $properties ); return $property['siteURL']; } /** * Sets up information about the module. * * @since 1.0.0 * * @return array Associative array of module info. */ protected function setup_info() { return array( 'slug' => 'search-console', 'name' => _x( 'Search Console', 'Service name', 'google-site-kit' ), 'description' => __( 'Google Search Console and helps you understand how Google views your site and optimize its performance in search results.', 'google-site-kit' ), 'order' => 1, 'homepage' => __( 'https://search.google.com/search-console', 'google-site-kit' ), ); } /** * Get the configured SearchConsole service instance. * * @since 1.25.0 * * @return Google_Service_SearchConsole The Search Console API service. */ private function get_searchconsole_service() { return $this->get_service( 'searchconsole' ); } /** * Sets up the Google services the module should use. * * This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested * for the first time. * * @since 1.0.0 * @since 1.2.0 Now requires Google_Site_Kit_Client instance. * * @param Google_Site_Kit_Client $client Google client instance. * @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an * instance of Google_Service. */ protected function setup_services( Google_Site_Kit_Client $client ) { return array( 'searchconsole' => new Google_Service_SearchConsole( $client ), ); } /** * Sets up the module's settings instance. * * @since 1.3.0 * * @return Module_Settings */ protected function setup_settings() { return new Settings( $this->options ); } /** * Sets up the module's assets to register. * * @since 1.9.0 * * @return Asset[] List of Asset objects. */ protected function setup_assets() { $base_url = $this->context->url( 'dist/assets/' ); return array( new Script( 'googlesitekit-modules-search-console', array( 'src' => $base_url . 'js/googlesitekit-modules-search-console.js', 'dependencies' => array( 'googlesitekit-vendor', 'googlesitekit-api', 'googlesitekit-data', 'googlesitekit-datastore-user', 'googlesitekit-modules', 'googlesitekit-components', 'googlesitekit-modules-data', ), ) ), ); } /** * Returns TRUE to indicate that this module should be always active. * * @since 1.49.0 * * @return bool Returns `true` indicating that this module should be activated all the time. */ public static function is_force_active() { return true; } /** * Checks if the current user has access to the current configured service entity. * * @since 1.70.0 * * @return boolean|WP_Error */ public function check_service_entity_access() { $data_request = array( 'start_date' => gmdate( 'Y-m-d' ), 'end_date' => gmdate( 'Y-m-d' ), 'row_limit' => 1, ); try { $this->create_search_analytics_data_request( $data_request ); } catch ( Exception $e ) { if ( $e->getCode() === 403 ) { return false; } return $this->exception_to_error( $e ); } return true; } } PK! ]Optimize/Web_Tag.phpnu[get_method_proxy_once( 'render' ), 1 ); $this->do_init_tag_action(); } /** * Outputs the Optimize anti-flicker script tag. * * @since 1.39.0 */ protected function render() { $anti_flicker_script = sprintf( "(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date; h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')}; (a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c; })(window,document.documentElement,'async-hide','dataLayer',4000, {'%s':true});", esc_js( $this->tag_id ) ); printf( "\n\n", esc_html__( 'Anti-flicker snippet added by Site Kit', 'google-site-kit' ) ); echo ''; BC_Functions::wp_print_inline_script_tag( $anti_flicker_script ); printf( "\n\n", esc_html__( 'End Anti-flicker snippet added by Site Kit', 'google-site-kit' ) ); } } PK!j` Optimize/Settings.phpnu[register_legacy_keys_migration( array( 'AMPExperimentJson' => 'ampExperimentJSON', 'ampExperimentJson' => 'ampExperimentJSON', 'optimize_id' => 'optimizeID', 'optimizeId' => 'optimizeID', ) ); $this->register_owned_keys(); add_filter( 'option_' . self::OPTION, function ( $option ) { // Migrate legacy values where this was saved as decoded JSON object. if ( is_array( $option ) && array_key_exists( 'ampExperimentJSON', $option ) && ! is_string( $option['ampExperimentJSON'] ) ) { if ( empty( $option['ampExperimentJSON'] ) ) { $option['ampExperimentJSON'] = ''; } else { $option['ampExperimentJSON'] = wp_json_encode( $option['ampExperimentJSON'] ); } } return $option; } ); } /** * Returns keys for owned settings. * * @since 1.16.0 * * @return array An array of keys for owned settings. */ public function get_owned_keys() { return array( 'optimizeID', ); } /** * Gets the default value. * * @since 1.2.0 * * @return array */ protected function get_default() { return array( 'ownerID' => 0, 'ampExperimentJSON' => '', 'optimizeID' => '', 'placeAntiFlickerSnippet' => false, ); } /** * Gets the callback for sanitizing the setting's value before saving. * * @since 1.39.0 * * @return callable|null */ protected function get_sanitize_callback() { return function( $option ) { if ( is_array( $option ) ) { if ( isset( $option['placeAntiFlickerSnippet'] ) ) { $option['placeAntiFlickerSnippet'] = (bool) $option['placeAntiFlickerSnippet']; } } return $option; }; } } PK!VaOptimize/Tag_Guard.phpnu[settings->get(); if ( ! isset( $settings['placeAntiFlickerSnippet'] ) ) { return false; } return $settings['placeAntiFlickerSnippet']; } } PK!ɱWAnalytics_4/Report.phpnu[context = $context; } /** * Parses report dimensions received in the request params. * * @since 1.99.0 * * @param Data_Request $data Data request object. * @return Google_Service_AnalyticsData_Dimension[] An array of AnalyticsData Dimension objects. */ protected function parse_dimensions( Data_Request $data ) { $dimensions = $data['dimensions']; if ( empty( $dimensions ) || ( ! is_string( $dimensions ) && ! is_array( $dimensions ) ) ) { return array(); } if ( is_string( $dimensions ) ) { $dimensions = explode( ',', $dimensions ); } elseif ( is_array( $dimensions ) && ! wp_is_numeric_array( $dimensions ) ) { // If single object is passed. $dimensions = array( $dimensions ); } $dimensions = array_filter( array_map( function ( $dimension_def ) { $dimension = new Google_Service_AnalyticsData_Dimension(); if ( is_string( $dimension_def ) ) { $dimension->setName( $dimension_def ); } elseif ( is_array( $dimension_def ) && ! empty( $dimension_def['name'] ) ) { $dimension->setName( $dimension_def['name'] ); } else { return null; } return $dimension; }, array_filter( $dimensions ) ) ); return $dimensions; } /** * Parses report date ranges received in the request params. * * @since 1.99.0 * * @param Data_Request $data Data request object. * @return Google_Service_AnalyticsData_DateRange[] An array of AnalyticsData DateRange objects. */ protected function parse_dateranges( Data_Request $data ) { $date_ranges = array(); $start_date = $data['startDate']; $end_date = $data['endDate']; if ( strtotime( $start_date ) && strtotime( $end_date ) ) { $compare_start_date = $data['compareStartDate']; $compare_end_date = $data['compareEndDate']; $date_ranges[] = array( $start_date, $end_date ); // When using multiple date ranges, it changes the structure of the response: // Aggregate properties (minimum, maximum, totals) will have an entry per date range. // The rows property will have additional row entries for each date range. if ( strtotime( $compare_start_date ) && strtotime( $compare_end_date ) ) { $date_ranges[] = array( $compare_start_date, $compare_end_date ); } } else { // Default the date range to the last 28 days. $date_ranges[] = Date::parse_date_range( 'last-28-days', 1 ); } $date_ranges = array_map( function ( $date_range ) { list ( $start_date, $end_date ) = $date_range; $date_range = new Google_Service_AnalyticsData_DateRange(); $date_range->setStartDate( $start_date ); $date_range->setEndDate( $end_date ); return $date_range; }, $date_ranges ); return $date_ranges; } /** * Parses the orderby value of the data request into an array of AnalyticsData OrderBy object instances. * * @since 1.99.0 * * @param Data_Request $data Data request object. * @return Google_Service_AnalyticsData_OrderBy[] An array of AnalyticsData OrderBy objects. */ protected function parse_orderby( Data_Request $data ) { $orderby = $data['orderby']; if ( empty( $orderby ) || ! is_array( $orderby ) || ! wp_is_numeric_array( $orderby ) ) { return array(); } $results = array_map( function ( $order_def ) { $order_by = new Google_Service_AnalyticsData_OrderBy(); $order_by->setDesc( ! empty( $order_def['desc'] ) ); if ( isset( $order_def['metric'] ) && isset( $order_def['metric']['metricName'] ) ) { $metric_order_by = new Google_Service_AnalyticsData_MetricOrderBy(); $metric_order_by->setMetricName( $order_def['metric']['metricName'] ); $order_by->setMetric( $metric_order_by ); } elseif ( isset( $order_def['dimension'] ) && isset( $order_def['dimension']['dimensionName'] ) ) { $dimension_order_by = new Google_Service_AnalyticsData_DimensionOrderBy(); $dimension_order_by->setDimensionName( $order_def['dimension']['dimensionName'] ); $order_by->setDimension( $dimension_order_by ); } else { return null; } return $order_by; }, $orderby ); $results = array_filter( $results ); $results = array_values( $results ); return $results; } } PK!MAnalytics_4/AMP_Tag.phpnu[get_method_proxy( 'extend_gtag_opt' ) ); } } /** * Outputs gtag tag. * * @since 1.104.0 */ protected function render() { // Only render this tag if the UA AMP tag was not rendered to avoid multiple tags. if ( did_action( 'googlesitekit_analytics_init_tag_amp' ) ) { return; } parent::render(); } /** * Extends gtag vars config with the GA4 tag config. * * @since 1.104.0 * * @param array $opt AMP gtag config. * @return array */ protected function extend_gtag_opt( $opt ) { $opt['vars']['config'] = array_merge( $opt['vars']['config'], $this->get_tag_config() ); // `gtag_id` isn't used in a multi-destination configuration. // See https://developers.google.com/analytics/devguides/collection/amp-analytics/#sending_data_to_multiple_destinations. unset( $opt['vars']['gtag_id'] ); return $opt; } } PK!0{pAnalytics_4/Web_Tag.phpnu[get_method_proxy( 'enqueue_gtag_script' ), 20 ); $this->do_init_tag_action(); } /** * Enqueues gtag script. * * @since 1.31.0 */ protected function enqueue_gtag_script() { if ( did_action( 'googlesitekit_analytics_init_tag' ) ) { // If the gtag script is already registered in the Analytics module, then we need to add configuration only. $config = sprintf( 'gtag("config", "%s");', esc_js( $this->tag_id ) ); wp_add_inline_script( 'google_gtagjs', $config ); } else { // Otherwise register gtag as in the Analytics module knowing that we used Measurement ID from GA4 instead of Property ID. parent::enqueue_gtag_script(); } } } PK!ձO||5Analytics_4/Report/Dimension_Filter/String_Filter.phpnu[compose_individual_filter_expression( $dimension_name, $match_type, $value ); } $expression_list = new Google_Service_AnalyticsData_FilterExpressionList(); $expression_list->setExpressions( $expressions ); $filter_expression = new Google_Service_AnalyticsData_FilterExpression(); $filter_expression->setOrGroup( $expression_list ); return $filter_expression; } // If we have a single value for the filter, then we should use just a single // string filter expression and there is no need to create a nested one. return $this->compose_individual_filter_expression( $dimension_name, $match_type, $filter_value ); } /** * Composes individual filter expression and returns it. * * @since 1.106.0 * * @param string $dimension_name The dimension name. * @param string $match_type The dimension filter match type. * @param mixed $value The dimension filter value. * @return Google_Service_AnalyticsData_FilterExpression The filter expression instance. */ protected function compose_individual_filter_expression( $dimension_name, $match_type, $value ) { $string_filter = new Google_Service_AnalyticsData_StringFilter(); $string_filter->setMatchType( $match_type ); $string_filter->setValue( $value ); $filter = new Google_Service_AnalyticsData_Filter(); $filter->setFieldName( $dimension_name ); $filter->setStringFilter( $string_filter ); $filter_expression = new Google_Service_AnalyticsData_FilterExpression(); $filter_expression->setFilter( $filter ); return $filter_expression; } } PK!6Analytics_4/Report/Dimension_Filter/In_List_Filter.phpnu[setValues( $dimension_value ); $filter = new Google_Service_AnalyticsData_Filter(); $filter->setFieldName( $dimension_name ); $filter->setInListFilter( $in_list_filter ); $expression = new Google_Service_AnalyticsData_FilterExpression(); $expression->setFilter( $filter ); return $expression; } } PK!.И:.Analytics_4/Report/Dimension_Filter/Filter.phpnu[parse_dimensions( $data ); if ( count( $dimensions ) !== 1 || $dimensions[0]->getName() !== 'date' ) { return $response; } // Get date ranges and return early if there are no date ranges for this report. $date_ranges = $this->get_sorted_dateranges( $data ); if ( empty( $date_ranges ) ) { return $response; } // Get all available dates in the report. $existing_rows = array(); foreach ( $response->getRows() as $row ) { $dimension_values = $row->getDimensionValues(); $range = 'date_range_0'; if ( count( $dimension_values ) > 1 ) { // Considering this code will only be run when we are requesting a single dimension, `date`, // the implication is that the row will _only_ have an additional dimension when multiple // date ranges are requested. // // In this scenario, the dimension at index 1 will have a value of `date_range_{i}`, where // `i` is the zero-based index of the date range. $range = $dimension_values[1]->getValue(); } $range = str_replace( 'date_range_', '', $range ); $date = $dimension_values[0]->getValue(); $key = self::get_response_row_key( $date, is_numeric( $range ) ? $range : false ); $existing_rows[ $key ] = $row; } $metric_headers = $response->getMetricHeaders(); $ranges_count = count( $date_ranges ); $multiple_ranges = $ranges_count > 1; $rows = array(); // Add rows for the current date for each date range. self::iterate_date_ranges( $date_ranges, function( $date ) use ( &$rows, $existing_rows, $ranges_count, $metric_headers, $multiple_ranges ) { for ( $i = 0; $i < $ranges_count; $i++ ) { // Copy the existing row if it is available, otherwise create a new zero-value row. $key = self::get_response_row_key( $date, $i ); $rows[ $key ] = isset( $existing_rows[ $key ] ) ? $existing_rows[ $key ] : $this->create_report_row( $metric_headers, $date, $multiple_ranges ? $i : false ); } } ); // If we have the same number of rows as in the response at the moment, then // we can return the response without setting the new rows back into the response. $new_rows_count = count( $rows ); if ( $new_rows_count <= $response->getRowCount() ) { return $response; } // If we have multiple date ranges, we need to sort rows to have them in // the correct order. if ( $multiple_ranges ) { $rows = self::sort_response_rows( $rows, $date_ranges ); } // Set updated rows back to the response object. $response->setRows( array_values( $rows ) ); $response->setRowCount( $new_rows_count ); return $response; } /** * Gets the response row key composed from the date and the date range index values. * * @since 1.99.0 * * @param string $date The date of the row to return key for. * @param int|bool $date_range_index The date range index, or FALSE if no index is available. * @return string The row key. */ protected static function get_response_row_key( $date, $date_range_index ) { return "{$date}_{$date_range_index}"; } /** * Returns sorted and filtered date ranges received in the request params. All corrupted date ranges * are ignored and not included in the returning list. * * @since 1.99.0 * * @param Data_Request $data Data request object. * @return Google_Service_AnalyticsData_DateRange[] An array of AnalyticsData DateRange objects. */ protected function get_sorted_dateranges( Data_Request $data ) { $date_ranges = $this->parse_dateranges( $data ); if ( empty( $date_ranges ) ) { return $date_ranges; } // Filter out all corrupted date ranges. $date_ranges = array_filter( $date_ranges, function( $range ) { $start = strtotime( $range->getStartDate() ); $end = strtotime( $range->getEndDate() ); return ! empty( $start ) && ! empty( $end ); } ); // Sort date ranges preserving keys to have the oldest date range at the beginning and // the latest date range at the end. uasort( $date_ranges, function( $a, $b ) { $a_start = strtotime( $a->getStartDate() ); $b_start = strtotime( $b->getStartDate() ); return $a_start - $b_start; } ); return $date_ranges; } /** * Sorts response rows using the algorithm similar to the one that Analytics 4 uses internally * and returns sorted rows. * * @since 1.99.0 * * @param Google_Service_AnalyticsData_Row[] $rows The current report rows. * @param Google_Service_AnalyticsData_DateRange[] $date_ranges The report date ranges. * @return Google_Service_AnalyticsData_Row[] Sorted rows. */ protected static function sort_response_rows( $rows, $date_ranges ) { $sorted_rows = array(); $ranges_count = count( $date_ranges ); self::iterate_date_ranges( $date_ranges, function( $date, $range_index ) use ( &$sorted_rows, $ranges_count, $rows ) { // First take the main date range row. $key = self::get_response_row_key( $date, $range_index ); $sorted_rows[ $key ] = $rows[ $key ]; // Then take all remaining rows. for ( $i = 0; $i < $ranges_count; $i++ ) { if ( $i !== $range_index ) { $key = self::get_response_row_key( $date, $i ); $sorted_rows[ $key ] = $rows[ $key ]; } } } ); return $sorted_rows; } /** * Iterates over the date ranges and calls callback for each date in each range. * * @since 1.99.0 * * @param Google_Service_AnalyticsData_DateRange[] $date_ranges The report date ranges. * @param callable $callback The callback to execute for each date. */ protected static function iterate_date_ranges( $date_ranges, $callback ) { foreach ( $date_ranges as $date_range_index => $date_range ) { $now = strtotime( $date_range->getStartDate() ); $end = strtotime( $date_range->getEndDate() ); do { call_user_func( $callback, gmdate( 'Ymd', $now ), $date_range_index ); $now += DAY_IN_SECONDS; } while ( $now <= $end ); } } } PK!i..Analytics_4/Report/Request.phpnu[setKeepEmptyRows( true ); $request->setMetricAggregations( array( 'TOTAL', 'MINIMUM', 'MAXIMUM' ) ); if ( ! empty( $data['limit'] ) ) { $request->setLimit( $data['limit'] ); } $dimensions = $this->parse_dimensions( $data ); if ( ! empty( $dimensions ) ) { if ( $is_shared_request ) { try { $this->validate_shared_dimensions( $dimensions ); } catch ( Invalid_Report_Dimensions_Exception $exception ) { return new WP_Error( 'invalid_analytics_4_report_dimensions', $exception->getMessage() ); } } $request->setDimensions( (array) $dimensions ); } $dimension_filters = $this->parse_dimension_filters( $data ); $request->setDimensionFilter( $dimension_filters ); $date_ranges = $this->parse_dateranges( $data ); $request->setDateRanges( $date_ranges ); $metrics = $data['metrics']; if ( is_string( $metrics ) || is_array( $metrics ) ) { if ( is_string( $metrics ) ) { $metrics = explode( ',', $data['metrics'] ); } elseif ( is_array( $metrics ) && ! wp_is_numeric_array( $metrics ) ) { // If single object is passed. $metrics = array( $metrics ); } $metrics = array_filter( array_map( function ( $metric_def ) { $metric = new Google_Service_AnalyticsData_Metric(); if ( is_string( $metric_def ) ) { $metric->setName( $metric_def ); } elseif ( is_array( $metric_def ) ) { $metric->setName( $metric_def['name'] ); if ( ! empty( $metric_def['expression'] ) ) { $metric->setExpression( $metric_def['expression'] ); } } else { return null; } return $metric; }, $metrics ) ); if ( ! empty( $metrics ) ) { try { $this->validate_metrics( $metrics ); } catch ( Invalid_Report_Metrics_Exception $exception ) { return new WP_Error( 'invalid_analytics_4_report_metrics', $exception->getMessage() ); } if ( $is_shared_request ) { try { $this->validate_shared_metrics( $metrics ); } catch ( Invalid_Report_Metrics_Exception $exception ) { return new WP_Error( 'invalid_analytics_4_report_metrics', $exception->getMessage() ); } } $request->setMetrics( $metrics ); } } // Order by. $orderby = $this->parse_orderby( $data ); if ( ! empty( $orderby ) ) { $request->setOrderBys( $orderby ); } return $request; } /** * Validates the given metrics for a report. * * Metrics must have valid names, matching the regular expression ^[a-zA-Z0-9_]+$ in keeping with the GA4 API. * * @since 1.99.0 * * @param Google_Service_AnalyticsData_Metric[] $metrics The metrics to validate. * @throws Invalid_Report_Metrics_Exception Thrown if the metrics are invalid. */ protected function validate_metrics( $metrics ) { $valid_name_expression = '^[a-zA-Z0-9_]+$'; $invalid_metrics = array_map( function ( $metric ) { return $metric->getName(); }, array_filter( $metrics, function ( $metric ) use ( $valid_name_expression ) { return ! preg_match( "#$valid_name_expression#", $metric->getName() ); } ) ); if ( count( $invalid_metrics ) > 0 ) { $message = count( $invalid_metrics ) > 1 ? sprintf( /* translators: 1: the regular expression for a valid name, 2: a comma separated list of the invalid metrics. */ __( 'Metric names should match the expression %1$s: %2$s', 'google-site-kit' ), $valid_name_expression, join( /* translators: used between list items, there is a space after the comma. */ __( ', ', 'google-site-kit' ), $invalid_metrics ) ) : sprintf( /* translators: 1: the regular expression for a valid name, 2: the invalid metric. */ __( 'Metric name should match the expression %1$s: %2$s', 'google-site-kit' ), $valid_name_expression, $invalid_metrics[0] ); throw new Invalid_Report_Metrics_Exception( $message ); } } /** * Validates the report metrics for a shared request. * * @since 1.99.0 * * @param Google_Service_AnalyticsData_Metric[] $metrics The metrics to validate. * @throws Invalid_Report_Metrics_Exception Thrown if the metrics are invalid. */ protected function validate_shared_metrics( $metrics ) { $valid_metrics = apply_filters( 'googlesitekit_shareable_analytics_4_metrics', array( 'activeUsers', 'averageSessionDuration', 'conversions', 'engagedSessions', 'engagementRate', 'screenPageViews', 'sessions', 'sessionConversionRate', 'totalUsers', ) ); $invalid_metrics = array_diff( array_map( function ( $metric ) { // If there is an expression, it means the name is there as an alias, otherwise the name should be a valid metric name. // Therefore, the expression takes precedence to the name for the purpose of allow-list validation. return ! empty( $metric->getExpression() ) ? $metric->getExpression() : $metric->getName(); }, $metrics ), $valid_metrics ); if ( count( $invalid_metrics ) > 0 ) { $message = count( $invalid_metrics ) > 1 ? sprintf( /* translators: %s: is replaced with a comma separated list of the invalid metrics. */ __( 'Unsupported metrics requested: %s', 'google-site-kit' ), join( /* translators: used between list items, there is a space after the comma. */ __( ', ', 'google-site-kit' ), $invalid_metrics ) ) : sprintf( /* translators: %s: is replaced with the invalid metric. */ __( 'Unsupported metric requested: %s', 'google-site-kit' ), $invalid_metrics[0] ); throw new Invalid_Report_Metrics_Exception( $message ); } } /** * Validates the report dimensions for a shared request. * * @since 1.99.0 * * @param Google_Service_AnalyticsData_Dimension[] $dimensions The dimensions to validate. * @throws Invalid_Report_Dimensions_Exception Thrown if the dimensions are invalid. */ protected function validate_shared_dimensions( $dimensions ) { $valid_dimensions = apply_filters( 'googlesitekit_shareable_analytics_4_dimensions', array( 'city', 'country', 'date', 'deviceCategory', 'newVsReturning', 'pagePath', 'pageTitle', 'sessionDefaultChannelGroup', 'sessionDefaultChannelGrouping', ) ); $invalid_dimensions = array_diff( array_map( function ( $dimension ) { return $dimension->getName(); }, $dimensions ), $valid_dimensions ); if ( count( $invalid_dimensions ) > 0 ) { $message = count( $invalid_dimensions ) > 1 ? sprintf( /* translators: %s: is replaced with a comma separated list of the invalid dimensions. */ __( 'Unsupported dimensions requested: %s', 'google-site-kit' ), join( /* translators: used between list items, there is a space after the comma. */ __( ', ', 'google-site-kit' ), $invalid_dimensions ) ) : sprintf( /* translators: %s: is replaced with the invalid dimension. */ __( 'Unsupported dimension requested: %s', 'google-site-kit' ), $invalid_dimensions[0] ); throw new Invalid_Report_Dimensions_Exception( $message ); } } /** * Parses dimension filters and returns a filter expression that should be added to the report request. * * @since 1.106.0 * * @param Data_Request $data Data request object. * @return Google_Service_AnalyticsData_FilterExpression The filter expression to use with the report request. */ protected function parse_dimension_filters( Data_Request $data ) { $expressions = array(); $reference_url = trim( $this->context->get_reference_site_url(), '/' ); $hostnames = URL::permute_site_hosts( URL::parse( $reference_url, PHP_URL_HOST ) ); $expressions[] = $this->parse_dimension_filter( 'hostName', $hostnames ); if ( ! empty( $data['url'] ) ) { $url = str_replace( $reference_url, '', esc_url_raw( $data['url'] ) ); $expressions[] = $this->parse_dimension_filter( 'pagePath', $url ); } if ( is_array( $data['dimensionFilters'] ) ) { foreach ( $data['dimensionFilters'] as $key => $value ) { $expressions[] = $this->parse_dimension_filter( $key, $value ); } } $filter_expression_list = new Google_Service_AnalyticsData_FilterExpressionList(); $filter_expression_list->setExpressions( array_filter( $expressions ) ); $dimension_filters = new Google_Service_AnalyticsData_FilterExpression(); $dimension_filters->setAndGroup( $filter_expression_list ); return $dimension_filters; } /** * Parses and returns a single dimension filter. * * @since 1.106.0 * * @param string $dimension_name The dimension name. * @param mixed $dimension_value The dimension fileter settings. * @return Google_Service_AnalyticsData_FilterExpression The filter expression instance. */ protected function parse_dimension_filter( $dimension_name, $dimension_value ) { // Use the string filter type by default. $filter_type = 'stringFilter'; if ( isset( $dimension_value['filterType'] ) ) { // If there is the filterType property, use the explicit filter type then. $filter_type = $dimension_value['filterType']; } elseif ( wp_is_numeric_array( $dimension_value ) ) { // Otherwise, if the dimension has a numeric array of values, we should fall // back to the "in list" filter type. $filter_type = 'inListFilter'; } if ( 'stringFilter' === $filter_type ) { $filter_class = String_Filter::class; } elseif ( 'inListFilter' === $filter_type ) { $filter_class = In_List_Filter::class; } else { return null; } $filter = new $filter_class(); $filter_expression = $filter->parse_filter_expression( $dimension_name, $dimension_value ); return $filter_expression; } } PK!_K Analytics_4/Report/Row_Trait.phpnu[setValue( $current_date ); $dimension_values[] = $current_date_dimension_value; // If we have multiple date ranges, we need to add "date_range_{i}" index to dimension values. if ( false !== $date_range_index ) { $date_range_dimension_value = new Google_Service_AnalyticsData_DimensionValue(); $date_range_dimension_value->setValue( "date_range_{$date_range_index}" ); $dimension_values[] = $date_range_dimension_value; } $metric_values = array(); foreach ( $metric_headers as $metric_header ) { $metric_value = new Google_Service_AnalyticsData_MetricValue(); $metric_value->setValue( $default_value ); $metric_values[] = $metric_value; } $row = new Google_Service_AnalyticsData_Row(); $row->setDimensionValues( $dimension_values ); $row->setMetricValues( $metric_values ); return $row; } } PK!GAnalytics_4/Settings.phpnu[register_owned_keys(); } /** * Gets Analytics 4 settings. * * @since 1.99.0 * * @return array Analytics 4 settings, or default if not set. */ public function get() { $value = parent::get(); // This is a temporary solution to keep using the Analytics ownerID setting // as the main source of truth for the Analytics 4 ownerID value. // // This is needed because currently the Analytics 4 functionality is separated // from the Analytics module and we need to keep the ownerID synced between two // modules. We will remove this hack when UA is sunset and only both modules // are merged. $analytics_settings = ( new Analytics_Settings( $this->options ) )->get(); $value['ownerID'] = $analytics_settings['ownerID']; return $value; } /** * Returns keys for owned settings. * * @since 1.30.0 * * @return array An array of keys for owned settings. */ public function get_owned_keys() { return array( // TODO: These can be uncommented when Analytics and Analytics 4 modules are officially separated. /* 'accountID', */ // phpcs:ignore Squiz.PHP.CommentedOutCode.Found /* 'adsConversionID', */ // phpcs:ignore Squiz.PHP.CommentedOutCode.Found 'propertyID', 'webDataStreamID', 'measurementID', 'googleTagID', 'googleTagAccountID', 'googleTagContainerID', ); } /** * Gets the default value. * * @since 1.30.0 * * @return array */ protected function get_default() { return array( 'ownerID' => 0, // TODO: These can be uncommented when Analytics and Analytics 4 modules are officially separated. /* 'accountID' => '', */ // phpcs:ignore Squiz.PHP.CommentedOutCode.Found /* 'adsConversionID' => '', */ // phpcs:ignore Squiz.PHP.CommentedOutCode.Found 'propertyID' => '', 'webDataStreamID' => '', 'measurementID' => '', 'useSnippet' => true, 'googleTagID' => '', 'googleTagAccountID' => '', 'googleTagContainerID' => '', 'googleTagLastSyncedAtMs' => 0, ); } /** * Gets the callback for sanitizing the setting's value before saving. * * @since 1.30.0 * * @return callable|null */ protected function get_sanitize_callback() { return function( $option ) { if ( is_array( $option ) ) { if ( isset( $option['useSnippet'] ) ) { $option['useSnippet'] = (bool) $option['useSnippet']; } if ( isset( $option['googleTagID'] ) ) { if ( ! preg_match( '/^(G|GT|AW)-[a-zA-Z0-9]+$/', $option['googleTagID'] ) ) { $option['googleTagID'] = ''; } } $numeric_properties = array( 'googleTagAccountID', 'googleTagContainerID' ); foreach ( $numeric_properties as $numeric_property ) { if ( isset( $option[ $numeric_property ] ) ) { if ( ! is_numeric( $option[ $numeric_property ] ) || ! $option[ $numeric_property ] > 0 ) { $option[ $numeric_property ] = ''; } } } } return $option; }; } /** * Merges the current user ID into the module settings as the initial owner ID. * * @since 1.99.0 */ protected function merge_initial_owner_id() { // This is a temporary solution to sync owner IDs between Analytics and Analytics 4 modules. // The owner ID setting of the Analytics module is the source of truth for the Analytics 4 module. // This will change when Analytics is sunset and we merge both modules into one. ( new Analytics_Settings( $this->options ) )->merge( array( 'ownerID' => get_current_user_id() ) ); } /** * Adds the current user ID as the module owner ID to the current module settings. * * @since 1.99.0 * * @param array $settings The new module settings. * @return array Updated module settings with the current user ID as the ownerID setting. */ protected function update_owner_id_in_settings( $settings ) { // This is a temporary solution to sync owner IDs between Analytics and Analytics 4 modules. // The owner ID setting of the Analytics module is the source of truth for the Analytics 4 module. // This will change when Analytics is sunset and we merge both modules into one. ( new Analytics_Settings( $this->options ) )->merge( array( 'ownerID' => get_current_user_id() ) ); return $settings; } } PK!Zj\Analytics_4/GoogleAnalyticsAdmin/Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest.phpnu[site_id; } /** * Sets the site ID. * * @since 1.98.0 * * @param string $id The site id. */ public function setSiteId( $id ) { $this->site_id = $id; } /** * Gets the site secret. * * @since 1.98.0 */ public function getSiteSecret() { return $this->site_secret; } /** * Sets the site secret. * * @since 1.98.0 * * @param string $secret The site secret. */ public function setSiteSecret( $secret ) { $this->site_secret = $secret; } } PK!.נ99?Analytics_4/GoogleAnalyticsAdmin/AccountProvisioningService.phpnu[accounts = new AccountsResource( $this, $this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName 'accounts', array( 'methods' => array( 'provisionAccountTicket' => array( 'path' => 'v1beta/accounts:provisionAccountTicket', 'httpMethod' => 'POST', 'parameters' => array(), ), ), ) ); } } PK!֕ѱ5Analytics_4/GoogleAnalyticsAdmin/AccountsResource.phpnu[ $post_body ); $params = array_merge( $params, $opt_params ); return $this->call( 'provisionAccountTicket', array( $params ), GoogleAnalyticsAdminV1betaProvisionAccountTicketResponse::class ); } } PK!]ooAnalytics_4/Tag_Guard.phpnu[settings->get(); return ! empty( $settings['useSnippet'] ) && ! empty( $settings['measurementID'] ); } } PK!a/ AdSense.phpnu[ad_blocking_recovery_tag = new Ad_Blocking_Recovery_Tag( new Encrypted_Options( $this->options ) ); } /** * Registers functionality through WordPress hooks. * * @since 1.0.0 */ public function register() { $this->register_scopes_hook(); $this->ad_blocking_recovery_tag->register(); add_action( 'wp_head', $this->get_method_proxy_once( 'render_platform_meta_tags' ) ); if ( $this->is_connected() ) { /** * Release filter forcing unlinked state. * * This is hooked into 'init' (default priority of 10), so that it * runs after the original filter is added. * * @see \Google\Site_Kit\Modules\Analytics::register() * @see \Google\Site_Kit\Modules\Analytics\Settings::register() */ add_action( 'googlesitekit_init', function () { remove_filter( 'googlesitekit_analytics_adsense_linked', '__return_false' ); } ); } // AdSense tag placement logic. add_action( 'template_redirect', $this->get_method_proxy( 'register_tag' ) ); } /** * Gets required Google OAuth scopes for the module. * * @since 1.0.0 * @since 1.9.0 Changed to `adsense.readonly` variant. * * @return array List of Google OAuth scopes. */ public function get_scopes() { return array( 'https://www.googleapis.com/auth/adsense.readonly', ); } /** * Checks whether the module is connected. * * A module being connected means that all steps required as part of its activation are completed. * * @since 1.0.0 * * @return bool True if module is connected, false otherwise. */ public function is_connected() { $settings = $this->get_settings()->get(); if ( empty( $settings['accountSetupComplete'] ) || empty( $settings['siteSetupComplete'] ) ) { return false; } return parent::is_connected(); } /** * Cleans up when the module is deactivated. * * @since 1.0.0 * @since 1.106.0 Remove Ad Blocking Recovery Tag setting on deactivation. */ public function on_deactivation() { $this->get_settings()->delete(); if ( $this->ad_blocking_recovery_tag->has() ) { $this->ad_blocking_recovery_tag->delete(); } } /** * Gets an array of debug field definitions. * * @since 1.5.0 * * @return array */ public function get_debug_fields() { $settings = $this->get_settings()->get(); $fields = array( 'adsense_account_id' => array( 'label' => __( 'AdSense account ID', 'google-site-kit' ), 'value' => $settings['accountID'], 'debug' => Debug_Data::redact_debug_value( $settings['accountID'], 7 ), ), 'adsense_client_id' => array( 'label' => __( 'AdSense client ID', 'google-site-kit' ), 'value' => $settings['clientID'], 'debug' => Debug_Data::redact_debug_value( $settings['clientID'], 10 ), ), 'adsense_account_status' => array( 'label' => __( 'AdSense account status', 'google-site-kit' ), 'value' => $settings['accountStatus'], ), 'adsense_site_status' => array( 'label' => __( 'AdSense site status', 'google-site-kit' ), 'value' => $settings['siteStatus'], ), 'adsense_use_snippet' => array( 'label' => __( 'AdSense snippet placed', 'google-site-kit' ), 'value' => $settings['useSnippet'] ? __( 'Yes', 'google-site-kit' ) : __( 'No', 'google-site-kit' ), 'debug' => $settings['useSnippet'] ? 'yes' : 'no', ), 'adsense_web_stories_adunit_id' => array( 'label' => __( 'Web Stories Ad Unit ID', 'google-site-kit' ), 'value' => $settings['webStoriesAdUnit'], 'debug' => $settings['webStoriesAdUnit'], ), 'adsense_setup_completed_timestamp' => array( 'label' => __( 'AdSense setup completed at', 'google-site-kit' ), 'value' => $settings['setupCompletedTimestamp'] ? date_i18n( get_option( 'date_format' ), $settings['setupCompletedTimestamp'] ) : __( 'Not available', 'google-site-kit' ), 'debug' => $settings['setupCompletedTimestamp'], ), ); if ( Feature_Flags::enabled( 'adBlockerDetection' ) ) { $fields = array_merge( $fields, array( 'adsense_abr_use_snippet' => array( 'label' => __( 'Ad Blocking Recovery snippet placed', 'google-site-kit' ), 'value' => $settings['useAdBlockingRecoverySnippet'] ? __( 'Yes', 'google-site-kit' ) : __( 'No', 'google-site-kit' ), 'debug' => $settings['useAdBlockingRecoverySnippet'] ? 'yes' : 'no', ), 'adsense_abr_use_error_protection_snippet' => array( 'label' => __( 'Ad Blocking Recovery error protection snippet placed', 'google-site-kit' ), 'value' => $settings['useAdBlockingRecoveryErrorSnippet'] ? __( 'Yes', 'google-site-kit' ) : __( 'No', 'google-site-kit' ), 'debug' => $settings['useAdBlockingRecoveryErrorSnippet'] ? 'yes' : 'no', ), 'adsense_abr_setup_status' => array( 'label' => __( 'Ad Blocking Recovery setup status', 'google-site-kit' ), 'value' => $this->get_ad_blocking_recovery_setup_status_label( $settings['adBlockingRecoverySetupStatus'] ), 'debug' => $settings['adBlockingRecoverySetupStatus'], ), ) ); } return $fields; } /** * Gets map of datapoint to definition data for each. * * @since 1.12.0 * * @return array Map of datapoints to their definitions. */ protected function get_datapoint_definitions() { return array( 'GET:accounts' => array( 'service' => 'adsense' ), 'GET:adunits' => array( 'service' => 'adsense' ), 'GET:alerts' => array( 'service' => 'adsense' ), 'GET:clients' => array( 'service' => 'adsense' ), 'GET:notifications' => array( 'service' => '' ), 'GET:report' => array( 'service' => 'adsense', 'shareable' => Feature_Flags::enabled( 'dashboardSharing' ), ), 'GET:sites' => array( 'service' => 'adsense' ), 'POST:sync-ad-blocking-recovery-tags' => array( 'service' => 'adsense' ), 'GET:urlchannels' => array( 'service' => 'adsense' ), ); } /** * Creates a request object for the given datapoint. * * @since 1.0.0 * * @param Data_Request $data Data request object. * @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure. * * @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist. */ protected function create_data_request( Data_Request $data ) { switch ( "{$data->method}:{$data->datapoint}" ) { case 'GET:accounts': $service = $this->get_service( 'adsense' ); return $service->accounts->listAccounts(); case 'GET:adunits': if ( ! isset( $data['accountID'] ) || ! isset( $data['clientID'] ) ) { $option = $this->get_settings()->get(); $data['accountID'] = $option['accountID']; if ( empty( $data['accountID'] ) ) { /* translators: %s: Missing parameter name */ return new WP_Error( 'missing_required_param', sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) ); } $data['clientID'] = $option['clientID']; if ( empty( $data['clientID'] ) ) { /* translators: %s: Missing parameter name */ return new WP_Error( 'missing_required_param', sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'clientID' ), array( 'status' => 400 ) ); } } $service = $this->get_service( 'adsense' ); return $service->accounts_adclients_adunits->listAccountsAdclientsAdunits( self::normalize_client_id( $data['accountID'], $data['clientID'] ) ); case 'GET:alerts': if ( ! isset( $data['accountID'] ) ) { $option = $this->get_settings()->get(); $data['accountID'] = $option['accountID']; if ( empty( $data['accountID'] ) ) { /* translators: %s: Missing parameter name */ return new WP_Error( 'missing_required_param', sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) ); } } $service = $this->get_service( 'adsense' ); return $service->accounts_alerts->listAccountsAlerts( self::normalize_account_id( $data['accountID'] ) ); case 'GET:clients': if ( ! isset( $data['accountID'] ) ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) ); } $service = $this->get_service( 'adsense' ); return $service->accounts_adclients->listAccountsAdclients( self::normalize_account_id( $data['accountID'] ) ); case 'GET:notifications': return function() { $alerts = $this->get_data( 'alerts' ); if ( is_wp_error( $alerts ) || empty( $alerts ) ) { return array(); } $alerts = array_filter( $alerts, function( Google_Service_Adsense_Alert $alert ) { return 'SEVERE' === $alert->getSeverity(); } ); // There is no SEVERE alert, return empty. if ( empty( $alerts ) ) { return array(); } /** * First Alert * * @var Google_Service_Adsense_Alert $alert */ $alert = array_shift( $alerts ); return array( array( 'id' => 'adsense-notification', 'description' => $alert->getMessage(), 'isDismissible' => true, 'format' => 'large', 'severity' => 'win-info', 'ctaURL' => $this->get_account_url(), 'ctaLabel' => __( 'Go to AdSense', 'google-site-kit' ), 'ctaTarget' => '_blank', ), ); }; case 'GET:report': $start_date = $data['startDate']; $end_date = $data['endDate']; if ( ! strtotime( $start_date ) || ! strtotime( $end_date ) ) { $dates = $this->date_range_to_dates( 'last-28-days' ); if ( is_wp_error( $dates ) ) { return $dates; } list ( $start_date, $end_date ) = $dates; } $args = array( 'start_date' => $start_date, 'end_date' => $end_date, ); $metrics = $this->parse_string_list( $data['metrics'] ); if ( ! empty( $metrics ) ) { if ( $this->is_shared_data_request( $data ) ) { try { $this->validate_shared_report_metrics( $metrics ); } catch ( Invalid_Report_Metrics_Exception $exception ) { return new WP_Error( 'invalid_adsense_report_metrics', $exception->getMessage() ); } } $args['metrics'] = $metrics; } $dimensions = $this->parse_string_list( $data['dimensions'] ); if ( ! empty( $dimensions ) ) { if ( $this->is_shared_data_request( $data ) ) { try { $this->validate_shared_report_dimensions( $dimensions ); } catch ( Invalid_Report_Dimensions_Exception $exception ) { return new WP_Error( 'invalid_adsense_report_dimensions', $exception->getMessage() ); } } $args['dimensions'] = $dimensions; } $orderby = $this->parse_earnings_orderby( $data['orderby'] ); if ( ! empty( $orderby ) ) { $args['sort'] = $orderby; } if ( ! empty( $data['limit'] ) ) { $args['limit'] = $data['limit']; } return $this->create_adsense_earning_data_request( array_filter( $args ) ); case 'GET:sites': if ( ! isset( $data['accountID'] ) ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) ); } $service = $this->get_service( 'adsense' ); return $service->accounts_sites->listAccountsSites( self::normalize_account_id( $data['accountID'] ) ); case 'POST:sync-ad-blocking-recovery-tags': $settings = $this->get_settings()->get(); if ( empty( $settings['accountID'] ) ) { return new WP_Error( 'module_not_connected', __( 'Module is not connected.', 'google-site-kit' ), array( 'status' => 500 ) ); } $service = $this->get_service( 'adsense' ); return $service->accounts->getAdBlockingRecoveryTag( self::normalize_account_id( $settings['accountID'] ) ); case 'GET:urlchannels': if ( ! isset( $data['accountID'] ) ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) ); } if ( ! isset( $data['clientID'] ) ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'clientID' ), array( 'status' => 400 ) ); } $service = $this->get_service( 'adsense' ); return $service->accounts_adclients_urlchannels->listAccountsAdclientsUrlchannels( self::normalize_client_id( $data['accountID'], $data['clientID'] ) ); } return parent::create_data_request( $data ); } /** * Parses a response for the given datapoint. * * @since 1.0.0 * * @param Data_Request $data Data request object. * @param mixed $response Request response. * * @return mixed Parsed response data on success, or WP_Error on failure. */ protected function parse_data_response( Data_Request $data, $response ) { switch ( "{$data->method}:{$data->datapoint}" ) { case 'GET:accounts': $accounts = array_filter( $response->getAccounts(), array( self::class, 'is_account_not_closed' ) ); return Sort::case_insensitive_list_sort( array_map( array( self::class, 'filter_account_with_ids' ), $accounts ), 'displayName' ); case 'GET:adunits': return array_map( array( self::class, 'filter_adunit_with_ids' ), $response->getAdUnits() ); case 'GET:alerts': return $response->getAlerts(); case 'GET:clients': return array_map( array( self::class, 'filter_client_with_ids' ), $response->getAdClients() ); case 'GET:report': return $response; case 'GET:sites': return $response->getSites(); case 'POST:sync-ad-blocking-recovery-tags': $this->ad_blocking_recovery_tag->set( array( 'tag' => $response->getTag(), 'error_protection_code' => $response->getErrorProtectionCode(), ) ); return new WP_REST_Response( array( 'success' => true, ) ); case 'GET:urlchannels': return $response->getUrlChannels(); } return parent::parse_data_response( $data, $response ); } /** * Checks for the state of an Account, whether closed or not. * * @since 1.73.0 * * @param Google_Model $account Account model. * @return bool Whether the account is not closed. */ public static function is_account_not_closed( $account ) { return 'CLOSED' !== $account->getState(); } /** * Gets the service URL for the current account or signup if none. * * @since 1.25.0 * * @return string */ protected function get_account_url() { $profile = $this->authentication->profile(); $option = $this->get_settings()->get(); $query = array( 'source' => 'site-kit', 'utm_source' => 'site-kit', 'utm_medium' => 'wordpress_signup', 'url' => rawurlencode( $this->context->get_reference_site_url() ), ); if ( ! empty( $option['accountID'] ) ) { $url = sprintf( 'https://www.google.com/adsense/new/%s/home', $option['accountID'] ); } else { $url = 'https://www.google.com/adsense/signup'; } if ( $profile->has() ) { $query['authuser'] = $profile->get()['email']; } return add_query_arg( $query, $url ); } /** * Parses the orderby value of the data request into an array of earning orderby format. * * @since 1.15.0 * * @param array|null $orderby Data request orderby value. * @return string[] An array of reporting orderby strings. */ protected function parse_earnings_orderby( $orderby ) { if ( empty( $orderby ) || ! is_array( $orderby ) ) { return array(); } $results = array_map( function ( $order_def ) { $order_def = array_merge( array( 'fieldName' => '', 'sortOrder' => '', ), (array) $order_def ); if ( empty( $order_def['fieldName'] ) || empty( $order_def['sortOrder'] ) ) { return null; } return ( 'ASCENDING' === $order_def['sortOrder'] ? '+' : '-' ) . $order_def['fieldName']; }, // When just object is passed we need to convert it to an array of objects. wp_is_numeric_array( $orderby ) ? $orderby : array( $orderby ) ); $results = array_filter( $results ); $results = array_values( $results ); return $results; } /** * Gets an array of dates for the given named date range. * * @param string $date_range Named date range. * E.g. 'last-28-days'. * * @return array|WP_Error Array of [startDate, endDate] or WP_Error if invalid named range. */ private function date_range_to_dates( $date_range ) { switch ( $date_range ) { case 'today': return array( gmdate( 'Y-m-d', strtotime( 'today' ) ), gmdate( 'Y-m-d', strtotime( 'today' ) ), ); // Intentional fallthrough. case 'last-7-days': case 'last-14-days': case 'last-28-days': case 'last-90-days': return Date::parse_date_range( $date_range ); } return new WP_Error( 'invalid_date_range', __( 'Invalid date range.', 'google-site-kit' ) ); } /** * Creates a new AdSense earning request for the current account, site and given arguments. * * @since 1.0.0 * * @param array $args { * Optional. Additional arguments. * * @type array $dimensions List of request dimensions. Default empty array. * @type array $metrics List of request metrics. Default empty array. * @type string $start_date Start date in 'Y-m-d' format. Default empty string. * @type string $end_date End date in 'Y-m-d' format. Default empty string. * @type int $row_limit Limit of rows to return. Default none (will be skipped). * } * @return RequestInterface|WP_Error AdSense earning request instance. */ protected function create_adsense_earning_data_request( array $args = array() ) { $args = wp_parse_args( $args, array( 'dimensions' => array(), 'metrics' => array(), 'start_date' => '', 'end_date' => '', 'limit' => '', 'sort' => array(), ) ); $option = $this->get_settings()->get(); $account_id = $option['accountID']; if ( empty( $account_id ) ) { return new WP_Error( 'account_id_not_set', __( 'AdSense account ID not set.', 'google-site-kit' ) ); } list( $start_year, $start_month, $start_day ) = explode( '-', $args['start_date'] ); list( $end_year, $end_month, $end_day ) = explode( '-', $args['end_date'] ); $opt_params = array( // In the AdSense API v2, date parameters require the individual pieces to be specified as integers. // See https://developers.google.com/adsense/management/reference/rest/v2/accounts.reports/generate. 'dateRange' => 'CUSTOM', 'startDate.year' => (int) $start_year, 'startDate.month' => (int) $start_month, 'startDate.day' => (int) $start_day, 'endDate.year' => (int) $end_year, 'endDate.month' => (int) $end_month, 'endDate.day' => (int) $end_day, 'languageCode' => $this->context->get_locale( 'site', 'language-code' ), // Include default metrics only for backward-compatibility. 'metrics' => array( 'ESTIMATED_EARNINGS', 'PAGE_VIEWS_RPM', 'IMPRESSIONS' ), ); if ( ! empty( $args['dimensions'] ) ) { $opt_params['dimensions'] = (array) $args['dimensions']; } if ( ! empty( $args['metrics'] ) ) { $opt_params['metrics'] = (array) $args['metrics']; } if ( ! empty( $args['sort'] ) ) { $opt_params['orderBy'] = (array) $args['sort']; } if ( ! empty( $args['limit'] ) ) { $opt_params['limit'] = (int) $args['limit']; } // @see https://developers.google.com/adsense/management/reporting/filtering?hl=en#OR $site_hostname = URL::parse( $this->context->get_reference_site_url(), PHP_URL_HOST ); $opt_params['filters'] = join( ',', array_map( function ( $hostname ) { return 'DOMAIN_NAME==' . $hostname; }, URL::permute_site_hosts( $site_hostname ) ) ); return $this->get_service( 'adsense' ) ->accounts_reports ->generate( self::normalize_account_id( $account_id ), $opt_params ); } /** * Sets up information about the module. * * @since 1.0.0 * * @return array Associative array of module info. */ protected function setup_info() { $idenfifier_args = array( 'source' => 'site-kit', 'url' => $this->context->get_reference_site_url(), ); return array( 'slug' => self::MODULE_SLUG, 'name' => _x( 'AdSense', 'Service name', 'google-site-kit' ), 'description' => __( 'Earn money by placing ads on your website. It’s free and easy.', 'google-site-kit' ), 'order' => 2, 'homepage' => add_query_arg( $idenfifier_args, 'https://adsense.google.com/start' ), ); } /** * Sets up the Google services the module should use. * * This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested * for the first time. * * @since 1.0.0 * @since 1.2.0 Now requires Google_Site_Kit_Client instance. * * @param Google_Site_Kit_Client $client Google client instance. * @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an * instance of Google_Service. */ protected function setup_services( Google_Site_Kit_Client $client ) { return array( 'adsense' => new Google_Service_Adsense( $client ), ); } /** * Sets up the module's settings instance. * * @since 1.2.0 * * @return Module_Settings */ protected function setup_settings() { return new Settings( $this->options ); } /** * Sets up the module's assets to register. * * @since 1.9.0 * * @return Asset[] List of Asset objects. */ protected function setup_assets() { $base_url = $this->context->url( 'dist/assets/' ); return array( new Script( 'googlesitekit-modules-adsense', array( 'src' => $base_url . 'js/googlesitekit-modules-adsense.js', 'dependencies' => array( 'googlesitekit-vendor', 'googlesitekit-api', 'googlesitekit-data', 'googlesitekit-modules', 'googlesitekit-datastore-site', 'googlesitekit-datastore-user', 'googlesitekit-components', ), ) ), ); } /** * Registers the AdSense tag. * * @since 1.24.0 */ private function register_tag() { // TODO: 'amp_story' support can be phased out in the long term. if ( is_singular( array( 'amp_story' ) ) ) { return; } $module_settings = $this->get_settings(); $settings = $module_settings->get(); if ( $this->context->is_amp() ) { $tag = new AMP_Tag( $settings['clientID'], self::MODULE_SLUG ); $tag->set_story_ad_slot_id( $settings['webStoriesAdUnit'] ); } else { $tag = new Web_Tag( $settings['clientID'], self::MODULE_SLUG ); } if ( $tag->is_tag_blocked() ) { return; } $tag->use_guard( new Tag_Verify_Guard( $this->context->input() ) ); $tag->use_guard( new WP_Query_404_Guard() ); $tag->use_guard( new Tag_Guard( $module_settings ) ); $tag->use_guard( new Auto_Ad_Guard( $module_settings ) ); $tag->use_guard( new Tag_Environment_Type_Guard() ); if ( $tag->can_register() ) { $tag->register(); } if ( Feature_Flags::enabled( 'adBlockerDetection' ) && ! $this->context->is_amp() ) { $ad_blocking_recovery_web_tag = new Ad_Blocking_Recovery_Web_Tag( $this->ad_blocking_recovery_tag, $settings['useAdBlockingRecoveryErrorSnippet'] ); $ad_blocking_recovery_web_tag->use_guard( new Tag_Verify_Guard( $this->context->input() ) ); $ad_blocking_recovery_web_tag->use_guard( new WP_Query_404_Guard() ); $ad_blocking_recovery_web_tag->use_guard( new Ad_Blocking_Recovery_Tag_Guard( $module_settings ) ); $ad_blocking_recovery_web_tag->use_guard( new Tag_Environment_Type_Guard() ); if ( $ad_blocking_recovery_web_tag->can_register() ) { $ad_blocking_recovery_web_tag->register(); } } } /** * Parses account ID, adds it to the model object and returns updated model. * * @since 1.36.0 * * @param Google_Model $account Account model. * @param string $id_key Attribute name that contains account ID. * @return \stdClass Updated model with _id attribute. */ public static function filter_account_with_ids( $account, $id_key = 'name' ) { $obj = $account->toSimpleObject(); $matches = array(); if ( preg_match( '#accounts/([^/]+)#', $account[ $id_key ], $matches ) ) { $obj->_id = $matches[1]; } return $obj; } /** * Parses account and client IDs, adds it to the model object and returns updated model. * * @since 1.36.0 * * @param Google_Model $client Client model. * @param string $id_key Attribute name that contains client ID. * @return \stdClass Updated model with _id and _accountID attributes. */ public static function filter_client_with_ids( $client, $id_key = 'name' ) { $obj = $client->toSimpleObject(); $matches = array(); if ( preg_match( '#accounts/([^/]+)/adclients/([^/]+)#', $client[ $id_key ], $matches ) ) { $obj->_id = $matches[2]; $obj->_accountID = $matches[1]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase } return $obj; } /** * Parses account, client and ad unit IDs, adds it to the model object and returns updated model. * * @since 1.36.0 * * @param Google_Model $adunit Ad unit model. * @param string $id_key Attribute name that contains ad unit ID. * @return \stdClass Updated model with _id, _clientID and _accountID attributes. */ public static function filter_adunit_with_ids( $adunit, $id_key = 'name' ) { $obj = $adunit->toSimpleObject(); $matches = array(); if ( preg_match( '#accounts/([^/]+)/adclients/([^/]+)/adunits/([^/]+)#', $adunit[ $id_key ], $matches ) ) { $obj->_id = $matches[3]; $obj->_clientID = $matches[2]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $obj->_accountID = $matches[1]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase } return $obj; } /** * Normalizes account ID and returns it. * * @since 1.36.0 * * @param string $account_id Account ID. * @return string Updated account ID with "accounts/" prefix. */ public static function normalize_account_id( $account_id ) { return 'accounts/' . $account_id; } /** * Normalizes ad client ID and returns it. * * @since 1.36.0 * * @param string $account_id Account ID. * @param string $client_id Ad client ID. * @return string Account ID and ad client ID in "accounts/{accountID}/adclients/{clientID}" format. */ public static function normalize_client_id( $account_id, $client_id ) { return 'accounts/' . $account_id . '/adclients/' . $client_id; } /** * Outputs the Adsense for Platforms meta tags. * * @since 1.43.0 */ private function render_platform_meta_tags() { printf( "\n\n", esc_html__( 'Google AdSense snippet added by Site Kit', 'google-site-kit' ) ); echo ''; echo "\n"; echo ''; printf( "\n\n", esc_html__( 'End Google AdSense snippet added by Site Kit', 'google-site-kit' ) ); } /** * Checks if the current user has access to the current configured service entity. * * @since 1.70.0 * * @return boolean|WP_Error */ public function check_service_entity_access() { $data_request = array( 'start_date' => gmdate( 'Y-m-d' ), 'end_date' => gmdate( 'Y-m-d' ), 'limit' => 1, ); try { $request = $this->create_adsense_earning_data_request( $data_request ); if ( is_wp_error( $request ) ) { return $request; } } catch ( Exception $e ) { if ( $e->getCode() === 403 ) { return false; } return $this->exception_to_error( $e ); } return true; } /** * Validates the report metrics for a shared request. * * @since 1.83.0 * @since 1.98.0 Renamed the method, and moved the check for being a shared request to the caller. * * @param string[] $metrics The metrics to validate. * @throws Invalid_Report_Metrics_Exception Thrown if the metrics are invalid. */ protected function validate_shared_report_metrics( $metrics ) { $valid_metrics = apply_filters( 'googlesitekit_shareable_adsense_metrics', array( 'ESTIMATED_EARNINGS', 'IMPRESSIONS', 'PAGE_VIEWS_CTR', 'PAGE_VIEWS_RPM', ) ); $invalid_metrics = array_diff( $metrics, $valid_metrics ); if ( count( $invalid_metrics ) > 0 ) { $message = count( $invalid_metrics ) > 1 ? sprintf( /* translators: %s: is replaced with a comma separated list of the invalid metrics. */ __( 'Unsupported metrics requested: %s', 'google-site-kit' ), join( /* translators: used between list items, there is a space after the comma. */ __( ', ', 'google-site-kit' ), $invalid_metrics ) ) : sprintf( /* translators: %s: is replaced with the invalid metric. */ __( 'Unsupported metric requested: %s', 'google-site-kit' ), $invalid_metrics[0] ); throw new Invalid_Report_Metrics_Exception( $message ); } } /** * Validates the report dimensions for a shared request. * * @since 1.83.0 * @since 1.98.0 Renamed the method, and moved the check for being a shared request to the caller. * * @param string[] $dimensions The dimensions to validate. * @throws Invalid_Report_Dimensions_Exception Thrown if the dimensions are invalid. */ protected function validate_shared_report_dimensions( $dimensions ) { $valid_dimensions = apply_filters( 'googlesitekit_shareable_adsense_dimensions', array( 'DATE', ) ); $invalid_dimensions = array_diff( $dimensions, $valid_dimensions ); if ( count( $invalid_dimensions ) > 0 ) { $message = count( $invalid_dimensions ) > 1 ? sprintf( /* translators: %s: is replaced with a comma separated list of the invalid dimensions. */ __( 'Unsupported dimensions requested: %s', 'google-site-kit' ), join( /* translators: used between list items, there is a space after the comma. */ __( ', ', 'google-site-kit' ), $invalid_dimensions ) ) : sprintf( /* translators: %s: is replaced with the invalid dimension. */ __( 'Unsupported dimension requested: %s', 'google-site-kit' ), $invalid_dimensions[0] ); throw new Invalid_Report_Dimensions_Exception( $message ); } } /** * Gets the Ad Blocking Recovery setup status label. * * @since 1.107.0 * * @param string $setup_status The saved raw setting. * @return string The status label based on the raw setting. */ private function get_ad_blocking_recovery_setup_status_label( $setup_status ) { switch ( $setup_status ) { case Settings::AD_BLOCKING_RECOVERY_SETUP_STATUS_TAG_PLACED: return __( 'Snippet is placed', 'google-site-kit' ); case Settings::AD_BLOCKING_RECOVERY_SETUP_STATUS_SETUP_CONFIRMED: return __( 'Setup complete', 'google-site-kit' ); default: return __( 'Not set up', 'google-site-kit' ); } } } PK! r"b``AdSense/AMP_Tag.phpnu[get_method_proxy( 'render_story_auto_ads' ) ); } else { // For AMP Native and Transitional (if `wp_body_open` supported). add_action( 'wp_body_open', $this->get_method_proxy( 'render' ), -9999 ); // For AMP Native and Transitional (as fallback). add_filter( 'the_content', $this->get_method_proxy( 'amp_content_add_auto_ads' ) ); // For AMP Reader (if `amp_post_template_body_open` supported). add_action( 'amp_post_template_body_open', $this->get_method_proxy( 'render' ), -9999 ); // For AMP Reader (as fallback). add_action( 'amp_post_template_footer', $this->get_method_proxy( 'render' ), -9999 ); // Load amp-auto-ads component for AMP Reader. $this->enqueue_amp_reader_component_script( 'amp-auto-ads', 'https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js' ); } $this->do_init_tag_action(); } /** * Gets the attributes for amp-story-auto-ads and amp-auto-ads tags. * * @since 1.39.0 * * @param string $type Whether it's for web stories. Can be `web-story` or ``. * @return array Filtered $options. */ private function get_auto_ads_attributes( $type = '' ) { $options = array( 'ad-client' => $this->tag_id, ); if ( 'web-story' === $type && ! empty( $this->story_ad_slot_id ) ) { $options['ad-slot'] = $this->story_ad_slot_id; } $filtered_options = 'web-story' === $type ? apply_filters( 'googlesitekit_amp_story_auto_ads_attributes', $options, $this->tag_id, $this->story_ad_slot_id ) : apply_filters( 'googlesitekit_amp_auto_ads_attributes', $options, $this->tag_id, $this->story_ad_slot_id ); if ( is_array( $filtered_options ) && ! empty( $filtered_options ) ) { $options = $filtered_options; $options['ad-client'] = $this->tag_id; } return $options; } /** * Outputs the tag. * * @since 1.24.0 */ protected function render() { if ( $this->adsense_tag_printed ) { return; } $this->adsense_tag_printed = true; $attributes = ''; foreach ( $this->get_auto_ads_attributes() as $amp_auto_ads_opt_key => $amp_auto_ads_opt_value ) { $attributes .= sprintf( ' data-%s="%s"', esc_attr( $amp_auto_ads_opt_key ), esc_attr( $amp_auto_ads_opt_value ) ); } printf( "\n\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); printf( '', $attributes, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $this->get_tag_blocked_on_consent_attribute() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); printf( "\n\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); } /** * Adds the AMP auto ads tag if opted in. * * @since 1.24.0 * * @param string $content The page content. * @return string Filtered $content. */ private function amp_content_add_auto_ads( $content ) { // Only run for the primary application of the `the_content` filter. if ( $this->adsense_tag_printed || ! in_the_loop() ) { return $content; } $this->adsense_tag_printed = true; $snippet_comment_begin = sprintf( "\n\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); $snippet_comment_end = sprintf( "\n\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); $tag = sprintf( '', esc_attr( $this->tag_id ), $this->get_tag_blocked_on_consent_attribute() ); return $snippet_comment_begin . $tag . $snippet_comment_end . $content; } /** * Set Web Story Ad Slot ID * * @since 1.27.0 * * @param string $ad_slot_id The Ad Slot ID. */ public function set_story_ad_slot_id( $ad_slot_id ) { $this->story_ad_slot_id = $ad_slot_id; } /** * Adds the AMP Web Story auto ads code if enabled. * * @since 1.27.0 */ private function render_story_auto_ads() { $config = array( 'ad-attributes' => array( 'type' => 'adsense', ), ); $attributes = array(); foreach ( $this->get_auto_ads_attributes( 'web-story' ) as $key => $value ) { $attributes[ 'data-' . $key ] = $value; } $config['ad-attributes'] = array_merge( $config['ad-attributes'], $attributes ); printf( "\n\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); printf( '', wp_json_encode( $config ) ); printf( "\n\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) ); } } PK!X (AdSense/Ad_Blocking_Recovery_Web_Tag.phpnu[ad_blocking_recovery_tag = $ad_blocking_recovery_tag; $this->use_error_protection_snippet = $use_error_protection_snippet; } /** * Registers tag hooks. * * @since 1.105.0 */ public function register() { add_action( 'wp_head', $this->get_method_proxy_once( 'render' ) ); add_filter( 'wp_resource_hints', $this->get_dns_prefetch_hints_callback( '//fundingchoicesmessages.google.com' ), 10, 2 ); } /** * Outputs the AdSense script tag. * * @since 1.105.0 */ protected function render() { $tags = $this->ad_blocking_recovery_tag->get(); if ( empty( $tags['tag'] ) || empty( $tags['error_protection_code'] ) ) { return; } printf( "\n\n", esc_html__( 'Google AdSense Ad Blocking Recovery snippet added by Site Kit', 'google-site-kit' ) ); echo $tags['tag']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( "\n\n", esc_html__( 'End Google AdSense Ad Blocking Recovery snippet added by Site Kit', 'google-site-kit' ) ); if ( $this->use_error_protection_snippet ) { printf( "\n\n", esc_html__( 'Google AdSense Ad Blocking Recovery Error Protection snippet added by Site Kit', 'google-site-kit' ) ); echo $tags['error_protection_code']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( "\n\n", esc_html__( 'End Google AdSense Ad Blocking Recovery Error Protection snippet added by Site Kit', 'google-site-kit' ) ); } } } PK!.fG AdSense/Web_Tag.phpnu[get_method_proxy_once( 'render' ) ); add_filter( 'wp_resource_hints', $this->get_dns_prefetch_hints_callback( '//pagead2.googlesyndication.com' ), 10, 2 ); $this->do_init_tag_action(); } /** * Outputs the AdSense script tag. * * @since 1.24.0 */ protected function render() { // If we haven't completed the account connection yet, we still insert the AdSense tag // because it is required for account verification. $adsense_script_src = sprintf( 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=%s&host=%s', esc_attr( $this->tag_id ), // Site owner's web property code. 'ca-host-pub-2644536267352236' // SiteKit's web property code. ); $adsense_script_attributes = array( 'async' => true, 'src' => $adsense_script_src, 'crossorigin' => 'anonymous', ); $adsense_attributes = $this->get_tag_blocked_on_consent_attribute_array(); $auto_ads_opt = array(); $auto_ads_opt_filtered = apply_filters( 'googlesitekit_auto_ads_opt', $auto_ads_opt, $this->tag_id ); if ( is_array( $auto_ads_opt_filtered ) && ! empty( $auto_ads_opt_filtered ) ) { $strip_attributes = array( 'google_ad_client' => '', 'enable_page_level_ads' => '', ); $auto_ads_opt_filtered = array_diff_key( $auto_ads_opt_filtered, $strip_attributes ); $auto_ads_opt_sanitized = array(); foreach ( $auto_ads_opt_filtered as $key => $value ) { $new_key = 'data-'; $new_key .= str_replace( '_', '-', $key ); $auto_ads_opt_sanitized[ $new_key ] = $value; } $adsense_attributes = array_merge( $adsense_attributes, $auto_ads_opt_sanitized ); } printf( "\n\n", esc_html__( 'Google AdSense snippet added by Site Kit', 'google-site-kit' ) ); BC_Functions::wp_print_script_tag( array_merge( $adsense_script_attributes, $adsense_attributes ) ); printf( "\n\n", esc_html__( 'End Google AdSense snippet added by Site Kit', 'google-site-kit' ) ); } } PK!]$AdSense/Ad_Blocking_Recovery_Tag.phpnu[is_valid_tag_object( $option ) ) { return $this->get_default(); } return $option; } /** * Sets ad blocking recovery tag. * * @since 1.104.0 * * @param array $value Array with tag and error protection code. * * @return bool True on success, false on failure. */ public function set( $value ) { if ( ! $this->is_valid_tag_object( $value ) ) { return false; } return parent::set( $value ); } /** * Gets the expected value type. * * @since 1.104.0 * * @return string The type name. */ protected function get_type() { return 'object'; } /** * Gets the default value. * * @since 1.104.0 * * @return array */ protected function get_default() { return array( 'tag' => '', 'error_protection_code' => '', ); } /** * Determines whether the given value is a valid tag object. * * @since 1.104.0 * * @param mixed $tag Tag object. * * @return bool TRUE if valid, otherwise FALSE. */ private function is_valid_tag_object( $tag ) { return is_array( $tag ) && isset( $tag['tag'] ) && isset( $tag['error_protection_code'] ) && is_string( $tag['tag'] ) && is_string( $tag['error_protection_code'] ); } } PK!-SAdSense/Settings.phpnu[ array( 'accountStatus' => 'approved', 'siteStatus' => 'added', ), 'account-connected-nonmatching' => array( 'accountStatus' => 'approved', 'siteStatus' => 'added', ), 'account-connected-no-data' => array( 'accountStatus' => 'approved', 'siteStatus' => 'added', ), 'account-pending-review' => array( 'accountStatus' => 'approved', 'siteStatus' => 'none', ), 'account-required-action' => array( 'accountStatus' => 'no-client', ), 'disapproved-account-afc' => array( 'accountStatus' => 'no-client', ), 'ads-display-pending' => array( 'accountStatus' => 'pending', ), 'disapproved-account' => array( 'accountStatus' => 'disapproved', ), 'no-account' => array( 'accountStatus' => 'none', ), 'no-account-tag-found' => array( 'accountStatus' => 'none', ), ); /** * Registers the setting in WordPress. * * @since 1.2.0 */ public function register() { parent::register(); $this->register_legacy_keys_migration( array( 'account_id' => 'accountID', 'accountId' => 'accountID', 'account_status' => 'accountStatus', 'adsenseTagEnabled' => 'useSnippet', 'client_id' => 'clientID', 'clientId' => 'clientID', 'setup_complete' => 'setupComplete', ) ); $this->register_owned_keys(); add_filter( 'option_' . self::OPTION, function ( $option ) { /** * Filters the AdSense account ID to use. * * @since 1.0.0 * * @param string $account_id Empty by default, will fall back to the option value if not set. */ $account_id = apply_filters( 'googlesitekit_adsense_account_id', '' ); if ( $account_id ) { $option['accountID'] = $account_id; } // Migrate legacy account statuses (now split into account status and site status). if ( ! empty( $option['accountStatus'] ) && isset( $this->legacy_account_statuses[ $option['accountStatus'] ] ) ) { foreach ( $this->legacy_account_statuses[ $option['accountStatus'] ] as $key => $value ) { $option[ $key ] = $value; } } // Migration of legacy setting. if ( ! empty( $option['setupComplete'] ) ) { $option['accountSetupComplete'] = $option['setupComplete']; $option['siteSetupComplete'] = $option['setupComplete']; } unset( $option['setupComplete'] ); return $option; } ); add_filter( 'pre_update_option_' . self::OPTION, function ( $value, $old_value ) { if ( isset( $old_value['setupCompletedTimestamp'] ) ) { return $value; } if ( ! empty( $old_value['accountStatus'] ) && ! empty( $old_value['siteStatus'] ) && 'ready' === $old_value['accountStatus'] && 'ready' === $old_value['siteStatus'] ) { $value['setupCompletedTimestamp'] = strtotime( '-1 month' ); } elseif ( ! empty( $value['accountStatus'] ) && ! empty( $value['siteStatus'] ) && 'ready' === $value['accountStatus'] && 'ready' === $value['siteStatus'] ) { $value['setupCompletedTimestamp'] = time(); } return $value; }, 10, 2 ); } /** * Returns keys for owned settings. * * @since 1.16.0 * * @return array An array of keys for owned settings. */ public function get_owned_keys() { return array( 'accountID', 'clientID', ); } /** * Gets the default value. * * @since 1.2.0 * @since 1.102.0 Added settings for the Ad Blocking Recovery feature. * * @return array */ protected function get_default() { return array( 'ownerID' => 0, 'accountID' => '', 'autoAdsDisabled' => array(), 'clientID' => '', 'accountStatus' => '', 'siteStatus' => '', 'accountSetupComplete' => false, 'siteSetupComplete' => false, 'useSnippet' => true, 'webStoriesAdUnit' => '', 'setupCompletedTimestamp' => null, 'useAdBlockingRecoverySnippet' => false, 'useAdBlockingRecoveryErrorSnippet' => false, 'adBlockingRecoverySetupStatus' => '', ); } /** * Gets the callback for sanitizing the setting's value before saving. * * @since 1.6.0 * * @return callable|null */ protected function get_sanitize_callback() { return function( $option ) { if ( is_array( $option ) ) { if ( isset( $option['accountSetupComplete'] ) ) { $option['accountSetupComplete'] = (bool) $option['accountSetupComplete']; } if ( isset( $option['siteStatusComplete'] ) ) { $option['siteStatusComplete'] = (bool) $option['siteStatusComplete']; } if ( isset( $option['useSnippet'] ) ) { $option['useSnippet'] = (bool) $option['useSnippet']; } if ( isset( $option['autoAdsDisabled'] ) ) { $option['autoAdsDisabled'] = (array) $option['autoAdsDisabled']; } if ( Feature_Flags::enabled( 'adBlockerDetection' ) ) { if ( isset( $option['useAdBlockingRecoverySnippet'] ) ) { $option['useAdBlockingRecoverySnippet'] = (bool) $option['useAdBlockingRecoverySnippet']; } if ( isset( $option['useAdBlockingRecoveryErrorSnippet'] ) ) { $option['useAdBlockingRecoveryErrorSnippet'] = (bool) $option['useAdBlockingRecoveryErrorSnippet']; } if ( isset( $option['adBlockingRecoverySetupStatus'] ) && ! in_array( $option['adBlockingRecoverySetupStatus'], array( '', self::AD_BLOCKING_RECOVERY_SETUP_STATUS_TAG_PLACED, self::AD_BLOCKING_RECOVERY_SETUP_STATUS_SETUP_CONFIRMED, ), true ) ) { $option['adBlockingRecoverySetupStatus'] = $this->get()['adBlockingRecoverySetupStatus']; } } } return $option; }; } } PK!0*AdSense/Ad_Blocking_Recovery_Tag_Guard.phpnu[settings->get(); return ! empty( $settings['adBlockingRecoverySetupStatus'] ) && $settings['useAdBlockingRecoverySnippet']; } } PK!lԔAdSense/Tag_Guard.phpnu[settings->get(); // For web stories, the tag must only be rendered if a story-specific ad unit is provided. if ( is_singular( 'web-story' ) && empty( $settings['webStoriesAdUnit'] ) ) { return false; } return ! empty( $settings['useSnippet'] ) && ! empty( $settings['clientID'] ); } } PK!- RAdSense/Auto_Ad_Guard.phpnu[settings->get(); if ( ! isset( $settings['autoAdsDisabled'] ) ) { return true; } if ( ( in_array( 'loggedinUsers', $settings['autoAdsDisabled'], true ) && is_user_logged_in() ) || ( in_array( 'contentCreators', $settings['autoAdsDisabled'], true ) && current_user_can( 'edit_posts' ) ) ) { return false; } return true; } } PK!;cd PageSpeed_Insights/Settings.phpnu[PK!`fBAnalytics/Tag_Interface.phpnu[PK!O8>2eAnalytics/Google_Service_AnalyticsProvisioning.phpnu[PK!t  Analytics/Account_Ticket.phpnu[PK!wffAnalytics/AMP_Tag.phpnu[PK!vzz!*Analytics/Proxy_AccountTicket.phpnu[PK!KO_0Analytics/Web_Tag.phpnu[PK!'aʝeIAnalytics/Settings.phpnu[PK!)-G**fAnalytics/Advanced_Tracking.phpnu[PK!SS vAnalytics/Proxy_Provisioning.phpnu[PK![G{Analytics/Tag_Guard.phpnu[PK!C7003oAnalytics/Advanced_Tracking/Event_List_Registry.phpnu[PK!Ą*Analytics/Advanced_Tracking/Event_List.phpnu[PK!>  %Analytics/Advanced_Tracking/Event.phpnu[PK!YD/`Analytics/Advanced_Tracking/Script_Injector.phpnu[PK!3SAnalytics/Advanced_Tracking/AMP_Config_Injector.phpnu[PK!^5?5?Site_Verification.phpnu[PK!jy76T6T4Tag_Manager.phpnu[PK! QQ8Search_Console/Settings.phpnu[PK!}Y00E?Analytics_4.phpnu[PK!,YZZ Analytics.phpnu[PK!-,b44 KOptimize.phpnu[PK!P  Tag_Manager/AMP_Tag.phpnu[PK!v  Tag_Manager/Web_Tag.phpnu[PK!R2( ( lTag_Manager/Settings.phpnu[PK!;ppTag_Manager/Tag_Guard.phpnu[PK!›PageSpeed_Insights.phpnu[PK!'oLL!Search_Console.phpnu[PK! ]oOptimize/Web_Tag.phpnu[PK!j` uOptimize/Settings.phpnu[PK!VaOptimize/Tag_Guard.phpnu[PK!ɱWAnalytics_4/Report.phpnu[PK!MAnalytics_4/AMP_Tag.phpnu[PK!0{p!Analytics_4/Web_Tag.phpnu[PK!ձO||5VAnalytics_4/Report/Dimension_Filter/String_Filter.phpnu[PK!67Analytics_4/Report/Dimension_Filter/In_List_Filter.phpnu[PK!.И:.ZAnalytics_4/Report/Dimension_Filter/Filter.phpnu[PK!~IAnalytics_4/Report/Response.phpnu[PK!i..Analytics_4/Report/Request.phpnu[PK!_K Analytics_4/Report/Row_Trait.phpnu[PK!GAnalytics_4/Settings.phpnu[PK!Zj\0Analytics_4/GoogleAnalyticsAdmin/Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest.phpnu[PK!.נ99?m7Analytics_4/GoogleAnalyticsAdmin/AccountProvisioningService.phpnu[PK!֕ѱ5>Analytics_4/GoogleAnalyticsAdmin/AccountsResource.phpnu[PK!]oo+DAnalytics_4/Tag_Guard.phpnu[PK!a/ GAdSense.phpnu[PK! r"b``AdSense/AMP_Tag.phpnu[PK!X (NAdSense/Ad_Blocking_Recovery_Web_Tag.phpnu[PK!.fG AdSense/Web_Tag.phpnu[PK!]$AdSense/Ad_Blocking_Recovery_Tag.phpnu[PK!-S AdSense/Settings.phpnu[PK!0*B'AdSense/Ad_Blocking_Recovery_Tag_Guard.phpnu[PK!lԔJ+AdSense/Tag_Guard.phpnu[PK!- R80AdSense/Auto_Ad_Guard.phpnu[PK665