update
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Datapoint
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Class representing a datapoint definition.
|
||||
*
|
||||
* @since 1.77.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Datapoint {
|
||||
|
||||
/**
|
||||
* Service identifier.
|
||||
*
|
||||
* @since 1.77.0
|
||||
* @var string
|
||||
*/
|
||||
private $service = '';
|
||||
|
||||
/**
|
||||
* Required scopes.
|
||||
*
|
||||
* @since 1.77.0
|
||||
* @var string[]
|
||||
*/
|
||||
private $scopes = array();
|
||||
|
||||
/**
|
||||
* Shareable status.
|
||||
*
|
||||
* @since 1.77.0
|
||||
* @var bool
|
||||
*/
|
||||
private $shareable;
|
||||
|
||||
/**
|
||||
* Request scopes message.
|
||||
*
|
||||
* @since 1.77.0
|
||||
* @var string
|
||||
*/
|
||||
private $request_scopes_message;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param array $definition Definition fields.
|
||||
*/
|
||||
public function __construct( array $definition ) {
|
||||
$this->shareable = ! empty( $definition['shareable'] );
|
||||
|
||||
if ( isset( $definition['service'] ) && is_string( $definition['service'] ) ) {
|
||||
$this->service = $definition['service'];
|
||||
}
|
||||
|
||||
if ( isset( $definition['scopes'] ) && is_array( $definition['scopes'] ) ) {
|
||||
$this->scopes = $definition['scopes'];
|
||||
}
|
||||
|
||||
if ( isset( $definition['request_scopes_message'] ) && is_string( $definition['request_scopes_message'] ) ) {
|
||||
$this->request_scopes_message = $definition['request_scopes_message'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the datapoint is shareable.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_shareable() {
|
||||
return $this->shareable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service identifier.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_service() {
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of required scopes.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_required_scopes() {
|
||||
return $this->scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request scopes message.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_request_scopes_message() {
|
||||
if ( $this->request_scopes_message ) {
|
||||
return $this->request_scopes_message;
|
||||
}
|
||||
|
||||
return __( 'You’ll need to grant Site Kit permission to do this.', 'google-site-kit' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,802 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Module
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\Assets\Assets;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\OAuth_Client;
|
||||
use Google\Site_Kit\Core\Authentication\Exception\Insufficient_Scopes_Exception;
|
||||
use Google\Site_Kit\Core\Authentication\Exception\Google_Proxy_Code_Exception;
|
||||
use Google\Site_Kit\Core\Contracts\WP_Errorable;
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Storage\Options;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
use Google\Site_Kit\Core\Authentication\Authentication;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\Storage\Transients;
|
||||
use Google\Site_Kit_Dependencies\Google\Service as Google_Service;
|
||||
use Google\Site_Kit_Dependencies\Google_Service_Exception;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Base class for a module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*
|
||||
* @property-read string $slug Unique module identifier.
|
||||
* @property-read string $name Module name.
|
||||
* @property-read string $description Module description.
|
||||
* @property-read int $order Module order within module lists.
|
||||
* @property-read string $homepage External module homepage URL.
|
||||
* @property-read array $depends_on List of other module slugs the module depends on.
|
||||
* @property-read bool $force_active Whether the module cannot be disabled.
|
||||
* @property-read bool $internal Whether the module is internal, thus without any UI.
|
||||
*/
|
||||
abstract class Module {
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Option API instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* User Option API instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var User_Options
|
||||
*/
|
||||
protected $user_options;
|
||||
|
||||
/**
|
||||
* Authentication instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Authentication
|
||||
*/
|
||||
protected $authentication;
|
||||
|
||||
/**
|
||||
* Assets API instance.
|
||||
*
|
||||
* @since 1.40.0
|
||||
* @var Assets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* Transients instance.
|
||||
*
|
||||
* @since 1.96.0
|
||||
* @var Transients
|
||||
*/
|
||||
protected $transients;
|
||||
|
||||
/**
|
||||
* Module information.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var array
|
||||
*/
|
||||
private $info = array();
|
||||
|
||||
/**
|
||||
* Google API client instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Google_Site_Kit_Client|null
|
||||
*/
|
||||
private $google_client;
|
||||
|
||||
/**
|
||||
* Google services as $identifier => $service_instance pairs.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var array|null
|
||||
*/
|
||||
private $google_services;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Context $context Plugin context.
|
||||
* @param Options $options Optional. Option API instance. Default is a new instance.
|
||||
* @param User_Options $user_options Optional. User Option API instance. Default is a new instance.
|
||||
* @param Authentication $authentication Optional. Authentication instance. Default is a new instance.
|
||||
* @param Assets $assets Optional. Assets API instance. Default is a new instance.
|
||||
*/
|
||||
public function __construct(
|
||||
Context $context,
|
||||
Options $options = null,
|
||||
User_Options $user_options = null,
|
||||
Authentication $authentication = null,
|
||||
Assets $assets = null
|
||||
) {
|
||||
$this->context = $context;
|
||||
$this->options = $options ?: new Options( $this->context );
|
||||
$this->user_options = $user_options ?: new User_Options( $this->context );
|
||||
$this->authentication = $authentication ?: new Authentication( $this->context, $this->options, $this->user_options );
|
||||
$this->assets = $assets ?: new Assets( $this->context );
|
||||
$this->transients = new Transients( $this->context );
|
||||
$this->info = $this->parse_info( (array) $this->setup_info() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract public function register();
|
||||
|
||||
/**
|
||||
* Magic isset-er.
|
||||
*
|
||||
* Allows checking for existence of module information.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $key Key to check..
|
||||
* @return bool True if value for $key is available, false otherwise.
|
||||
*/
|
||||
final public function __isset( $key ) {
|
||||
return isset( $this->info[ $key ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter.
|
||||
*
|
||||
* Allows reading module information.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $key Key to get value for.
|
||||
* @return mixed Value for $key, or null if not available.
|
||||
*/
|
||||
final public function __get( $key ) {
|
||||
if ( ! isset( $this->info[ $key ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->info[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $datapoint Datapoint to get data for.
|
||||
* @param array|Data_Request $data Optional. Contextual data to provide. Default empty array.
|
||||
* @return mixed Data on success, or WP_Error on failure.
|
||||
*/
|
||||
final public function get_data( $datapoint, $data = array() ) {
|
||||
return $this->execute_data_request(
|
||||
new Data_Request( 'GET', 'modules', $this->slug, $datapoint, $data )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $datapoint Datapoint to get data for.
|
||||
* @param array|Data_Request $data Data to set.
|
||||
* @return mixed Response data on success, or WP_Error on failure.
|
||||
*/
|
||||
final public function set_data( $datapoint, $data ) {
|
||||
return $this->execute_data_request(
|
||||
new Data_Request( 'POST', 'modules', $this->slug, $datapoint, $data )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of datapoints the class provides data for.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of datapoints.
|
||||
*/
|
||||
final public function get_datapoints() {
|
||||
$keys = array();
|
||||
$definitions = $this->get_datapoint_definitions();
|
||||
|
||||
foreach ( array_keys( $definitions ) as $key ) {
|
||||
$parts = explode( ':', $key );
|
||||
$name = end( $parts );
|
||||
if ( ! empty( $name ) ) {
|
||||
$keys[ $name ] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( $keys );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapping between available datapoints and their services.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.9.0 No longer abstract.
|
||||
* @deprecated 1.12.0
|
||||
*
|
||||
* @return array Associative array of $datapoint => $service_identifier pairs.
|
||||
*/
|
||||
protected function get_datapoint_services() {
|
||||
_deprecated_function( __METHOD__, '1.12.0', static::class . '::get_datapoint_definitions' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of datapoint to definition data for each.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @return array Map of datapoints to their definitions.
|
||||
*/
|
||||
protected function get_datapoint_definitions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the datapoint definition instance.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param string $datapoint_id Datapoint ID.
|
||||
* @return Datapoint Datapoint instance.
|
||||
* @throws Invalid_Datapoint_Exception Thrown if no datapoint exists by the given ID.
|
||||
*/
|
||||
protected function get_datapoint_definition( $datapoint_id ) {
|
||||
$definitions = $this->get_datapoint_definitions();
|
||||
|
||||
// All datapoints must be defined.
|
||||
if ( empty( $definitions[ $datapoint_id ] ) ) {
|
||||
throw new Invalid_Datapoint_Exception();
|
||||
}
|
||||
|
||||
return new Datapoint( $definitions[ $datapoint_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
*
|
||||
* // phpcs:ignore Squiz.Commenting.FunctionComment.InvalidNoReturn
|
||||
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
|
||||
* @throws Invalid_Datapoint_Exception Override in a sub-class.
|
||||
*/
|
||||
protected function create_data_request( Data_Request $data ) {
|
||||
throw new Invalid_Datapoint_Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return mixed Data on success, or WP_Error on failure.
|
||||
*/
|
||||
final protected function execute_data_request( Data_Request $data ) {
|
||||
$restore_defers = array();
|
||||
try {
|
||||
$datapoint = $this->get_datapoint_definition( "{$data->method}:{$data->datapoint}" );
|
||||
$oauth_client = $this->get_oauth_client_for_datapoint( $datapoint );
|
||||
|
||||
$this->validate_datapoint_scopes( $datapoint, $oauth_client );
|
||||
$this->validate_base_scopes( $oauth_client );
|
||||
|
||||
// In order for a request to leverage a client other than the default
|
||||
// it must return a RequestInterface (Google Services return this when defer = true).
|
||||
// If not deferred, the request will be executed immediately with the client
|
||||
// the service instance was instantiated with, which will always be the
|
||||
// default client, configured for the current user and provided in `get_service`.
|
||||
|
||||
// Client defer is false by default, so we need to configure the default to defer
|
||||
// even if a different client will be the one to execute the request because
|
||||
// the default instance is what services are setup with.
|
||||
$restore_defers[] = $this->get_client()->withDefer( true );
|
||||
if ( $this->authentication->get_oauth_client() !== $oauth_client ) {
|
||||
$restore_defers[] = $oauth_client->get_client()->withDefer( true );
|
||||
|
||||
$current_user = wp_get_current_user();
|
||||
// Adds the current user to the active consumers list.
|
||||
$oauth_client->add_active_consumer( $current_user );
|
||||
}
|
||||
|
||||
$request = $this->create_data_request( $data );
|
||||
|
||||
if ( is_wp_error( $request ) ) {
|
||||
return $request;
|
||||
} elseif ( $request instanceof Closure ) {
|
||||
$response = $request();
|
||||
} elseif ( $request instanceof RequestInterface ) {
|
||||
$response = $oauth_client->get_client()->execute( $request );
|
||||
} else {
|
||||
return new WP_Error(
|
||||
'invalid_datapoint_request',
|
||||
__( 'Invalid datapoint request.', 'google-site-kit' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
return $this->exception_to_error( $e, $data->datapoint );
|
||||
} finally {
|
||||
foreach ( $restore_defers as $restore_defer ) {
|
||||
$restore_defer();
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->parse_data_response( $data, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates necessary scopes for the given datapoint.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param Datapoint $datapoint Datapoint instance.
|
||||
* @param OAuth_Client $oauth_client OAuth_Client instance.
|
||||
* @throws Insufficient_Scopes_Exception Thrown if required scopes are not satisfied.
|
||||
*/
|
||||
private function validate_datapoint_scopes( Datapoint $datapoint, OAuth_Client $oauth_client ) {
|
||||
$required_scopes = $datapoint->get_required_scopes();
|
||||
|
||||
if ( $required_scopes && ! $oauth_client->has_sufficient_scopes( $required_scopes ) ) {
|
||||
$message = $datapoint->get_request_scopes_message();
|
||||
|
||||
throw new Insufficient_Scopes_Exception( $message, 0, null, $required_scopes );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates necessary scopes for the module.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param OAuth_Client $oauth_client OAuth_Client instance.
|
||||
* @throws Insufficient_Scopes_Exception Thrown if required scopes are not satisfied.
|
||||
*/
|
||||
private function validate_base_scopes( OAuth_Client $oauth_client ) {
|
||||
if ( ! $this instanceof Module_With_Scopes ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $oauth_client->has_sufficient_scopes( $this->get_scopes() ) ) {
|
||||
$message = sprintf(
|
||||
/* translators: %s: module name */
|
||||
__( 'Site Kit can’t access the relevant data from %s because you haven’t granted all permissions requested during setup.', 'google-site-kit' ),
|
||||
$this->name
|
||||
);
|
||||
throw new Insufficient_Scopes_Exception( $message, 0, null, $this->get_scopes() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output for a specific frontend hook.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $hook Frontend hook name, e.g. 'wp_head', 'wp_footer', etc.
|
||||
* @return string Output the hook generates.
|
||||
*/
|
||||
final protected function get_frontend_hook_output( $hook ) {
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
// Unset current user to make WordPress behave as if nobody was logged in.
|
||||
wp_set_current_user( false );
|
||||
|
||||
ob_start();
|
||||
do_action( $hook );
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Restore the current user.
|
||||
wp_set_current_user( $current_user_id );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Google client the module uses.
|
||||
*
|
||||
* This method should be used to access the client.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now returns Google_Site_Kit_Client instance.
|
||||
* @since 1.35.0 Updated to be public.
|
||||
*
|
||||
* @return Google_Site_Kit_Client Google client instance.
|
||||
*
|
||||
* @throws Exception Thrown when the module did not correctly set up the client.
|
||||
*/
|
||||
final public function get_client() {
|
||||
if ( null === $this->google_client ) {
|
||||
$client = $this->setup_client();
|
||||
if ( ! $client instanceof Google_Site_Kit_Client ) {
|
||||
throw new Exception( __( 'Google client not set up correctly.', 'google-site-kit' ) );
|
||||
}
|
||||
$this->google_client = $client;
|
||||
}
|
||||
|
||||
return $this->google_client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the oAuth client instance to use for the given datapoint.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param Datapoint $datapoint Datapoint definition.
|
||||
* @return OAuth_Client OAuth_Client instance.
|
||||
*/
|
||||
private function get_oauth_client_for_datapoint( Datapoint $datapoint ) {
|
||||
if (
|
||||
$this instanceof Module_With_Owner
|
||||
&& $this->is_shareable()
|
||||
&& $datapoint->is_shareable()
|
||||
&& $this->get_owner_id() !== get_current_user_id()
|
||||
&& ! $this->is_recoverable()
|
||||
&& current_user_can( Permissions::READ_SHARED_MODULE_DATA, $this->slug )
|
||||
) {
|
||||
$oauth_client = $this->get_owner_oauth_client();
|
||||
|
||||
try {
|
||||
$this->validate_base_scopes( $oauth_client );
|
||||
return $oauth_client;
|
||||
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// Fallthrough to default oauth client if scopes are unsatisfied.
|
||||
}
|
||||
}
|
||||
|
||||
return $this->authentication->get_oauth_client();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Google service for the given identifier.
|
||||
*
|
||||
* This method should be used to access Google services.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $identifier Identifier for the service.
|
||||
* @return Google_Service Google service instance.
|
||||
*
|
||||
* @throws Exception Thrown when the module did not correctly set up the services or when the identifier is invalid.
|
||||
*/
|
||||
final protected function get_service( $identifier ) {
|
||||
if ( null === $this->google_services ) {
|
||||
$services = $this->setup_services( $this->get_client() );
|
||||
if ( ! is_array( $services ) ) {
|
||||
throw new Exception( __( 'Google services not set up correctly.', 'google-site-kit' ) );
|
||||
}
|
||||
foreach ( $services as $service ) {
|
||||
if ( ! $service instanceof Google_Service ) {
|
||||
throw new Exception( __( 'Google services not set up correctly.', 'google-site-kit' ) );
|
||||
}
|
||||
}
|
||||
$this->google_services = $services;
|
||||
}
|
||||
|
||||
if ( ! isset( $this->google_services[ $identifier ] ) ) {
|
||||
/* translators: %s: service identifier */
|
||||
throw new Exception( sprintf( __( 'Google service identified by %s does not exist.', 'google-site-kit' ), $identifier ) );
|
||||
}
|
||||
|
||||
return $this->google_services[ $identifier ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
abstract protected function setup_info();
|
||||
|
||||
/**
|
||||
* Sets up the Google client the module should use.
|
||||
*
|
||||
* This method is invoked once by {@see Module::get_client()} to lazily set up the client when it is requested
|
||||
* for the first time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now returns Google_Site_Kit_Client instance.
|
||||
*
|
||||
* @return Google_Site_Kit_Client Google client instance.
|
||||
*/
|
||||
protected function setup_client() {
|
||||
return $this->authentication->get_oauth_client()->get_client();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 whether or not to return raw requests and returns a callback to reset to the previous value.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param bool $defer Whether or not to return raw requests.
|
||||
* @return callable Callback function that resets to the original $defer value.
|
||||
*/
|
||||
protected function with_client_defer( $defer ) {
|
||||
return $this->get_client()->withDefer( $defer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $info Associative array of module info.
|
||||
* @return array Parsed $info.
|
||||
*/
|
||||
private function parse_info( array $info ) {
|
||||
$info = wp_parse_args(
|
||||
$info,
|
||||
array(
|
||||
'slug' => '',
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'order' => 10,
|
||||
'homepage' => '',
|
||||
'feature' => '',
|
||||
'depends_on' => array(),
|
||||
'force_active' => static::is_force_active(),
|
||||
'internal' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $info['name'] ) && ! empty( $info['slug'] ) ) {
|
||||
$info['name'] = $info['slug'];
|
||||
}
|
||||
|
||||
$info['depends_on'] = (array) $info['depends_on'];
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an exception into a WP_Error object.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.49.0 Uses the new `Google_Proxy::setup_url_v2` method when the `serviceSetupV2` feature flag is enabled.
|
||||
* @since 1.70.0 $datapoint parameter is optional.
|
||||
*
|
||||
* @param Exception $e Exception object.
|
||||
* @param string $datapoint Optional. Datapoint originally requested. Default is an empty string.
|
||||
* @return WP_Error WordPress error object.
|
||||
*/
|
||||
protected function exception_to_error( Exception $e, $datapoint = '' ) {
|
||||
if ( $e instanceof WP_Errorable ) {
|
||||
return $e->to_wp_error();
|
||||
}
|
||||
|
||||
$code = $e->getCode();
|
||||
|
||||
$message = $e->getMessage();
|
||||
$status = is_numeric( $code ) && $code ? (int) $code : 500;
|
||||
$reason = '';
|
||||
$reconnect_url = '';
|
||||
|
||||
if ( $e instanceof Google_Service_Exception ) {
|
||||
$errors = $e->getErrors();
|
||||
if ( isset( $errors[0]['message'] ) ) {
|
||||
$message = $errors[0]['message'];
|
||||
}
|
||||
if ( isset( $errors[0]['reason'] ) ) {
|
||||
$reason = $errors[0]['reason'];
|
||||
}
|
||||
} elseif ( $e instanceof Google_Proxy_Code_Exception ) {
|
||||
$status = 401;
|
||||
$code = $message;
|
||||
$auth_client = $this->authentication->get_oauth_client();
|
||||
$message = $auth_client->get_error_message( $code );
|
||||
$google_proxy = $this->authentication->get_google_proxy();
|
||||
$credentials = $this->authentication->credentials()->get();
|
||||
$params = array(
|
||||
'code' => $e->getAccessCode(),
|
||||
'site_id' => ! empty( $credentials['oauth2_client_id'] ) ? $credentials['oauth2_client_id'] : '',
|
||||
);
|
||||
$params = $google_proxy->add_setup_step_from_error_code( $params, $code );
|
||||
$reconnect_url = $google_proxy->setup_url( $params );
|
||||
}
|
||||
|
||||
if ( empty( $code ) ) {
|
||||
$code = 'unknown';
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'status' => $status,
|
||||
'reason' => $reason,
|
||||
);
|
||||
|
||||
if ( ! empty( $reconnect_url ) ) {
|
||||
$data['reconnectURL'] = $reconnect_url;
|
||||
}
|
||||
|
||||
return new WP_Error( $code, $message, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the string list into an array of strings.
|
||||
*
|
||||
* @since 1.15.0
|
||||
*
|
||||
* @param string|array $items Items to parse.
|
||||
* @return array An array of string items.
|
||||
*/
|
||||
protected function parse_string_list( $items ) {
|
||||
if ( is_string( $items ) ) {
|
||||
$items = explode( ',', $items );
|
||||
}
|
||||
|
||||
if ( ! is_array( $items ) || empty( $items ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$items = array_map(
|
||||
function( $item ) {
|
||||
if ( ! is_string( $item ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item = trim( $item );
|
||||
if ( empty( $item ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $item;
|
||||
},
|
||||
$items
|
||||
);
|
||||
|
||||
$items = array_filter( $items );
|
||||
$items = array_values( $items );
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current request is for shared data.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return bool TRUE if the request is for shared data, otherwise FALSE.
|
||||
*/
|
||||
protected function is_shared_data_request( Data_Request $data ) {
|
||||
$datapoint = $this->get_datapoint_definition( "{$data->method}:{$data->datapoint}" );
|
||||
$oauth_client = $this->get_oauth_client_for_datapoint( $datapoint );
|
||||
|
||||
if ( $this->authentication->get_oauth_client() !== $oauth_client ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current module is forced to be active or not.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @return bool TRUE if the module forced to be active, otherwise FALSE.
|
||||
*/
|
||||
public static function is_force_active() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module is shareable.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return bool True if module is shareable, false otherwise.
|
||||
*/
|
||||
public function is_shareable() {
|
||||
if ( $this instanceof Module_With_Owner && $this->is_connected() ) {
|
||||
$datapoints = $this->get_datapoint_definitions();
|
||||
foreach ( $datapoints as $details ) {
|
||||
if ( ! empty( $details['shareable'] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module is recoverable.
|
||||
*
|
||||
* @since 1.78.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_recoverable() {
|
||||
/**
|
||||
* Filters the recoverable status of the module.
|
||||
*
|
||||
* @since 1.78.0
|
||||
* @param bool $_ Whether or not the module is recoverable. Default: false
|
||||
* @param string $slug Module slug.
|
||||
*/
|
||||
return (bool) apply_filters( 'googlesitekit_is_module_recoverable', false, $this->slug );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Module_Registry
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class for managing module registration.
|
||||
*
|
||||
* @since 1.21.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Module_Registry {
|
||||
/**
|
||||
* Registered modules.
|
||||
*
|
||||
* @since 1.21.0
|
||||
* @var array
|
||||
*/
|
||||
private $registry = array();
|
||||
|
||||
/**
|
||||
* Registers a module class on the registry.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @param string $module_classname Fully-qualified module class name to register.
|
||||
* @throws InvalidArgumentException Thrown if an invalid module class name is provided.
|
||||
*/
|
||||
public function register( $module_classname ) {
|
||||
if ( ! is_string( $module_classname ) || ! $module_classname ) {
|
||||
throw new InvalidArgumentException( 'A module class name is required to register a module.' );
|
||||
}
|
||||
|
||||
if ( ! class_exists( $module_classname ) ) {
|
||||
throw new InvalidArgumentException( "No class exists for '$module_classname'" );
|
||||
}
|
||||
|
||||
if ( ! is_subclass_of( $module_classname, Module::class ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf( 'All module classes must extend the base module class: %s', Module::class )
|
||||
);
|
||||
}
|
||||
|
||||
$this->registry[ $module_classname ] = $module_classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered module class names.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @return string[] Registered module class names.
|
||||
*/
|
||||
public function get_all() {
|
||||
return array_keys( $this->registry );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Module_Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Storage\Setting;
|
||||
|
||||
/**
|
||||
* Base class for module settings.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_Settings extends Setting {
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
$this->add_option_default_filters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges an array of settings to update.
|
||||
*
|
||||
* Only existing keys will be updated.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param array $partial Partial settings array to save.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function merge( array $partial ) {
|
||||
$settings = $this->get();
|
||||
$partial = array_filter(
|
||||
$partial,
|
||||
function ( $value ) {
|
||||
return null !== $value;
|
||||
}
|
||||
);
|
||||
$updated = array_intersect_key( $partial, $settings );
|
||||
|
||||
return $this->set( array_merge( $settings, $updated ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a filter to ensure default values are present in the saved option.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
protected function add_option_default_filters() {
|
||||
add_filter(
|
||||
'option_' . static::OPTION,
|
||||
function ( $option ) {
|
||||
if ( ! is_array( $option ) ) {
|
||||
return $this->get_default();
|
||||
}
|
||||
return $option;
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
// Fill in any missing keys with defaults.
|
||||
// Must run later to not conflict with legacy key migration.
|
||||
add_filter(
|
||||
'option_' . static::OPTION,
|
||||
function ( $option ) {
|
||||
if ( is_array( $option ) ) {
|
||||
return $option + $this->get_default();
|
||||
}
|
||||
return $option;
|
||||
},
|
||||
99
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected value type.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return string The type name.
|
||||
*/
|
||||
protected function get_type() {
|
||||
return 'object';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Module_Sharing_Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Storage\Setting;
|
||||
use Google\Site_Kit\Core\Util\Sanitize;
|
||||
|
||||
/**
|
||||
* Class for module sharing settings.
|
||||
*
|
||||
* @since 1.50.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Module_Sharing_Settings extends Setting {
|
||||
|
||||
const OPTION = 'googlesitekit_dashboard_sharing';
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected value type.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return string The type name.
|
||||
*/
|
||||
protected function get_type() {
|
||||
return 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback for sanitizing the setting's value before saving.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return callable Callback method that filters or type casts invalid setting values.
|
||||
*/
|
||||
protected function get_sanitize_callback() {
|
||||
return function( $option ) {
|
||||
if ( ! is_array( $option ) ) {
|
||||
return array();
|
||||
}
|
||||
$sanitized_option = array();
|
||||
foreach ( $option as $module_slug => $sharing_settings ) {
|
||||
$sanitized_option[ $module_slug ] = array();
|
||||
|
||||
if ( isset( $sharing_settings['sharedRoles'] ) ) {
|
||||
$filtered_shared_roles = $this->filter_shared_roles( Sanitize::sanitize_string_list( $sharing_settings['sharedRoles'] ) );
|
||||
|
||||
$sanitized_option[ $module_slug ]['sharedRoles'] = $filtered_shared_roles;
|
||||
}
|
||||
|
||||
if ( isset( $sharing_settings['management'] ) ) {
|
||||
$sanitized_option[ $module_slug ]['management'] = (string) $sharing_settings['management'];
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized_option;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the shared roles to only include roles with the edit_posts capability.
|
||||
*
|
||||
* @since 1.85.0.
|
||||
*
|
||||
* @param array $shared_roles The shared roles list.
|
||||
* @return string[] The sanitized shared roles list.
|
||||
*/
|
||||
private function filter_shared_roles( array $shared_roles ) {
|
||||
$filtered_shared_roles = array_filter(
|
||||
$shared_roles,
|
||||
function( $role_slug ) {
|
||||
$role = get_role( $role_slug );
|
||||
|
||||
if ( empty( $role ) || ! $role->has_cap( 'edit_posts' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
return array_values( $filtered_shared_roles );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the settings after filling in default values.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return array Value set for the option, or registered default if not set.
|
||||
*/
|
||||
public function get() {
|
||||
$settings = parent::get();
|
||||
|
||||
foreach ( $settings as $module_slug => $sharing_settings ) {
|
||||
if ( ! isset( $sharing_settings['sharedRoles'] ) || ! is_array( $sharing_settings['sharedRoles'] ) ) {
|
||||
$settings[ $module_slug ]['sharedRoles'] = array();
|
||||
}
|
||||
if ( ! isset( $sharing_settings['management'] ) || ! in_array( $sharing_settings['management'], array( 'all_admins', 'owner' ), true ) ) {
|
||||
$settings[ $module_slug ]['management'] = 'owner';
|
||||
}
|
||||
|
||||
if ( isset( $sharing_settings['sharedRoles'] ) && is_array( $sharing_settings['sharedRoles'] ) ) {
|
||||
$filtered_shared_roles = $this->filter_shared_roles( $sharing_settings['sharedRoles'] );
|
||||
$settings[ $module_slug ]['sharedRoles'] = $filtered_shared_roles;
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a partial Module_Sharing_Settings option array into existing sharing settings.
|
||||
*
|
||||
* @since 1.75.0
|
||||
* @since 1.77.0 Removed capability checks.
|
||||
*
|
||||
* @param array $partial Partial settings array to update existing settings with.
|
||||
*
|
||||
* @return bool True if sharing settings option was updated, false otherwise.
|
||||
*/
|
||||
public function merge( array $partial ) {
|
||||
$settings = $this->get();
|
||||
$partial = array_filter(
|
||||
$partial,
|
||||
function ( $value ) {
|
||||
return ! empty( $value );
|
||||
}
|
||||
);
|
||||
|
||||
return $this->set( $this->array_merge_deep( $settings, $partial ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sharing settings for a given module, or the defaults.
|
||||
*
|
||||
* @since 1.95.0
|
||||
*
|
||||
* @param string $slug Module slug.
|
||||
* @return array {
|
||||
* Sharing settings for the given module.
|
||||
* Default sharing settings do not grant any access so they
|
||||
* are safe to return for a non-existent or non-shareable module.
|
||||
*
|
||||
* @type array $sharedRoles A list of WP Role IDs that the module is shared with.
|
||||
* @type string $management Which users can manage the sharing settings.
|
||||
* }
|
||||
*/
|
||||
public function get_module( $slug ) {
|
||||
$settings = $this->get();
|
||||
|
||||
if ( isset( $settings[ $slug ] ) ) {
|
||||
return $settings[ $slug ];
|
||||
}
|
||||
|
||||
return array(
|
||||
'sharedRoles' => array(),
|
||||
'management' => 'owner',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the settings for a given module.
|
||||
*
|
||||
* @since 1.68.0
|
||||
*
|
||||
* @param string $slug Module slug.
|
||||
*/
|
||||
public function unset_module( $slug ) {
|
||||
$settings = $this->get();
|
||||
|
||||
if ( isset( $settings[ $slug ] ) ) {
|
||||
unset( $settings[ $slug ] );
|
||||
$this->set( $settings );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the combined roles that are set as shareable for all modules.
|
||||
*
|
||||
* @since 1.69.0
|
||||
*
|
||||
* @return array Combined array of shared roles for all modules.
|
||||
*/
|
||||
public function get_all_shared_roles() {
|
||||
$shared_roles = array();
|
||||
$settings = $this->get();
|
||||
foreach ( $settings as $sharing_settings ) {
|
||||
if ( ! isset( $sharing_settings['sharedRoles'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$shared_roles = array_merge( $shared_roles, $sharing_settings['sharedRoles'] );
|
||||
}
|
||||
return array_unique( $shared_roles );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared roles for the given module slug.
|
||||
*
|
||||
* @since 1.69.0
|
||||
*
|
||||
* @param string $slug Module slug.
|
||||
* @return array list of shared roles for the module, otherwise an empty list.
|
||||
*/
|
||||
public function get_shared_roles( $slug ) {
|
||||
$settings = $this->get();
|
||||
|
||||
if ( isset( $settings[ $slug ]['sharedRoles'] ) ) {
|
||||
return $settings[ $slug ]['sharedRoles'];
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two arrays recursively to a specific depth.
|
||||
*
|
||||
* When array1 and array2 have the same string keys, it overwrites
|
||||
* the elements of array1 with elements of array2. Otherwise, it adds/appends
|
||||
* elements of array2.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @param array $array1 First array.
|
||||
* @param array $array2 Second array.
|
||||
* @param int $depth Optional. Depth to merge to. Default is 1.
|
||||
*
|
||||
* @return array Merged array.
|
||||
*/
|
||||
private function array_merge_deep( $array1, $array2, $depth = 1 ) {
|
||||
foreach ( $array2 as $key => $value ) {
|
||||
if ( $depth > 0 && is_array( $value ) ) {
|
||||
$array1_key = isset( $array1[ $key ] ) ? $array1[ $key ] : null;
|
||||
$array1[ $key ] = $this->array_merge_deep( $array1_key, $value, $depth - 1 );
|
||||
} else {
|
||||
$array1[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $array1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Activation
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that has additional behavior when activated.
|
||||
*
|
||||
* @since 1.36.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Activation {
|
||||
/**
|
||||
* Handles module activation.
|
||||
*
|
||||
* @since 1.36.0
|
||||
*/
|
||||
public function on_activation();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Assets
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Assets\Asset;
|
||||
|
||||
/**
|
||||
* Interface for a module that includes assets.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Assets {
|
||||
|
||||
/**
|
||||
* Gets the assets to register for the module.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
public function get_assets();
|
||||
|
||||
/**
|
||||
* Enqueues all assets necessary for the module.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @since 1.37.0 Added the $asset_context argument.
|
||||
*
|
||||
* @param string $asset_context Context for page, see `Asset::CONTEXT_*` constants.
|
||||
*/
|
||||
public function enqueue_assets( $asset_context = Asset::CONTEXT_ADMIN_SITEKIT );
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Assets_Trait
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Assets\Asset;
|
||||
|
||||
/**
|
||||
* Trait for a module that includes assets.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Module_With_Assets_Trait {
|
||||
|
||||
/**
|
||||
* List of the module's Asset objects to register.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @var array
|
||||
*/
|
||||
protected $registerable_assets;
|
||||
|
||||
/**
|
||||
* Gets the assets to register for the module.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
public function get_assets() {
|
||||
if ( null === $this->registerable_assets ) {
|
||||
$this->registerable_assets = $this->setup_assets();
|
||||
}
|
||||
|
||||
return $this->registerable_assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues all assets necessary for the module.
|
||||
*
|
||||
* This default implementation simply enqueues all assets that the module
|
||||
* has registered.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @since 1.37.0 Added the $asset_context argument; only enqueue assets in the correct context.
|
||||
*
|
||||
* @param string $asset_context The page context to load this asset, see `Asset::CONTEXT_*` constants.
|
||||
*/
|
||||
public function enqueue_assets( $asset_context = Asset::CONTEXT_ADMIN_SITEKIT ) {
|
||||
$assets = $this->get_assets();
|
||||
array_walk(
|
||||
$assets,
|
||||
function( Asset $asset, $index, $asset_context ) {
|
||||
if ( $asset->has_context( $asset_context ) ) {
|
||||
$asset->enqueue();
|
||||
}
|
||||
},
|
||||
$asset_context
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's assets to register.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
abstract protected function setup_assets();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Data_Available_State
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that have data available state.
|
||||
*
|
||||
* @since 1.96.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Data_Available_State {
|
||||
|
||||
/**
|
||||
* Checks whether the data is available for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True if data is available, false otherwise.
|
||||
*/
|
||||
public function is_data_available();
|
||||
|
||||
/**
|
||||
* Sets the data available state for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function set_data_available();
|
||||
|
||||
/**
|
||||
* Resets the data available state for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function reset_data_available();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Data_Available_State_Trait
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Trait for a module that has data available state.
|
||||
*
|
||||
* @since 1.96.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Module_With_Data_Available_State_Trait {
|
||||
|
||||
/**
|
||||
* Gets data available transient name of the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return string Data available transient name.
|
||||
*/
|
||||
protected function get_data_available_transient_name() {
|
||||
return "googlesitekit_{$this->slug}_data_available";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data is available for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True if data is available, false otherwise.
|
||||
*/
|
||||
public function is_data_available() {
|
||||
return (bool) $this->transients->get( $this->get_data_available_transient_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data available state for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function set_data_available() {
|
||||
return $this->transients->set( $this->get_data_available_transient_name(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the data available state for the module.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function reset_data_available() {
|
||||
return $this->transients->delete( $this->get_data_available_transient_name() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Deactivation
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that has additional behavior when deactivated.
|
||||
*
|
||||
* @since 1.36.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Deactivation {
|
||||
/**
|
||||
* Handles module deactivation.
|
||||
*
|
||||
* @since 1.36.0
|
||||
*/
|
||||
public function on_deactivation();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Module_With_Debug_Fields
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface Module_With_Debug_Fields
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
interface Module_With_Debug_Fields {
|
||||
|
||||
/**
|
||||
* Gets an array of debug field definitions.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_debug_fields();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Owner
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that includes an owner.
|
||||
*
|
||||
* @since 1.16.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Owner {
|
||||
|
||||
/**
|
||||
* Gets an owner ID for the module.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @return int Owner ID.
|
||||
*/
|
||||
public function get_owner_id();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Owner_Trait
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Authentication\Clients\OAuth_Client;
|
||||
use Google\Site_Kit\Core\Authentication\Profile;
|
||||
use Google\Site_Kit\Core\Authentication\Token;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
|
||||
/**
|
||||
* Trait for a module that includes an owner ID.
|
||||
*
|
||||
* @since 1.16.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Module_With_Owner_Trait {
|
||||
/**
|
||||
* OAuth_Client instance.
|
||||
*
|
||||
* @since 1.77.0.
|
||||
* @var OAuth_Client
|
||||
*/
|
||||
protected $owner_oauth_client;
|
||||
|
||||
/**
|
||||
* Gets an owner ID for the module.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @return int Owner ID.
|
||||
*/
|
||||
public function get_owner_id() {
|
||||
if ( ! $this instanceof Module_With_Settings ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$settings = $this->get_settings()->get();
|
||||
if ( empty( $settings['ownerID'] ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $settings['ownerID'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OAuth_Client instance for the module owner.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return OAuth_Client OAuth_Client instance.
|
||||
*/
|
||||
public function get_owner_oauth_client() {
|
||||
if ( $this->owner_oauth_client instanceof OAuth_Client ) {
|
||||
return $this->owner_oauth_client;
|
||||
}
|
||||
|
||||
$user_options = new User_Options( $this->context, $this->get_owner_id() );
|
||||
|
||||
$this->owner_oauth_client = new OAuth_Client(
|
||||
$this->context,
|
||||
$this->options,
|
||||
$user_options,
|
||||
$this->authentication->credentials(),
|
||||
$this->authentication->get_google_proxy(),
|
||||
new Profile( $user_options ),
|
||||
new Token( $user_options )
|
||||
);
|
||||
|
||||
return $this->owner_oauth_client;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Persistent_Registration
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that requires persistent registration.
|
||||
*
|
||||
* @since 1.38.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Persistent_Registration {
|
||||
|
||||
/**
|
||||
* The registration method that is called even if the module is not activated.
|
||||
*
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public function register_persistent();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Scopes
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Interface for a module that requires Google OAuth scopes.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Scopes {
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
*/
|
||||
public function get_scopes();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Trait for a module that requires Google OAuth scopes.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Module_With_Scopes_Trait {
|
||||
|
||||
/**
|
||||
* Registers the hook to add required scopes.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function register_scopes_hook() {
|
||||
add_filter(
|
||||
'googlesitekit_auth_scopes',
|
||||
function( array $scopes ) {
|
||||
return array_merge( $scopes, $this->get_scopes() );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Service_Entity
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Interface for a module that includes a service entity.
|
||||
*
|
||||
* @since 1.70.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Module_With_Service_Entity {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface Google\Site_Kit\Core\Modules\Module_With_Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
interface Module_With_Settings {
|
||||
|
||||
/**
|
||||
* Gets the module's Setting instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return Module_Settings The Setting instance for the current module.
|
||||
*/
|
||||
public function get_settings();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Settings_Trait
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
/**
|
||||
* Trait for a module that includes a screen.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Module_With_Settings_Trait {
|
||||
|
||||
/**
|
||||
* Settings instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @var Module_Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* Sets up the module's settings instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return Module_Settings
|
||||
*/
|
||||
abstract protected function setup_settings();
|
||||
|
||||
/**
|
||||
* Gets the module's Settings instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return Module_Settings Module_Settings instance.
|
||||
*/
|
||||
public function get_settings() {
|
||||
if ( ! $this->settings instanceof Module_Settings ) {
|
||||
$this->settings = $this->setup_settings();
|
||||
}
|
||||
|
||||
return $this->settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
|
||||
interface Module_With_Tag {
|
||||
|
||||
/**
|
||||
* Registers the tag.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*/
|
||||
public function register_tag();
|
||||
|
||||
/**
|
||||
* Returns the Module_Tag_Matchers instance.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return Module_Tag_Matchers Module_Tag_Matchers instance.
|
||||
*/
|
||||
public function get_tag_matchers();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait Google\Site_Kit\Core\Modules\Module_With_Tag_Trait
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
|
||||
trait Module_With_Tag_Trait {
|
||||
|
||||
/**
|
||||
* Checks if the module tag is found in the provided content.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @param string $content Content to search for the tags.
|
||||
* @return bool TRUE if tag is found, FALSE if not.
|
||||
*/
|
||||
public function has_placed_tag_in_content( $content ) {
|
||||
$tag_matchers = $this->get_tag_matchers()->regex_matchers();
|
||||
$module_name = $this->name;
|
||||
|
||||
// Remove 4 from translatable string name of the module if present.
|
||||
if ( strpos( $module_name, '4' ) !== false ) {
|
||||
$module_name = trim( str_replace( '4', '', $module_name ) );
|
||||
}
|
||||
|
||||
$search_string = 'Google ' . $module_name . ' snippet added by Site Kit';
|
||||
// @TODO Replace the comment text around the module name with methods that should expose it.
|
||||
$search_translatable_string = sprintf(
|
||||
/* translators: %s: translatable module name */
|
||||
__( 'Google %s snippet added by Site Kit', 'google-site-kit' ),
|
||||
$module_name
|
||||
);
|
||||
|
||||
if ( strpos( $content, $search_string ) !== false || strpos( $content, $search_translatable_string ) !== false ) {
|
||||
return Module_Tag_Matchers::TAG_EXISTS_WITH_COMMENTS;
|
||||
} else {
|
||||
foreach ( $tag_matchers as $pattern ) {
|
||||
if ( preg_match( $pattern, $content ) ) {
|
||||
return Module_Tag_Matchers::TAG_EXISTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Module_Tag_Matchers::NO_TAG_FOUND;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,967 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Modules
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\Assets\Assets;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\OAuth_Client;
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Storage\Options;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
use Google\Site_Kit\Core\Authentication\Authentication;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Modules\AdSense;
|
||||
use Google\Site_Kit\Modules\Analytics_4;
|
||||
use Google\Site_Kit\Modules\PageSpeed_Insights;
|
||||
use Google\Site_Kit\Modules\Search_Console;
|
||||
use Google\Site_Kit\Modules\Site_Verification;
|
||||
use Google\Site_Kit\Modules\Tag_Manager;
|
||||
use Google\Site_Kit\Modules\Ads;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class managing the different modules.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Modules {
|
||||
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
const OPTION_ACTIVE_MODULES = 'googlesitekit_active_modules';
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* Option API instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Options
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Module Sharing Settings instance.
|
||||
*
|
||||
* @since 1.68.0
|
||||
* @var Module_Sharing_Settings
|
||||
*/
|
||||
private $sharing_settings;
|
||||
|
||||
/**
|
||||
* User Option API instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var User_Options
|
||||
*/
|
||||
private $user_options;
|
||||
|
||||
/**
|
||||
* Authentication instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Authentication
|
||||
*/
|
||||
private $authentication;
|
||||
|
||||
/**
|
||||
* Available modules as $slug => $module pairs.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var array
|
||||
*/
|
||||
private $modules = array();
|
||||
|
||||
/**
|
||||
* Map of module slugs and which other modules they depend on.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var array
|
||||
*/
|
||||
private $dependencies = array();
|
||||
|
||||
/**
|
||||
* Map of module slugs and which other modules depend on them.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var array
|
||||
*/
|
||||
private $dependants = array();
|
||||
|
||||
/**
|
||||
* Module_Registry instance.
|
||||
*
|
||||
* @since 1.21.0
|
||||
* @var Module_Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* Assets API instance.
|
||||
*
|
||||
* @since 1.40.0
|
||||
* @var Assets
|
||||
*/
|
||||
private $assets;
|
||||
|
||||
/**
|
||||
* REST_Modules_Controller instance.
|
||||
*
|
||||
* @since 1.92.0
|
||||
* @var REST_Modules_Controller
|
||||
*/
|
||||
private $rest_controller;
|
||||
|
||||
/**
|
||||
* REST_Dashboard_Sharing_Controller instance.
|
||||
*
|
||||
* @since 1.109.0
|
||||
* @var REST_Dashboard_Sharing_Controller
|
||||
*/
|
||||
private $dashboard_sharing_controller;
|
||||
|
||||
/**
|
||||
* Core module class names.
|
||||
*
|
||||
* @since 1.21.0
|
||||
* @var string[] Core module class names.
|
||||
*/
|
||||
private $core_modules = array(
|
||||
Site_Verification::MODULE_SLUG => Site_Verification::class,
|
||||
Search_Console::MODULE_SLUG => Search_Console::class,
|
||||
Ads::MODULE_SLUG => Ads::class,
|
||||
Analytics_4::MODULE_SLUG => Analytics_4::class,
|
||||
Tag_Manager::MODULE_SLUG => Tag_Manager::class,
|
||||
AdSense::MODULE_SLUG => AdSense::class,
|
||||
PageSpeed_Insights::MODULE_SLUG => PageSpeed_Insights::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Context $context Plugin context.
|
||||
* @param Options $options Optional. Option API instance. Default is a new instance.
|
||||
* @param User_Options $user_options Optional. User Option API instance. Default is a new instance.
|
||||
* @param Authentication $authentication Optional. Authentication instance. Default is a new instance.
|
||||
* @param Assets $assets Optional. Assets API instance. Default is a new instance.
|
||||
*/
|
||||
public function __construct(
|
||||
Context $context,
|
||||
Options $options = null,
|
||||
User_Options $user_options = null,
|
||||
Authentication $authentication = null,
|
||||
Assets $assets = null
|
||||
) {
|
||||
$this->context = $context;
|
||||
$this->options = $options ?: new Options( $this->context );
|
||||
$this->sharing_settings = new Module_Sharing_Settings( $this->options );
|
||||
$this->user_options = $user_options ?: new User_Options( $this->context );
|
||||
$this->authentication = $authentication ?: new Authentication( $this->context, $this->options, $this->user_options );
|
||||
$this->assets = $assets ?: new Assets( $this->context );
|
||||
|
||||
$this->rest_controller = new REST_Modules_Controller( $this );
|
||||
$this->dashboard_sharing_controller = new REST_Dashboard_Sharing_Controller( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {
|
||||
add_filter(
|
||||
'googlesitekit_features_request_data',
|
||||
function( $body ) {
|
||||
$active_modules = $this->get_active_modules();
|
||||
$connected_modules = array_filter(
|
||||
$active_modules,
|
||||
function( $module ) {
|
||||
return $module->is_connected();
|
||||
}
|
||||
);
|
||||
|
||||
$body['active_modules'] = implode( ' ', array_keys( $active_modules ) );
|
||||
$body['connected_modules'] = implode( ' ', array_keys( $connected_modules ) );
|
||||
return $body;
|
||||
}
|
||||
);
|
||||
|
||||
$available_modules = $this->get_available_modules();
|
||||
array_walk(
|
||||
$available_modules,
|
||||
function( Module $module ) {
|
||||
if ( $module instanceof Module_With_Settings ) {
|
||||
$module->get_settings()->register();
|
||||
}
|
||||
|
||||
if ( $module instanceof Module_With_Persistent_Registration ) {
|
||||
$module->register_persistent();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this->rest_controller->register();
|
||||
$this->sharing_settings->register();
|
||||
$this->dashboard_sharing_controller->register();
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_assets',
|
||||
function( $assets ) use ( $available_modules ) {
|
||||
foreach ( $available_modules as $module ) {
|
||||
if ( $module instanceof Module_With_Assets ) {
|
||||
$assets = array_merge( $assets, $module->get_assets() );
|
||||
}
|
||||
}
|
||||
return $assets;
|
||||
}
|
||||
);
|
||||
|
||||
$active_modules = $this->get_active_modules();
|
||||
array_walk(
|
||||
$active_modules,
|
||||
function( Module $module ) {
|
||||
$module->register();
|
||||
}
|
||||
);
|
||||
|
||||
add_filter( 'googlesitekit_inline_base_data', $this->get_method_proxy( 'inline_js_data' ) );
|
||||
add_filter( 'googlesitekit_inline_tracking_data', $this->get_method_proxy( 'inline_js_data' ) );
|
||||
|
||||
add_filter( 'googlesitekit_inline_modules_data', $this->get_method_proxy( 'inline_modules_data' ) );
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_dashboard_sharing_data',
|
||||
function ( $data ) {
|
||||
$data['sharedOwnershipModules'] = array_keys( $this->get_shared_ownership_modules() );
|
||||
$data['defaultSharedOwnershipModuleSettings'] = $this->populate_default_shared_ownership_module_settings( array() );
|
||||
|
||||
return $data;
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_module_exists',
|
||||
function ( $exists, $slug ) {
|
||||
return $this->module_exists( $slug );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_is_module_recoverable',
|
||||
function ( $recoverable, $slug ) {
|
||||
return $this->is_module_recoverable( $slug );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_is_module_connected',
|
||||
function ( $connected, $slug ) {
|
||||
return $this->is_module_connected( $slug );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter( 'option_' . Module_Sharing_Settings::OPTION, $this->get_method_proxy( 'populate_default_shared_ownership_module_settings' ) );
|
||||
add_filter( 'default_option_' . Module_Sharing_Settings::OPTION, $this->get_method_proxy( 'populate_default_shared_ownership_module_settings' ), 20 );
|
||||
|
||||
$this->sharing_settings->on_change(
|
||||
function( $old_values, $values ) {
|
||||
if ( is_array( $values ) && is_array( $old_values ) ) {
|
||||
array_walk(
|
||||
$values,
|
||||
function( $value, $module_slug ) use ( $old_values ) {
|
||||
if ( ! $this->module_exists( $module_slug ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module = $this->get_module( $module_slug );
|
||||
|
||||
if ( ! $module instanceof Module_With_Service_Entity ) {
|
||||
// If the option was just added, set the ownerID directly and bail.
|
||||
if ( empty( $old_values ) ) {
|
||||
$module->get_settings()->merge(
|
||||
array(
|
||||
'ownerID' => get_current_user_id(),
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$changed_settings = false;
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
array_walk(
|
||||
$value,
|
||||
function( $setting, $setting_key ) use ( $old_values, $module_slug, &$changed_settings ) {
|
||||
// Check if old value is an array and set, then compare both arrays.
|
||||
if (
|
||||
is_array( $setting ) &&
|
||||
isset( $old_values[ $module_slug ][ $setting_key ] ) &&
|
||||
is_array( $old_values[ $module_slug ][ $setting_key ] )
|
||||
) {
|
||||
sort( $setting );
|
||||
sort( $old_values[ $module_slug ][ $setting_key ] );
|
||||
if ( $setting !== $old_values[ $module_slug ][ $setting_key ] ) {
|
||||
$changed_settings = true;
|
||||
}
|
||||
} elseif (
|
||||
// If we don't have the old values or the types are different, then we have updated settings.
|
||||
! isset( $old_values[ $module_slug ][ $setting_key ] ) ||
|
||||
gettype( $setting ) !== gettype( $old_values[ $module_slug ][ $setting_key ] ) ||
|
||||
$setting !== $old_values[ $module_slug ][ $setting_key ]
|
||||
) {
|
||||
$changed_settings = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ( $changed_settings ) {
|
||||
$module->get_settings()->merge(
|
||||
array(
|
||||
'ownerID' => get_current_user_id(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds / modifies data to pass to JS.
|
||||
*
|
||||
* @since 1.78.0
|
||||
*
|
||||
* @param array $data Inline JS data.
|
||||
* @return array Filtered $data.
|
||||
*/
|
||||
private function inline_js_data( $data ) {
|
||||
$all_active_modules = $this->get_active_modules();
|
||||
|
||||
$non_internal_active_modules = array_filter(
|
||||
$all_active_modules,
|
||||
function( Module $module ) {
|
||||
return false === $module->internal;
|
||||
}
|
||||
);
|
||||
|
||||
$data['activeModules'] = array_keys( $non_internal_active_modules );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates modules data to pass to JS.
|
||||
*
|
||||
* @since 1.96.0
|
||||
*
|
||||
* @param array $modules_data Inline modules data.
|
||||
* @return array Inline modules data.
|
||||
*/
|
||||
private function inline_modules_data( $modules_data ) {
|
||||
$available_modules = $this->get_available_modules();
|
||||
|
||||
foreach ( $available_modules as $module ) {
|
||||
if ( $module instanceof Module_With_Data_Available_State ) {
|
||||
$modules_data[ 'data_available_' . $module->slug ] = $this->is_module_active( $module->slug ) && $module->is_connected() && $module->is_data_available();
|
||||
}
|
||||
}
|
||||
|
||||
return $modules_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reference to the Module_Sharing_Settings instance.
|
||||
*
|
||||
* @since 1.69.0
|
||||
*
|
||||
* @return Module_Sharing_Settings An instance of the Module_Sharing_Settings class.
|
||||
*/
|
||||
public function get_module_sharing_settings() {
|
||||
return $this->sharing_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available modules.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.85.0 Filter out modules which are missing any of the dependencies specified in `depends_on`.
|
||||
*
|
||||
* @return array Available modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_available_modules() {
|
||||
if ( empty( $this->modules ) ) {
|
||||
$module_classes = $this->get_registry()->get_all();
|
||||
foreach ( $module_classes as $module_class ) {
|
||||
$instance = new $module_class( $this->context, $this->options, $this->user_options, $this->authentication, $this->assets );
|
||||
|
||||
$this->modules[ $instance->slug ] = $instance;
|
||||
$this->dependencies[ $instance->slug ] = array();
|
||||
$this->dependants[ $instance->slug ] = array();
|
||||
}
|
||||
|
||||
uasort(
|
||||
$this->modules,
|
||||
function( Module $a, Module $b ) {
|
||||
if ( $a->order === $b->order ) {
|
||||
return 0;
|
||||
}
|
||||
return ( $a->order < $b->order ) ? -1 : 1;
|
||||
}
|
||||
);
|
||||
|
||||
// Remove any modules which are missing dependencies. This may occur as the result of a dependency
|
||||
// being removed via the googlesitekit_available_modules filter.
|
||||
$this->modules = array_filter(
|
||||
$this->modules,
|
||||
function( Module $module ) {
|
||||
foreach ( $module->depends_on as $dependency ) {
|
||||
if ( ! isset( $this->modules[ $dependency ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
// Set up dependency maps.
|
||||
foreach ( $this->modules as $module ) {
|
||||
foreach ( $module->depends_on as $dependency ) {
|
||||
if ( $module->slug === $dependency ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->dependencies[ $module->slug ][] = $dependency;
|
||||
$this->dependants[ $dependency ][] = $module->slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active modules.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Active modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_active_modules() {
|
||||
$modules = $this->get_available_modules();
|
||||
$option = $this->get_active_modules_option();
|
||||
|
||||
return array_filter(
|
||||
$modules,
|
||||
function( Module $module ) use ( $option ) {
|
||||
// Force active OR manually active modules.
|
||||
return $module->force_active || in_array( $module->slug, $option, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connected modules.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*
|
||||
* @return array Connected modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_connected_modules() {
|
||||
$modules = $this->get_available_modules();
|
||||
|
||||
return array_filter(
|
||||
$modules,
|
||||
function( Module $module ) {
|
||||
return $this->is_module_connected( $module->slug );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the module identified by the given slug.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return Module Module for the slug.
|
||||
*
|
||||
* @throws Exception Thrown when the module slug is invalid.
|
||||
*/
|
||||
public function get_module( $slug ) {
|
||||
$modules = $this->get_available_modules();
|
||||
|
||||
if ( ! isset( $modules[ $slug ] ) ) {
|
||||
/* translators: %s: module slug */
|
||||
throw new Exception( sprintf( __( 'Invalid module slug %s.', 'google-site-kit' ), $slug ) );
|
||||
}
|
||||
|
||||
return $modules[ $slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the module exists.
|
||||
*
|
||||
* @since 1.80.0
|
||||
*
|
||||
* @param string $slug Module slug.
|
||||
* @return bool True if the module exists, false otherwise.
|
||||
*/
|
||||
public function module_exists( $slug ) {
|
||||
try {
|
||||
$this->get_module( $slug );
|
||||
return true;
|
||||
} catch ( Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of module slugs the module with the given slug depends on.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return array List of slugs for other modules that are dependencies.
|
||||
*
|
||||
* @throws Exception Thrown when the module slug is invalid.
|
||||
*/
|
||||
public function get_module_dependencies( $slug ) {
|
||||
$modules = $this->get_available_modules();
|
||||
|
||||
if ( ! isset( $modules[ $slug ] ) ) {
|
||||
/* translators: %s: module slug */
|
||||
throw new Exception( sprintf( __( 'Invalid module slug %s.', 'google-site-kit' ), $slug ) );
|
||||
}
|
||||
|
||||
return $this->dependencies[ $slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of module slugs that depend on the module with the given slug.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return array List of slugs for other modules that are dependants.
|
||||
*
|
||||
* @throws Exception Thrown when the module slug is invalid.
|
||||
*/
|
||||
public function get_module_dependants( $slug ) {
|
||||
$modules = $this->get_available_modules();
|
||||
|
||||
if ( ! isset( $modules[ $slug ] ) ) {
|
||||
/* translators: %s: module slug */
|
||||
throw new Exception( sprintf( __( 'Invalid module slug %s.', 'google-site-kit' ), $slug ) );
|
||||
}
|
||||
|
||||
return $this->dependants[ $slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module identified by the given slug is active.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True if module is active, false otherwise.
|
||||
*/
|
||||
public function is_module_active( $slug ) {
|
||||
$modules = $this->get_active_modules();
|
||||
|
||||
return isset( $modules[ $slug ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module identified by the given slug is connected.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True if module is connected, false otherwise.
|
||||
*/
|
||||
public function is_module_connected( $slug ) {
|
||||
if ( ! $this->is_module_active( $slug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$module = $this->get_module( $slug );
|
||||
|
||||
return (bool) $module->is_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module identified by the given slug is shareable.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True if module is shareable, false otherwise.
|
||||
*/
|
||||
public function is_module_shareable( $slug ) {
|
||||
$modules = $this->get_shareable_modules();
|
||||
|
||||
return isset( $modules[ $slug ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the module identified by the given slug.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function activate_module( $slug ) {
|
||||
try {
|
||||
$module = $this->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$option = $this->get_active_modules_option();
|
||||
|
||||
if ( in_array( $slug, $option, true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$option[] = $slug;
|
||||
|
||||
$this->set_active_modules_option( $option );
|
||||
|
||||
if ( $module instanceof Module_With_Activation ) {
|
||||
$module->on_activation();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module identified by the given slug is enabled by the option.
|
||||
*
|
||||
* @since 1.46.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True if module has been manually enabled, false otherwise.
|
||||
*/
|
||||
private function manually_enabled( $slug ) {
|
||||
$option = $this->get_active_modules_option();
|
||||
return in_array( $slug, $option, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates the module identified by the given slug.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $slug Unique module slug.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function deactivate_module( $slug ) {
|
||||
try {
|
||||
$module = $this->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$option = $this->get_active_modules_option();
|
||||
|
||||
$key = array_search( $slug, $option, true );
|
||||
if ( false === $key ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prevent deactivation if force-active.
|
||||
if ( $module->force_active ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset( $option[ $key ] );
|
||||
|
||||
$this->set_active_modules_option( array_values( $option ) );
|
||||
|
||||
if ( $module instanceof Module_With_Deactivation ) {
|
||||
$module->on_deactivation();
|
||||
}
|
||||
|
||||
$this->sharing_settings->unset_module( $slug );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues all module-specific assets.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$available_modules = $this->get_available_modules();
|
||||
array_walk(
|
||||
$available_modules,
|
||||
function( Module $module ) {
|
||||
if ( $module instanceof Module_With_Assets ) {
|
||||
$module->enqueue_assets();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured module registry instance.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @return Module_Registry
|
||||
*/
|
||||
protected function get_registry() {
|
||||
if ( ! $this->registry instanceof Module_Registry ) {
|
||||
$this->registry = $this->setup_registry();
|
||||
}
|
||||
|
||||
return $this->registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a fresh module registry instance.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @return Module_Registry
|
||||
*/
|
||||
protected function setup_registry() {
|
||||
$registry = new Module_Registry();
|
||||
/**
|
||||
* Filters core module slugs before registering them in the module registry. Each slug presented on this array will
|
||||
* be registered for inclusion. If a module is forced to be active, then it will be included even if the module slug is
|
||||
* removed from this filter.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @param array $available_modules An array of core module slugs available for registration in the module registry.
|
||||
* @return array An array of filtered module slugs.
|
||||
*/
|
||||
$available_modules = (array) apply_filters( 'googlesitekit_available_modules', array_keys( $this->core_modules ) );
|
||||
$modules = array_fill_keys( $available_modules, true );
|
||||
|
||||
foreach ( $this->core_modules as $slug => $module ) {
|
||||
if ( isset( $modules[ $slug ] ) || call_user_func( array( $module, 'is_force_active' ) ) ) {
|
||||
$registry->register( $module );
|
||||
}
|
||||
}
|
||||
|
||||
return $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option containing the active modules.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of active module slugs.
|
||||
*/
|
||||
private function get_active_modules_option() {
|
||||
$option = $this->options->get( self::OPTION_ACTIVE_MODULES );
|
||||
|
||||
if ( ! is_array( $option ) ) {
|
||||
$option = $this->options->get( 'googlesitekit-active-modules' );
|
||||
}
|
||||
|
||||
// If both options are not arrays, use the default value.
|
||||
if ( ! is_array( $option ) ) {
|
||||
$option = array( PageSpeed_Insights::MODULE_SLUG );
|
||||
}
|
||||
|
||||
return $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the option containing the active modules.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $option List of active module slugs.
|
||||
*/
|
||||
private function set_active_modules_option( array $option ) {
|
||||
$this->options->set( self::OPTION_ACTIVE_MODULES, $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shareable connected modules.
|
||||
*
|
||||
* @since 1.50.0
|
||||
* @since 1.105.0 Updated to only return connected shareable modules.
|
||||
*
|
||||
* @return array Shareable modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_shareable_modules() {
|
||||
$all_connected_modules = $this->get_connected_modules();
|
||||
|
||||
return array_filter(
|
||||
$all_connected_modules,
|
||||
function( Module $module ) {
|
||||
return $module->is_shareable();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given module is recoverable.
|
||||
*
|
||||
* A module is recoverable if:
|
||||
* - No user is identified by its owner ID
|
||||
* - the owner lacks the capability to authenticate
|
||||
* - the owner is no longer authenticated
|
||||
* - no user exists for the owner ID
|
||||
*
|
||||
* @since 1.69.0
|
||||
*
|
||||
* @param Module|string $module A module instance or its slug.
|
||||
* @return bool True if the module is recoverable, false otherwise.
|
||||
*/
|
||||
public function is_module_recoverable( $module ) {
|
||||
if ( is_string( $module ) ) {
|
||||
try {
|
||||
$module = $this->get_module( $module );
|
||||
} catch ( Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $module instanceof Module_With_Owner ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$shared_roles = $this->sharing_settings->get_shared_roles( $module->slug );
|
||||
if ( empty( $shared_roles ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$owner_id = $module->get_owner_id();
|
||||
if ( ! $owner_id || ! user_can( $owner_id, Permissions::AUTHENTICATE ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$restore_user = $this->user_options->switch_user( $owner_id );
|
||||
$owner_authenticated = $this->authentication->is_authenticated();
|
||||
$restore_user();
|
||||
|
||||
if ( ! $owner_authenticated ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recoverable modules.
|
||||
*
|
||||
* @since 1.50.0
|
||||
*
|
||||
* @return array Recoverable modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_recoverable_modules() {
|
||||
return array_filter(
|
||||
$this->get_shareable_modules(),
|
||||
array( $this, 'is_module_recoverable' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets shared ownership modules.
|
||||
*
|
||||
* @since 1.70.0
|
||||
*
|
||||
* @return array Shared ownership modules as $slug => $module pairs.
|
||||
*/
|
||||
public function get_shared_ownership_modules() {
|
||||
return array_filter(
|
||||
$this->get_shareable_modules(),
|
||||
function( $module ) {
|
||||
return ! ( $module instanceof Module_With_Service_Entity );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts default settings for shared ownership modules in passed dashboard sharing settings.
|
||||
*
|
||||
* Sharing settings for shared ownership modules such as pagespeed-insights
|
||||
* should always be manageable by "all admins". This function inserts
|
||||
* this 'default' setting for their respective module slugs even when the
|
||||
* dashboard_sharing settings option is not defined in the database or when settings
|
||||
* are not set for these modules.
|
||||
*
|
||||
* @since 1.75.0
|
||||
* @since 1.85.0 Renamed from filter_shared_ownership_module_settings to populate_default_shared_ownership_module_settings.
|
||||
*
|
||||
* @param array $sharing_settings The dashboard_sharing settings option fetched from the database.
|
||||
* @return array Dashboard sharing settings option with default settings inserted for shared ownership modules.
|
||||
*/
|
||||
protected function populate_default_shared_ownership_module_settings( $sharing_settings ) {
|
||||
$shared_ownership_modules = array_keys( $this->get_shared_ownership_modules() );
|
||||
foreach ( $shared_ownership_modules as $shared_ownership_module ) {
|
||||
if ( ! isset( $sharing_settings[ $shared_ownership_module ] ) ) {
|
||||
$sharing_settings[ $shared_ownership_module ] = array(
|
||||
'sharedRoles' => array(),
|
||||
'management' => 'all_admins',
|
||||
);
|
||||
}
|
||||
}
|
||||
return $sharing_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ownerIDs of all shareable modules.
|
||||
*
|
||||
* @since 1.75.0
|
||||
*
|
||||
* @return array Array of $module_slug => $owner_id.
|
||||
*/
|
||||
public function get_shareable_modules_owners() {
|
||||
$module_owners = array();
|
||||
$shareable_modules = $this->get_shareable_modules();
|
||||
foreach ( $shareable_modules as $module_slug => $module ) {
|
||||
$module_owners[ $module_slug ] = $module->get_owner_id();
|
||||
}
|
||||
return $module_owners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes sharing settings.
|
||||
*
|
||||
* @since 1.84.0
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function delete_dashboard_sharing_settings() {
|
||||
return $this->options->delete( Module_Sharing_Settings::OPTION );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\REST_Dashboard_Sharing_Controller
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\REST_API\REST_Route;
|
||||
use Google\Site_Kit\Core\Util\Collection_Key_Cap_Filter;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Server;
|
||||
|
||||
/**
|
||||
* Class for handling dashboard sharing rest routes.
|
||||
*
|
||||
* @since 1.75.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class REST_Dashboard_Sharing_Controller {
|
||||
|
||||
/**
|
||||
* Modules instance.
|
||||
*
|
||||
* @since 1.75.0
|
||||
* @var Modules
|
||||
*/
|
||||
protected $modules;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.75.0
|
||||
*
|
||||
* @param Modules $modules Modules instance.
|
||||
*/
|
||||
public function __construct( Modules $modules ) {
|
||||
$this->modules = $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.75.0
|
||||
*/
|
||||
public function register() {
|
||||
add_filter(
|
||||
'googlesitekit_rest_routes',
|
||||
function ( $routes ) {
|
||||
return array_merge( $routes, $this->get_rest_routes() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets REST route instances.
|
||||
*
|
||||
* @since 1.75.0
|
||||
*
|
||||
* @return REST_Route[] List of REST_Route objects.
|
||||
*/
|
||||
protected function get_rest_routes() {
|
||||
$can_manage_options = function () {
|
||||
return current_user_can( Permissions::MANAGE_OPTIONS );
|
||||
};
|
||||
|
||||
return array(
|
||||
new REST_Route(
|
||||
'core/modules/data/sharing-settings',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function ( WP_REST_Request $request ) {
|
||||
$original_module_owners = $this->modules->get_shareable_modules_owners();
|
||||
|
||||
$sharing_settings = $this->modules->get_module_sharing_settings();
|
||||
$new_sharing_settings = array_reduce(
|
||||
array(
|
||||
new Collection_Key_Cap_Filter( 'sharedRoles', Permissions::MANAGE_MODULE_SHARING_OPTIONS ),
|
||||
new Collection_Key_Cap_Filter( 'management', Permissions::DELEGATE_MODULE_SHARING_MANAGEMENT ),
|
||||
),
|
||||
function ( $settings, Collection_Key_Cap_Filter $filter ) {
|
||||
return $filter->filter_key_by_cap( $settings );
|
||||
},
|
||||
(array) $request['data']
|
||||
);
|
||||
|
||||
$sharing_settings->merge( $new_sharing_settings );
|
||||
|
||||
$new_module_owners = $this->modules->get_shareable_modules_owners();
|
||||
$changed_module_owners = array_filter(
|
||||
$new_module_owners,
|
||||
function ( $new_owner_id, $module_slug ) use ( $original_module_owners ) {
|
||||
return $new_owner_id !== $original_module_owners[ $module_slug ];
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'settings' => $sharing_settings->get(),
|
||||
// Cast array to an object so JSON encoded response is always an object,
|
||||
// even when the array is empty.
|
||||
'newOwnerIDs' => (object) $changed_module_owners,
|
||||
)
|
||||
);
|
||||
},
|
||||
'permission_callback' => $can_manage_options,
|
||||
'args' => array(
|
||||
'data' => array(
|
||||
'type' => 'object',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => function () {
|
||||
$delete_settings = $this->modules->delete_dashboard_sharing_settings();
|
||||
|
||||
return new WP_REST_Response(
|
||||
$delete_settings
|
||||
);
|
||||
},
|
||||
'permission_callback' => $can_manage_options,
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,809 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\REST_Modules_Controller
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\REST_API\REST_Routes;
|
||||
use Google\Site_Kit\Core\REST_API\REST_Route;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_ViewOnly_Keys_Interface;
|
||||
use WP_REST_Server;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use WP_Error;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class for handling modules rest routes.
|
||||
*
|
||||
* @since 1.92.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class REST_Modules_Controller {
|
||||
|
||||
const REST_ROUTE_CHECK_ACCESS = 'core/modules/data/check-access';
|
||||
|
||||
/**
|
||||
* Modules instance.
|
||||
*
|
||||
* @since 1.92.0
|
||||
* @var Modules
|
||||
*/
|
||||
protected $modules;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @param Modules $modules Modules instance.
|
||||
*/
|
||||
public function __construct( Modules $modules ) {
|
||||
$this->modules = $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*/
|
||||
public function register() {
|
||||
add_filter(
|
||||
'googlesitekit_rest_routes',
|
||||
function( $routes ) {
|
||||
return array_merge( $routes, $this->get_rest_routes() );
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'googlesitekit_apifetch_preload_paths',
|
||||
function ( $paths ) {
|
||||
$modules_routes = array(
|
||||
'/' . REST_Routes::REST_ROOT . '/core/modules/data/list',
|
||||
);
|
||||
|
||||
$settings_routes = array_map(
|
||||
function ( Module $module ) {
|
||||
if ( $module instanceof Module_With_Settings ) {
|
||||
return '/' . REST_Routes::REST_ROOT . "/modules/{$module->slug}/data/settings";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
$this->modules->get_active_modules()
|
||||
);
|
||||
|
||||
return array_merge( $paths, $modules_routes, array_filter( $settings_routes ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the REST schema for a module.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @return array Module REST schema.
|
||||
*/
|
||||
private function get_module_schema() {
|
||||
return array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'module',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Name of the module.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Description of the module.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'homepage' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'The module homepage.', 'google-site-kit' ),
|
||||
'format' => 'uri',
|
||||
'readonly' => true,
|
||||
),
|
||||
'internal' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether the module is internal, thus without any UI.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'active' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether the module is active.', 'google-site-kit' ),
|
||||
),
|
||||
'connected' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether the module setup has been completed.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'dependencies' => array(
|
||||
'type' => 'array',
|
||||
'description' => __( 'List of slugs of other modules that the module depends on.', 'google-site-kit' ),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'readonly' => true,
|
||||
),
|
||||
'dependants' => array(
|
||||
'type' => 'array',
|
||||
'description' => __( 'List of slugs of other modules depending on the module.', 'google-site-kit' ),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'readonly' => true,
|
||||
),
|
||||
'shareable' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether the module is shareable.', 'google-site-kit' ),
|
||||
),
|
||||
'recoverable' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether the module is recoverable.', 'google-site-kit' ),
|
||||
),
|
||||
'owner' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => __( 'Owner ID.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'login' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Owner login.', 'google-site-kit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets related REST routes.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @return array List of REST_Route objects.
|
||||
*/
|
||||
private function get_rest_routes() {
|
||||
$can_setup = function() {
|
||||
return current_user_can( Permissions::SETUP );
|
||||
};
|
||||
|
||||
$can_authenticate = function() {
|
||||
return current_user_can( Permissions::AUTHENTICATE );
|
||||
};
|
||||
|
||||
$can_list_data = function() {
|
||||
return current_user_can( Permissions::VIEW_SPLASH ) || current_user_can( Permissions::VIEW_DASHBOARD );
|
||||
};
|
||||
|
||||
$can_view_insights = function() {
|
||||
// This accounts for routes that need to be called before user has completed setup flow.
|
||||
if ( current_user_can( Permissions::SETUP ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return current_user_can( Permissions::VIEW_POSTS_INSIGHTS );
|
||||
};
|
||||
|
||||
$can_manage_options = function() {
|
||||
// This accounts for routes that need to be called before user has completed setup flow.
|
||||
if ( current_user_can( Permissions::SETUP ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return current_user_can( Permissions::MANAGE_OPTIONS );
|
||||
};
|
||||
|
||||
$get_module_schema = function () {
|
||||
return $this->get_module_schema();
|
||||
};
|
||||
|
||||
return array(
|
||||
new REST_Route(
|
||||
'core/modules/data/list',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$modules = array_map(
|
||||
array( $this, 'prepare_module_data_for_response' ),
|
||||
$this->modules->get_available_modules()
|
||||
);
|
||||
return new WP_REST_Response( array_values( $modules ) );
|
||||
},
|
||||
'permission_callback' => $can_list_data,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'schema' => $get_module_schema,
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'core/modules/data/activation',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$data = $request['data'];
|
||||
$slug = isset( $data['slug'] ) ? $data['slug'] : '';
|
||||
|
||||
try {
|
||||
$this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', $e->getMessage() );
|
||||
}
|
||||
|
||||
$modules = $this->modules->get_available_modules();
|
||||
|
||||
if ( ! empty( $data['active'] ) ) {
|
||||
// Prevent activation if one of the dependencies is not active.
|
||||
$dependency_slugs = $this->modules->get_module_dependencies( $slug );
|
||||
foreach ( $dependency_slugs as $dependency_slug ) {
|
||||
if ( ! $this->modules->is_module_active( $dependency_slug ) ) {
|
||||
/* translators: %s: module name */
|
||||
return new WP_Error( 'inactive_dependencies', sprintf( __( 'Module cannot be activated because of inactive dependency %s.', 'google-site-kit' ), $modules[ $dependency_slug ]->name ), array( 'status' => 500 ) );
|
||||
}
|
||||
}
|
||||
if ( ! $this->modules->activate_module( $slug ) ) {
|
||||
return new WP_Error( 'cannot_activate_module', __( 'An internal error occurred while trying to activate the module.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
} else {
|
||||
// Automatically deactivate dependants.
|
||||
$dependant_slugs = $this->modules->get_module_dependants( $slug );
|
||||
foreach ( $dependant_slugs as $dependant_slug ) {
|
||||
if ( $this->modules->is_module_active( $dependant_slug ) ) {
|
||||
if ( ! $this->modules->deactivate_module( $dependant_slug ) ) {
|
||||
/* translators: %s: module name */
|
||||
return new WP_Error( 'cannot_deactivate_dependant', sprintf( __( 'Module cannot be deactivated because deactivation of dependant %s failed.', 'google-site-kit' ), $modules[ $dependant_slug ]->name ), array( 'status' => 500 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! $this->modules->deactivate_module( $slug ) ) {
|
||||
return new WP_Error( 'cannot_deactivate_module', __( 'An internal error occurred while trying to deactivate the module.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_REST_Response( array( 'success' => true ) );
|
||||
},
|
||||
'permission_callback' => $can_manage_options,
|
||||
'args' => array(
|
||||
'data' => array(
|
||||
'type' => 'object',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'schema' => $get_module_schema,
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'core/modules/data/info',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
try {
|
||||
$module = $this->modules->get_module( $request['slug'] );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', $e->getMessage() );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $this->prepare_module_data_for_response( $module ) );
|
||||
},
|
||||
'permission_callback' => $can_authenticate,
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'schema' => $get_module_schema,
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
self::REST_ROUTE_CHECK_ACCESS,
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$data = $request['data'];
|
||||
$slug = isset( $data['slug'] ) ? $data['slug'] : '';
|
||||
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $module->is_connected() ) {
|
||||
return new WP_Error( 'module_not_connected', __( 'Module is not connected.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
if ( ! $module instanceof Module_With_Service_Entity ) {
|
||||
if ( $module->is_shareable() ) {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'access' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new WP_Error( 'invalid_module', __( 'Module access cannot be checked.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
$access = $module->check_service_entity_access();
|
||||
|
||||
if ( is_wp_error( $access ) ) {
|
||||
return $access;
|
||||
}
|
||||
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'access' => $access,
|
||||
)
|
||||
);
|
||||
},
|
||||
'permission_callback' => $can_setup,
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'modules/(?P<slug>[a-z0-9\-]+)/data/notifications',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$slug = $request['slug'];
|
||||
$modules = $this->modules->get_available_modules();
|
||||
if ( ! isset( $modules[ $slug ] ) ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
$notifications = array();
|
||||
if ( $this->modules->is_module_active( $slug ) ) {
|
||||
$notifications = $modules[ $slug ]->get_data( 'notifications' );
|
||||
if ( is_wp_error( $notifications ) ) {
|
||||
// Don't consider it an error if the module does not have a 'notifications' datapoint.
|
||||
if ( Invalid_Datapoint_Exception::WP_ERROR_CODE === $notifications->get_error_code() ) {
|
||||
$notifications = array();
|
||||
}
|
||||
return $notifications;
|
||||
}
|
||||
}
|
||||
return new WP_REST_Response( $notifications );
|
||||
},
|
||||
'permission_callback' => $can_authenticate,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'modules/(?P<slug>[a-z0-9\-]+)/data/settings',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => function( WP_REST_Request $request ) use ( $can_manage_options ) {
|
||||
$slug = $request['slug'];
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $module instanceof Module_With_Settings ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Module does not support settings.', 'google-site-kit' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$settings = $module->get_settings();
|
||||
|
||||
if ( $can_manage_options() ) {
|
||||
return new WP_REST_Response( $settings->get() );
|
||||
}
|
||||
|
||||
if ( $settings instanceof Setting_With_ViewOnly_Keys_Interface ) {
|
||||
$view_only_settings = array_intersect_key(
|
||||
$settings->get(),
|
||||
array_flip( $settings->get_view_only_keys() )
|
||||
);
|
||||
|
||||
return new WP_REST_Response( $view_only_settings );
|
||||
}
|
||||
|
||||
return new WP_Error( 'no_view_only_settings' );
|
||||
},
|
||||
'permission_callback' => $can_list_data,
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$slug = $request['slug'];
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $module instanceof Module_With_Settings ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Module does not support settings.', 'google-site-kit' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
do_action( 'googlesitekit_pre_save_settings_' . $slug );
|
||||
$module->get_settings()->merge( (array) $request['data'] );
|
||||
|
||||
do_action( 'googlesitekit_save_settings_' . $slug );
|
||||
|
||||
return new WP_REST_Response( $module->get_settings()->get() );
|
||||
},
|
||||
'permission_callback' => $can_manage_options,
|
||||
'args' => array(
|
||||
'data' => array(
|
||||
'type' => 'object',
|
||||
'description' => __( 'Settings to set.', 'google-site-kit' ),
|
||||
'validate_callback' => function( $value ) {
|
||||
return is_array( $value );
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'modules/(?P<slug>[a-z0-9\-]+)/data/data-available',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$slug = $request['slug'];
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $this->modules->is_module_connected( $slug ) ) {
|
||||
return new WP_Error( 'module_not_connected', __( 'Module is not connected.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
if ( ! $module instanceof Module_With_Data_Available_State ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Module does not support setting data available state.', 'google-site-kit' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $module->set_data_available() );
|
||||
},
|
||||
'permission_callback' => $can_list_data,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'modules/(?P<slug>[a-z0-9\-]+)/data/(?P<datapoint>[a-z\-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$slug = $request['slug'];
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $this->modules->is_module_active( $slug ) ) {
|
||||
return new WP_Error( 'module_not_active', __( 'Module must be active to request data.', 'google-site-kit' ), array( 'status' => 403 ) );
|
||||
}
|
||||
|
||||
$data = $module->get_data( $request['datapoint'], $request->get_params() );
|
||||
if ( is_wp_error( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
return new WP_REST_Response( $data );
|
||||
},
|
||||
'permission_callback' => $can_view_insights,
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$slug = $request['slug'];
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'invalid_module_slug', __( 'Invalid module slug.', 'google-site-kit' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! $this->modules->is_module_active( $slug ) ) {
|
||||
return new WP_Error( 'module_not_active', __( 'Module must be active to request data.', 'google-site-kit' ), array( 'status' => 403 ) );
|
||||
}
|
||||
|
||||
$data = isset( $request['data'] ) ? (array) $request['data'] : array();
|
||||
$data = $module->set_data( $request['datapoint'], $data );
|
||||
if ( is_wp_error( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
return new WP_REST_Response( $data );
|
||||
},
|
||||
'permission_callback' => $can_manage_options,
|
||||
'args' => array(
|
||||
'data' => array(
|
||||
'type' => 'object',
|
||||
'description' => __( 'Data to set.', 'google-site-kit' ),
|
||||
'validate_callback' => function( $value ) {
|
||||
return is_array( $value );
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Identifier for the module.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
'datapoint' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Module data point to address.', 'google-site-kit' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
new REST_Route(
|
||||
'core/modules/data/recover-modules',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => function( WP_REST_Request $request ) {
|
||||
$data = $request['data'];
|
||||
$slugs = isset( $data['slugs'] ) ? $data['slugs'] : array();
|
||||
|
||||
if ( ! is_array( $slugs ) || empty( $slugs ) ) {
|
||||
return new WP_Error(
|
||||
'invalid_param',
|
||||
__( 'Request parameter slugs is not valid.', 'google-site-kit' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'success' => array(),
|
||||
'error' => array(),
|
||||
);
|
||||
|
||||
foreach ( $slugs as $slug ) {
|
||||
try {
|
||||
$module = $this->modules->get_module( $slug );
|
||||
} catch ( Exception $e ) {
|
||||
$response = $this->handle_module_recovery_error(
|
||||
$slug,
|
||||
$response,
|
||||
new WP_Error(
|
||||
'invalid_module_slug',
|
||||
$e->getMessage(),
|
||||
array( 'status' => 404 )
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $module->is_shareable() ) {
|
||||
$response = $this->handle_module_recovery_error(
|
||||
$slug,
|
||||
$response,
|
||||
new WP_Error(
|
||||
'module_not_shareable',
|
||||
__( 'Module is not shareable.', 'google-site-kit' ),
|
||||
array( 'status' => 404 )
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $this->modules->is_module_recoverable( $module ) ) {
|
||||
$response = $this->handle_module_recovery_error(
|
||||
$slug,
|
||||
$response,
|
||||
new WP_Error(
|
||||
'module_not_recoverable',
|
||||
__( 'Module is not recoverable.', 'google-site-kit' ),
|
||||
array( 'status' => 403 )
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$check_access_endpoint = '/' . REST_Routes::REST_ROOT . '/' . self::REST_ROUTE_CHECK_ACCESS;
|
||||
$check_access_request = new WP_REST_Request( 'POST', $check_access_endpoint );
|
||||
$check_access_request->set_body_params(
|
||||
array(
|
||||
'data' => array(
|
||||
'slug' => $slug,
|
||||
),
|
||||
)
|
||||
);
|
||||
$check_access_response = rest_do_request( $check_access_request );
|
||||
|
||||
if ( is_wp_error( $check_access_response ) ) {
|
||||
$response = $this->handle_module_recovery_error(
|
||||
$slug,
|
||||
$response,
|
||||
$check_access_response
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$access = isset( $check_access_response->data['access'] ) ? $check_access_response->data['access'] : false;
|
||||
if ( ! $access ) {
|
||||
$response = $this->handle_module_recovery_error(
|
||||
$slug,
|
||||
$response,
|
||||
new WP_Error(
|
||||
'module_not_accessible',
|
||||
__( 'Module is not accessible by current user.', 'google-site-kit' ),
|
||||
array( 'status' => 403 )
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the module's ownerID to the ID of the user making the request.
|
||||
$module_setting_updates = array(
|
||||
'ownerID' => get_current_user_id(),
|
||||
);
|
||||
$recovered_module = $module->get_settings()->merge( $module_setting_updates );
|
||||
|
||||
if ( $recovered_module ) {
|
||||
$response['success'][ $slug ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cast error array to an object so JSON encoded response is
|
||||
// always an object, even when the error array is empty.
|
||||
if ( ! $response['error'] ) {
|
||||
$response['error'] = (object) array();
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $response );
|
||||
},
|
||||
'permission_callback' => $can_setup,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'schema' => $get_module_schema,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares module data for a REST response according to the schema.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @param Module $module Module instance.
|
||||
* @return array Module REST response data.
|
||||
*/
|
||||
private function prepare_module_data_for_response( Module $module ) {
|
||||
$module_data = array(
|
||||
'slug' => $module->slug,
|
||||
'name' => $module->name,
|
||||
'description' => $module->description,
|
||||
'homepage' => $module->homepage,
|
||||
'internal' => $module->internal,
|
||||
'order' => $module->order,
|
||||
'forceActive' => $module->force_active,
|
||||
'recoverable' => $module->is_recoverable(),
|
||||
'shareable' => $this->modules->is_module_shareable( $module->slug ),
|
||||
'active' => $this->modules->is_module_active( $module->slug ),
|
||||
'connected' => $this->modules->is_module_connected( $module->slug ),
|
||||
'dependencies' => $this->modules->get_module_dependencies( $module->slug ),
|
||||
'dependants' => $this->modules->get_module_dependants( $module->slug ),
|
||||
'owner' => null,
|
||||
);
|
||||
|
||||
if ( current_user_can( 'list_users' ) && $module instanceof Module_With_Owner ) {
|
||||
$owner_id = $module->get_owner_id();
|
||||
if ( $owner_id ) {
|
||||
$module_data['owner'] = array(
|
||||
'id' => $owner_id,
|
||||
'login' => get_the_author_meta( 'user_login', $owner_id ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $module_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares error data to pass with WP_REST_Response.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @param WP_Error $error Error (WP_Error) to prepare.
|
||||
*
|
||||
* @return array Formatted error response suitable for the client.
|
||||
*/
|
||||
protected function prepare_error_response( $error ) {
|
||||
return array(
|
||||
'code' => $error->get_error_code(),
|
||||
'message' => $error->get_error_message(),
|
||||
'data' => $error->get_error_data(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates response with error encounted during module recovery.
|
||||
*
|
||||
* @since 1.92.0
|
||||
*
|
||||
* @param string $slug The module slug.
|
||||
* @param array $response The existing response.
|
||||
* @param WP_Error $error The error encountered.
|
||||
*
|
||||
* @return array The updated response with error included.
|
||||
*/
|
||||
protected function handle_module_recovery_error( $slug, $response, $error ) {
|
||||
$response['success'][ $slug ] = false;
|
||||
$response['error'][ $slug ] = $this->prepare_error_response(
|
||||
$error
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tags\Module_AMP_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Tags
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tags;
|
||||
|
||||
use Google\Site_Kit\Core\Tags\Blockable_Tag_Interface;
|
||||
|
||||
/**
|
||||
* Base class for AMP tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_AMP_Tag extends Module_Tag implements Blockable_Tag_Interface {
|
||||
|
||||
/**
|
||||
* Checks whether or not the tag should be blocked from rendering.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @return bool TRUE if the tag should be blocked, otherwise FALSE.
|
||||
*/
|
||||
public function is_tag_blocked() {
|
||||
/**
|
||||
* Filters whether or not the AMP tag should be blocked from rendering.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param bool $blocked Whether or not the tag output is suppressed. Default: false.
|
||||
*/
|
||||
return (bool) apply_filters( "googlesitekit_{$this->module_slug}_tag_amp_blocked", false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML attributes for a script tag that may potentially require user consent before loading.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @return string HTML attributes to add if the tag requires consent to load, or an empty string.
|
||||
*/
|
||||
public function get_tag_blocked_on_consent_attribute() {
|
||||
// @see https://amp.dev/documentation/components/amp-consent/#advanced-predefined-consent-blocking-behaviors
|
||||
$allowed_amp_block_on_consent_values = array(
|
||||
'_till_responded',
|
||||
'_till_accepted',
|
||||
'_auto_reject',
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters whether the tag requires user consent before loading.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param bool|string $blocked Whether or not the tag requires user consent to load. Alternatively, this can also be one of
|
||||
* the special string values '_till_responded', '_till_accepted', or '_auto_reject'. Default: false.
|
||||
*/
|
||||
$block_on_consent = apply_filters( "googlesitekit_{$this->module_slug}_tag_amp_block_on_consent", false );
|
||||
if ( in_array( $block_on_consent, $allowed_amp_block_on_consent_values, true ) ) {
|
||||
return sprintf( ' data-block-on-consent="%s"', $block_on_consent );
|
||||
}
|
||||
|
||||
if ( filter_var( $block_on_consent, FILTER_VALIDATE_BOOLEAN ) ) {
|
||||
return ' data-block-on-consent';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a component script for AMP Reader.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $handle Script handle.
|
||||
* @param string $src Script source URL.
|
||||
* @return callable Hook function.
|
||||
*/
|
||||
protected function enqueue_amp_reader_component_script( $handle, $src ) {
|
||||
$component_script_hook = function( $data ) use ( $handle, $src ) {
|
||||
if ( ! isset( $data['amp_component_scripts'] ) || ! is_array( $data['amp_component_scripts'] ) ) {
|
||||
$data['amp_component_scripts'] = array();
|
||||
}
|
||||
|
||||
if ( ! isset( $data['amp_component_scripts'][ $handle ] ) ) {
|
||||
$data['amp_component_scripts'][ $handle ] = $src;
|
||||
}
|
||||
|
||||
return $data;
|
||||
};
|
||||
|
||||
add_filter( 'amp_post_template_data', $component_script_hook );
|
||||
|
||||
return $component_script_hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the "googlesitekit_{module_slug}_init_tag_amp" action to let 3rd party plugins to perform required setup.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function do_init_tag_action() {
|
||||
/**
|
||||
* Fires when the tag has been initialized which means that the tag will be rendered in the current request.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $tag_id Tag ID.
|
||||
*/
|
||||
do_action( "googlesitekit_{$this->module_slug}_init_tag_amp", $this->tag_id );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tags\Module_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Tags
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tags;
|
||||
|
||||
use Google\Site_Kit\Core\Tags\Tag;
|
||||
|
||||
/**
|
||||
* Base class for a module tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_Tag extends Tag {
|
||||
|
||||
/**
|
||||
* Module slug.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @since 1.109.0 Renamed from slug to module_slug.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $module_slug;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $tag_id Tag ID.
|
||||
* @param string $module_slug Module slug.
|
||||
*/
|
||||
public function __construct( $tag_id, $module_slug ) {
|
||||
parent::__construct( $tag_id );
|
||||
$this->module_slug = $module_slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
abstract protected function render();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Tags
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tags;
|
||||
|
||||
use Google\Site_Kit\Core\Guards\Guard_Interface;
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Base class for a module tag guard.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_Tag_Guard implements Guard_Interface {
|
||||
|
||||
/**
|
||||
* Module settings.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @var Module_Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param Module_Settings $settings Module settings.
|
||||
*/
|
||||
public function __construct( Module_Settings $settings ) {
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract public function can_activate();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\Tags
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tags;
|
||||
|
||||
use Google\Site_Kit\Core\Tags\Tag_Matchers_Interface;
|
||||
|
||||
/**
|
||||
* Base class for Tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_Tag_Matchers implements Tag_Matchers_Interface {
|
||||
|
||||
const NO_TAG_FOUND = 0;
|
||||
const TAG_EXISTS = 1;
|
||||
const TAG_EXISTS_WITH_COMMENTS = 2;
|
||||
|
||||
/**
|
||||
* Holds array of regex tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return array Array of regex matchers.
|
||||
*/
|
||||
abstract public function regex_matchers();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tags\Module_Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\Tags
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tags;
|
||||
|
||||
use Google\Site_Kit\Core\Tags\Blockable_Tag_Interface;
|
||||
|
||||
/**
|
||||
* Base class for Web tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Module_Web_Tag extends Module_Tag implements Blockable_Tag_Interface {
|
||||
|
||||
/**
|
||||
* Checks whether or not the tag should be blocked from rendering.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @return bool TRUE if the tag should be blocked, otherwise FALSE.
|
||||
*/
|
||||
public function is_tag_blocked() {
|
||||
/**
|
||||
* Filters whether or not the tag should be blocked from rendering.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param bool $blocked Whether or not the tag output is suppressed. Default: false.
|
||||
*/
|
||||
return (bool) apply_filters( "googlesitekit_{$this->module_slug}_tag_blocked", false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML attributes for a script tag that may potentially require user consent before loading.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @return string HTML attributes to add if the tag requires consent to load, or an empty string.
|
||||
*/
|
||||
public function get_tag_blocked_on_consent_attribute() {
|
||||
if ( $this->is_tag_blocked_on_consent() ) {
|
||||
return ' type="text/plain" data-block-on-consent';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of HTML attributes for a script tag that may potentially require user consent before loading.
|
||||
*
|
||||
* @since 1.41.0
|
||||
*
|
||||
* @return array containing HTML attributes to add if the tag requires consent to load, or an empty array.
|
||||
*/
|
||||
public function get_tag_blocked_on_consent_attribute_array() {
|
||||
if ( $this->is_tag_blocked_on_consent() ) {
|
||||
return array(
|
||||
'type' => 'text/plain',
|
||||
'data-block-on-consent' => true,
|
||||
);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the tag is set to be manually blocked for consent.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_tag_blocked_on_consent() {
|
||||
$deprecated_args = (array) $this->get_tag_blocked_on_consent_deprecated_args();
|
||||
|
||||
/**
|
||||
* Filters whether the tag requires user consent before loading.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param bool $blocked Whether or not the tag requires user consent to load. Default: false.
|
||||
*/
|
||||
if ( $deprecated_args ) {
|
||||
return (bool) apply_filters_deprecated(
|
||||
"googlesitekit_{$this->module_slug}_tag_block_on_consent",
|
||||
array( false ),
|
||||
...$deprecated_args
|
||||
);
|
||||
}
|
||||
|
||||
return (bool) apply_filters( "googlesitekit_{$this->module_slug}_tag_block_on_consent", false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contextual arguments for apply_filters_deprecated if block_on_consent is deprecated.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_tag_blocked_on_consent_deprecated_args() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the "googlesitekit_{module_slug}_init_tag" action to let 3rd party plugins to perform required setup.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function do_init_tag_action() {
|
||||
/**
|
||||
* Fires when the tag has been initialized which means that the tag will be rendered in the current request.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $tag_id Tag ID.
|
||||
*/
|
||||
do_action( "googlesitekit_{$this->module_slug}_init_tag", $this->tag_id );
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user