Commit realizado el 12:13:52 08-04-2024
This commit is contained in:
462
wp-content/plugins/web-stories/includes/Integrations/AMP.php
Normal file
462
wp-content/plugins/web-stories/includes/Integrations/AMP.php
Normal file
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
/**
|
||||
* Class AMP
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use DOMElement;
|
||||
use Google\Web_Stories\AMP\Integration\AMP_Story_Sanitizer;
|
||||
use Google\Web_Stories\Context;
|
||||
use Google\Web_Stories\Infrastructure\HasRequirements;
|
||||
use Google\Web_Stories\Model\Story;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use Google\Web_Stories\Settings;
|
||||
use Google\Web_Stories\Story_Post_Type;
|
||||
use WP_Post;
|
||||
|
||||
/**
|
||||
* Class AMP.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
|
||||
*
|
||||
* @phpstan-type AMPOptions array{
|
||||
* theme_support?: string,
|
||||
* supported_post_types?: string[],
|
||||
* supported_templates?: string[]
|
||||
* }
|
||||
*
|
||||
* @phpstan-type AMPSanitizers array{
|
||||
* AMP_Style_Sanitizer?: array{
|
||||
* dynamic_element_selectors?: string[]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
class AMP extends Service_Base implements HasRequirements {
|
||||
/**
|
||||
* Slug of the AMP validated URL post type.
|
||||
*/
|
||||
public const AMP_VALIDATED_URL_POST_TYPE = 'amp_validated_url';
|
||||
|
||||
/**
|
||||
* Settings instance.
|
||||
*
|
||||
* @var Settings Settings instance.
|
||||
*/
|
||||
private Settings $settings;
|
||||
|
||||
/**
|
||||
* Story_Post_Type instance.
|
||||
*
|
||||
* @var Story_Post_Type Story_Post_Type instance.
|
||||
*/
|
||||
private Story_Post_Type $story_post_type;
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @var Context Context instance.
|
||||
*/
|
||||
private Context $context;
|
||||
|
||||
/**
|
||||
* Analytics constructor.
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @param Settings $settings Settings instance.
|
||||
* @param Story_Post_Type $story_post_type Experiments instance.
|
||||
* @param Context $context Context instance.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
Story_Post_Type $story_post_type,
|
||||
Context $context
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->story_post_type = $story_post_type;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'option_amp-options', [ $this, 'filter_amp_options' ] );
|
||||
add_filter( 'amp_supportable_post_types', [ $this, 'filter_supportable_post_types' ] );
|
||||
add_filter( 'amp_to_amp_linking_element_excluded', [ $this, 'filter_amp_to_amp_linking_element_excluded' ], 10, 4 );
|
||||
add_filter( 'amp_content_sanitizers', [ $this, 'add_amp_content_sanitizers' ] );
|
||||
add_filter( 'amp_validation_error_sanitized', [ $this, 'filter_amp_validation_error_sanitized' ], 10, 2 );
|
||||
add_filter( 'amp_skip_post', [ $this, 'filter_amp_skip_post' ], 10, 2 );
|
||||
|
||||
// This filter is actually used in this plugin's `Sanitization` class.
|
||||
add_filter( 'web_stories_amp_validation_error_sanitized', [ $this, 'filter_amp_validation_error_sanitized' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of service IDs required for this service to be registered.
|
||||
*
|
||||
* Needed because settings needs to be registered first.
|
||||
*
|
||||
* @since 1.13.0
|
||||
*
|
||||
* @return string[] List of required services.
|
||||
*/
|
||||
public static function get_requirements(): array {
|
||||
return [ 'settings' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter AMP options to force Standard mode (AMP-first) when a web story is being requested.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param array|mixed $options Options.
|
||||
* @return array|mixed Filtered options.
|
||||
*
|
||||
* @phpstan-param AMPOptions $options
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($options is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function filter_amp_options( $options ) {
|
||||
if ( ! \is_array( $options ) ) {
|
||||
return $options;
|
||||
}
|
||||
if ( $this->get_request_post_type() === $this->story_post_type->get_slug() ) {
|
||||
$options['theme_support'] = 'standard';
|
||||
$options['supported_post_types'][] = $this->story_post_type->get_slug();
|
||||
$options['supported_templates'][] = 'is_singular';
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the post types which are supportable.
|
||||
*
|
||||
* Remove web-stories from the list unless the currently requested post type is for a web-story. This is done in
|
||||
* order to hide stories from the list of supportable post types on the AMP Settings screen.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param string[]|mixed $post_types Supportable post types.
|
||||
* @return array|mixed Supportable post types.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($post_types is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function filter_supportable_post_types( $post_types ) {
|
||||
if ( ! \is_array( $post_types ) ) {
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
$story_post_type = $this->story_post_type->get_slug();
|
||||
|
||||
$post_types = array_diff( $post_types, [ $story_post_type ] );
|
||||
|
||||
if ( $this->get_request_post_type() === $story_post_type ) {
|
||||
$post_types = [ ...$post_types, $story_post_type ];
|
||||
}
|
||||
|
||||
return array_unique( array_values( $post_types ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the AMP plugin's sanitizers.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param array|mixed $sanitizers Sanitizers.
|
||||
* @return array|mixed Sanitizers.
|
||||
*
|
||||
* @phpstan-param AMPSanitizers|mixed $sanitizers
|
||||
* @phpstan-return AMPSanitizers|mixed
|
||||
*/
|
||||
public function add_amp_content_sanitizers( $sanitizers ) {
|
||||
if ( ! $this->context->is_web_story() ) {
|
||||
return $sanitizers;
|
||||
}
|
||||
|
||||
$post = get_queried_object();
|
||||
if ( ! ( $post instanceof WP_Post ) ) {
|
||||
return $sanitizers;
|
||||
}
|
||||
|
||||
if ( ! \is_array( $sanitizers ) ) {
|
||||
return $sanitizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* AMP sanitizer configuration.
|
||||
*
|
||||
* @phpstan-var AMPSanitizers $sanitizers
|
||||
*/
|
||||
|
||||
$video_cache_enabled = (bool) $this->settings->get_setting( $this->settings::SETTING_NAME_VIDEO_CACHE );
|
||||
|
||||
$story = new Story();
|
||||
$story->load_from_post( $post );
|
||||
|
||||
$poster_images = [
|
||||
'poster-portrait-src' => esc_url_raw( $story->get_poster_portrait() ),
|
||||
];
|
||||
|
||||
if ( isset( $sanitizers['AMP_Style_Sanitizer'] ) ) {
|
||||
if ( ! isset( $sanitizers['AMP_Style_Sanitizer']['dynamic_element_selectors'] ) ) {
|
||||
$sanitizers['AMP_Style_Sanitizer']['dynamic_element_selectors'] = [];
|
||||
}
|
||||
|
||||
$sanitizers['AMP_Style_Sanitizer']['dynamic_element_selectors'][] = 'amp-story-captions';
|
||||
}
|
||||
|
||||
$sanitizers[ AMP_Story_Sanitizer::class ] = [
|
||||
'publisher_logo' => (string) $story->get_publisher_logo_url(),
|
||||
'publisher' => $story->get_publisher_name(),
|
||||
'poster_images' => array_filter( $poster_images ),
|
||||
'video_cache' => $video_cache_enabled,
|
||||
'title_tag' => wp_get_document_title(),
|
||||
'description' => wp_strip_all_tags( get_the_excerpt() ),
|
||||
];
|
||||
|
||||
return $sanitizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter amp_validation_error_sanitized to prevent invalid markup removal for Web Stories.
|
||||
*
|
||||
* Since the amp-story element requires the poster-portrait-src attribute to be valid, when this attribute is absent
|
||||
* the AMP plugin will try to remove the amp-story element altogether. This is not the preferred resolution! So
|
||||
* instead, this will force the invalid markup to be kept. When this is done, the AMP plugin in Standard mode
|
||||
* (which Web Stories enforces while serving singular web-story posts) will remove the amp attribute from the html
|
||||
* element so that the page will not be advertised as AMP. This prevents GSC from complaining about a validation
|
||||
* issue which we already know about.
|
||||
*
|
||||
* The same is done for <amp-video> elements, for example when they have missing poster images.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @link https://github.com/ampproject/amp-wp/blob/c6aed8f/includes/validation/class-amp-validation-manager.php#L1777-L1809
|
||||
*
|
||||
* @param null|bool $sanitized Whether sanitized. Null means sanitization is not overridden.
|
||||
* @param array{node_type?: int, node_name?: string, parent_name?: string} $error Validation error being sanitized.
|
||||
* @return null|bool Whether sanitized.
|
||||
*/
|
||||
public function filter_amp_validation_error_sanitized( ?bool $sanitized, array $error ): ?bool {
|
||||
// Skip sanitization for missing publisher logos and poster portrait images.
|
||||
if (
|
||||
isset( $error['node_type'], $error['node_name'], $error['parent_name'] ) &&
|
||||
(
|
||||
( XML_ELEMENT_NODE === $error['node_type'] && 'amp-story' === $error['node_name'] && 'body' === $error['parent_name'] ) ||
|
||||
( XML_ATTRIBUTE_NODE === $error['node_type'] && 'poster-portrait-src' === $error['node_name'] && 'amp-story' === $error['parent_name'] ) ||
|
||||
( XML_ATTRIBUTE_NODE === $error['node_type'] && 'publisher-logo-src' === $error['node_name'] && 'amp-story' === $error['parent_name'] )
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip sanitization for missing video posters.
|
||||
if ( isset( $error['node_name'] ) && 'amp-video' === $error['node_name'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip sanitization for amp-video > source with invalid src.
|
||||
if ( isset( $error['parent_name'] ) && 'source' === $error['parent_name'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether AMP-to-AMP is excluded for an element.
|
||||
*
|
||||
* The element may be either a link (`a` or `area`) or a `form`.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param bool|mixed $excluded Excluded. Default value is whether element already has a `noamphtml` link relation or the URL is among `excluded_urls`.
|
||||
* @param string $url URL considered for exclusion.
|
||||
* @param string[] $rel Link relations.
|
||||
* @param DOMElement|null $element The element considered for excluding from AMP-to-AMP linking. May be instance of `a`, `area`, or `form`.
|
||||
* @return bool|mixed Whether AMP-to-AMP is excluded.
|
||||
*/
|
||||
public function filter_amp_to_amp_linking_element_excluded( $excluded, string $url, array $rel, ?DOMElement $element ) {
|
||||
if ( $element instanceof DOMElement && $element->parentNode instanceof DOMElement && 'amp-story-player' === $element->parentNode->tagName ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $excluded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether to skip the post from AMP.
|
||||
*
|
||||
* Skips the post if the AMP plugin's version is lower than what is bundled in this plugin.
|
||||
* Prevents issues where this plugin uses newer features that the plugin doesn't know about yet,
|
||||
* causing false positives with validation.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp/issues/7131
|
||||
*
|
||||
* @param bool|mixed $skipped Whether the post should be skipped from AMP.
|
||||
* @param int $post Post ID.
|
||||
* @return bool|mixed Whether post should be skipped from AMP.
|
||||
*/
|
||||
public function filter_amp_skip_post( $skipped, int $post ) {
|
||||
// This is the opposite to the `AMP__VERSION >= WEBSTORIES_AMP_VERSION` check in the HTML renderer.
|
||||
if (
|
||||
$this->story_post_type->get_slug() === get_post_type( $post )
|
||||
&&
|
||||
\defined( '\AMP__VERSION' )
|
||||
&&
|
||||
version_compare( WEBSTORIES_AMP_VERSION, AMP__VERSION, '>' )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $skipped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post type for the current request.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.NPathComplexity)
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
protected function get_request_post_type(): ?string {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
if ( did_action( 'wp' ) && is_singular() ) {
|
||||
$post_type = get_post_type( get_queried_object_id() );
|
||||
return $post_type ?: null;
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $_GET['action'], $_GET['post'] ) &&
|
||||
'amp_validate' === $_GET['action'] &&
|
||||
is_admin()
|
||||
) {
|
||||
/**
|
||||
* Post ID.
|
||||
*
|
||||
* @var string|int $post_id
|
||||
*/
|
||||
$post_id = $_GET['post'];
|
||||
|
||||
if ( get_post_type( (int) $post_id ) === self::AMP_VALIDATED_URL_POST_TYPE ) {
|
||||
return $this->get_validated_url_post_type( (int) $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
$current_screen_post_type = $this->context->get_screen_post_type();
|
||||
|
||||
if ( $current_screen_post_type ) {
|
||||
$current_post = get_post();
|
||||
|
||||
if ( self::AMP_VALIDATED_URL_POST_TYPE === $current_screen_post_type && $current_post instanceof WP_Post && $current_post->post_type === $current_screen_post_type ) {
|
||||
$validated_url_post_type = $this->get_validated_url_post_type( $current_post->ID );
|
||||
if ( $validated_url_post_type ) {
|
||||
return $validated_url_post_type;
|
||||
}
|
||||
}
|
||||
|
||||
return $current_screen_post_type;
|
||||
}
|
||||
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
/**
|
||||
* Request URI.
|
||||
*
|
||||
* @var string $request_uri
|
||||
*/
|
||||
$request_uri = $_SERVER['REQUEST_URI'];
|
||||
if ( str_contains( (string) wp_unslash( $request_uri ), $this->story_post_type->get_rest_url() ) ) {
|
||||
return $this->story_post_type->get_slug();
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular post type which is the queried object for the given validated URL post.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $post_id Post ID for Validated URL Post.
|
||||
* @return string|null Post type or null if validated URL is not for a singular post.
|
||||
*/
|
||||
protected function get_validated_url_post_type( int $post_id ): ?string {
|
||||
if ( empty( $post_id ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( self::AMP_VALIDATED_URL_POST_TYPE !== $post->post_type ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* AMP queried object.
|
||||
*
|
||||
* @var array{type?: string, id?: int|string}|string $queried_object
|
||||
*/
|
||||
$queried_object = get_post_meta( $post->ID, '_amp_queried_object', true );
|
||||
|
||||
if ( ! \is_array( $queried_object ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( isset( $queried_object['id'], $queried_object['type'] ) && 'post' === $queried_object['type'] ) {
|
||||
/**
|
||||
* Post ID.
|
||||
*
|
||||
* @var int|string $post_id
|
||||
*/
|
||||
$post_id = $queried_object['id'];
|
||||
|
||||
$post_type = get_post_type( (int) $post_id );
|
||||
if ( $post_type ) {
|
||||
return $post_type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Conditional_Featured_Image
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use Google\Web_Stories\Story_Post_Type;
|
||||
|
||||
/**
|
||||
* Class Conditional_Featured_Image.
|
||||
*/
|
||||
class Conditional_Featured_Image extends Service_Base {
|
||||
|
||||
/**
|
||||
* Story_Post_Type instance.
|
||||
*
|
||||
* @var Story_Post_Type Story_Post_Type instance.
|
||||
*/
|
||||
private Story_Post_Type $story_post_type;
|
||||
|
||||
/**
|
||||
* Conditional_Featured_Image constructor.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @param Story_Post_Type $story_post_type Story_Post_Type instance.
|
||||
*/
|
||||
public function __construct( Story_Post_Type $story_post_type ) {
|
||||
$this->story_post_type = $story_post_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'cybocfi_enabled_for_post_type', [ $this, 'cybocfi_enabled_for_post_type' ], 99, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the conditional-featured-image plugin.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @param mixed $enabled If enabled or not.
|
||||
* @param string $post_type Post type slug.
|
||||
* @return mixed Filter value.
|
||||
*/
|
||||
public function cybocfi_enabled_for_post_type( $enabled, string $post_type ) {
|
||||
if ( $this->story_post_type->get_slug() === $post_type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
}
|
||||
}
|
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Core_Themes_Support
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Admin\Customizer;
|
||||
use Google\Web_Stories\Assets;
|
||||
use Google\Web_Stories\Renderer\Stories\Renderer;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use function Google\Web_Stories\render_theme_stories;
|
||||
|
||||
/**
|
||||
* Class Core_Themes_Support.
|
||||
*/
|
||||
class Core_Themes_Support extends Service_Base {
|
||||
|
||||
/**
|
||||
* Default array of core themes to add support to.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static array $supported_themes = [
|
||||
'twentytwentyone',
|
||||
'twentytwenty',
|
||||
'twentynineteen',
|
||||
'twentyseventeen',
|
||||
'twentysixteen',
|
||||
'twentyfifteen',
|
||||
'twentyfourteen',
|
||||
'twentythirteen',
|
||||
'twentytwelve',
|
||||
'twentyeleven',
|
||||
'twentyten',
|
||||
];
|
||||
|
||||
/**
|
||||
* Assets instance.
|
||||
*
|
||||
* @var Assets Assets instance.
|
||||
*/
|
||||
private Assets $assets;
|
||||
|
||||
/**
|
||||
* Core theme supports constructor.
|
||||
*
|
||||
* @since 1.8.0
|
||||
*
|
||||
* @param Assets $assets Assets instance.
|
||||
*/
|
||||
public function __construct( Assets $assets ) {
|
||||
$this->assets = $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds theme support for Web Stories.
|
||||
*
|
||||
* This will enable add_theme_support with predefined
|
||||
* options supported themes.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public function extend_theme_support(): void {
|
||||
add_theme_support( 'web-stories' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed Webstories.
|
||||
*
|
||||
* Embeds web stories with default customizer settings.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public function embed_web_stories(): void {
|
||||
$stylesheet = get_stylesheet();
|
||||
if ( is_readable( sprintf( '%sassets/css/web-stories-theme-style-%s.css', WEBSTORIES_PLUGIN_DIR_PATH, $stylesheet ) ) ) {
|
||||
$this->assets->enqueue_style_asset( 'web-stories-theme-style-' . $stylesheet, [ Renderer::STYLE_HANDLE ] );
|
||||
}
|
||||
?>
|
||||
<div class="web-stories-theme-header-section">
|
||||
<?php render_theme_stories(); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class if it is one of supported core themes.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param array|mixed $classes Array of body classes.
|
||||
* @return array|mixed Updated array of classes.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($classes is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function add_core_theme_classes( $classes ) {
|
||||
if ( ! \is_array( $classes ) ) {
|
||||
return $classes;
|
||||
}
|
||||
$classes[] = 'has-web-stories';
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds theme support and hook to embed the web stories.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public function register(): void {
|
||||
|
||||
if ( ! \in_array( get_stylesheet(), self::$supported_themes, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->extend_theme_support();
|
||||
|
||||
/**
|
||||
* Customizer options.
|
||||
*
|
||||
* @var array<string, mixed> $options
|
||||
*/
|
||||
$options = get_option( Customizer::STORY_OPTION, [] );
|
||||
|
||||
// Load theme specific styles and render function only if selected to show stories.
|
||||
if ( empty( $options['show_stories'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'body_class', [ $this, 'add_core_theme_classes' ] );
|
||||
add_action( 'wp_body_open', [ $this, 'embed_web_stories' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action to use for registering the service.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @return string Registration action to use.
|
||||
*/
|
||||
public static function get_registration_action(): string {
|
||||
return 'after_setup_theme';
|
||||
}
|
||||
}
|
144
wp-content/plugins/web-stories/includes/Integrations/Ezoic.php
Normal file
144
wp-content/plugins/web-stories/includes/Integrations/Ezoic.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Ezoic
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\AMP\Optimization;
|
||||
use Google\Web_Stories\AMP\Sanitization;
|
||||
use Google\Web_Stories\Context;
|
||||
use Google\Web_Stories\Exception\SanitizationException;
|
||||
use Google\Web_Stories\Infrastructure\Conditional;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use Google\Web_Stories_Dependencies\AmpProject\Dom\Document;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Ezoic.
|
||||
*/
|
||||
class Ezoic extends Service_Base implements Conditional {
|
||||
/**
|
||||
* Sanitization instance.
|
||||
*
|
||||
* @var Sanitization Sanitization instance.
|
||||
*/
|
||||
private Sanitization $sanitization;
|
||||
|
||||
/**
|
||||
* Optimization instance.
|
||||
*
|
||||
* @var Optimization Optimization instance.
|
||||
*/
|
||||
private Optimization $optimization;
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @var Context Context instance.
|
||||
*/
|
||||
private Context $context;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.33.0
|
||||
*
|
||||
* @param Sanitization $sanitization Sanitization instance.
|
||||
* @param Optimization $optimization Optimization instance.
|
||||
* @param Context $context Context instance.
|
||||
*/
|
||||
public function __construct( Sanitization $sanitization, Optimization $optimization, Context $context ) {
|
||||
$this->sanitization = $sanitization;
|
||||
$this->optimization = $optimization;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.33.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'ez_buffered_final_content', [ $this, 'process_ez_buffered_final_content' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the Ezoic integration is currently needed.
|
||||
*
|
||||
* @since 1.33.0
|
||||
*
|
||||
* @return bool Whether Ezoic integration is currently needed.
|
||||
*/
|
||||
public static function is_needed(): bool {
|
||||
return \defined( 'EZOIC_INTEGRATION_VERSION' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes and Sanitizes Ezoic's final prepared content.
|
||||
*
|
||||
* @since 1.33.0
|
||||
*
|
||||
* @param string $content HTML document response collected by Ezoic Output Buffer.
|
||||
* @return string AMP document response.
|
||||
*/
|
||||
public function process_ez_buffered_final_content( string $content ): string {
|
||||
if ( $this->context->is_web_story() ) {
|
||||
// Enforce UTF-8 encoding as it is a requirement for AMP.
|
||||
if ( ! headers_sent() ) {
|
||||
header( 'Content-Type: text/html; charset=utf-8' );
|
||||
}
|
||||
|
||||
$dom = Document::fromHtml( $content );
|
||||
|
||||
if ( ! $dom instanceof Document ) {
|
||||
return $this->render_error_page( SanitizationException::from_document_parse_error() );
|
||||
}
|
||||
|
||||
$this->sanitization->sanitize_document( $dom );
|
||||
$this->optimization->optimize_document( $dom );
|
||||
|
||||
return $dom->saveHTML();
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render error page.
|
||||
*
|
||||
* @since 1.33.0
|
||||
*
|
||||
* @param Throwable $throwable Exception or (as of PHP7) Error.
|
||||
* @return string Error page.
|
||||
*/
|
||||
private function render_error_page( Throwable $throwable ): string {
|
||||
return esc_html__( 'There was an error generating the web story, probably because of a server misconfiguration. Try contacting your hosting provider or open a new support request.', 'web-stories' ) .
|
||||
"\n" .
|
||||
"\n" .
|
||||
// translators: 1: error message. 2: location.
|
||||
sprintf( esc_html__( 'Error message: %1$s (%2$s)', 'web-stories' ), $throwable->getMessage(), $throwable->getFile() . ':' . $throwable->getLine() );
|
||||
}
|
||||
}
|
396
wp-content/plugins/web-stories/includes/Integrations/Jetpack.php
Normal file
396
wp-content/plugins/web-stories/includes/Integrations/Jetpack.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Jetpack
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Context;
|
||||
use Google\Web_Stories\Media\Media_Source_Taxonomy;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use Google\Web_Stories\Story_Post_Type;
|
||||
use WP_Post;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class Jetpack.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
|
||||
*
|
||||
* @phpstan-type AttachmentData array{
|
||||
* media_details?: array{
|
||||
* length?: int,
|
||||
* length_formatted?: string
|
||||
* },
|
||||
* url?: string,
|
||||
* featured_media_src?: string
|
||||
* }
|
||||
*
|
||||
* @phpstan-type EnhancedAttachmentMetadata array{
|
||||
* width: int,
|
||||
* height: int,
|
||||
* file: string,
|
||||
* sizes: mixed,
|
||||
* image_meta: mixed,
|
||||
* videopress?: array{
|
||||
* duration: int,
|
||||
* poster: string,
|
||||
* width: int,
|
||||
* height: int,
|
||||
* file_url_base?: array{
|
||||
* https: string
|
||||
* },
|
||||
* files?: array{
|
||||
* hd?: array{
|
||||
* mp4?: string
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
class Jetpack extends Service_Base {
|
||||
|
||||
/**
|
||||
* VideoPress Mime type.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*/
|
||||
public const VIDEOPRESS_MIME_TYPE = 'video/videopress';
|
||||
|
||||
/**
|
||||
* VideoPress poster meta key.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*/
|
||||
public const VIDEOPRESS_POSTER_META_KEY = 'videopress_poster_image';
|
||||
|
||||
/**
|
||||
* Media_Source_Taxonomy instance.
|
||||
*
|
||||
* @var Media_Source_Taxonomy Experiments instance.
|
||||
*/
|
||||
protected Media_Source_Taxonomy $media_source_taxonomy;
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @var Context Context instance.
|
||||
*/
|
||||
private Context $context;
|
||||
|
||||
/**
|
||||
* Jetpack constructor.
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @param Media_Source_Taxonomy $media_source_taxonomy Media_Source_Taxonomy instance.
|
||||
* @param Context $context Context instance.
|
||||
*/
|
||||
public function __construct( Media_Source_Taxonomy $media_source_taxonomy, Context $context ) {
|
||||
$this->media_source_taxonomy = $media_source_taxonomy;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register(): void {
|
||||
// See https://github.com/Automattic/jetpack/blob/4b85be883b3c584c64eeb2fb0f3fcc15dabe2d30/modules/custom-post-types/portfolios.php#L80.
|
||||
if ( \defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
||||
add_filter( 'wpcom_sitemap_post_types', [ $this, 'add_to_jetpack_sitemap' ] );
|
||||
} else {
|
||||
add_filter( 'jetpack_sitemap_post_types', [ $this, 'add_to_jetpack_sitemap' ] );
|
||||
}
|
||||
|
||||
add_filter( 'jetpack_is_amp_request', [ $this, 'force_amp_request' ] );
|
||||
add_filter( 'web_stories_allowed_mime_types', [ $this, 'add_videopress' ] );
|
||||
add_filter( 'web_stories_rest_prepare_attachment', [ $this, 'filter_rest_api_response' ], 10, 2 );
|
||||
add_filter( 'ajax_query_attachments_args', [ $this, 'filter_ajax_query_attachments_args' ] );
|
||||
add_action( 'added_post_meta', [ $this, 'add_term' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the web-story post type to Jetpack / WordPress.com sitemaps.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @see https://github.com/Automattic/jetpack/blob/4b85be883b3c584c64eeb2fb0f3fcc15dabe2d30/modules/custom-post-types/portfolios.php#L80
|
||||
*
|
||||
* @param array|mixed $post_types Array of post types.
|
||||
* @return array|mixed Modified list of post types.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($post_types is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function add_to_jetpack_sitemap( $post_types ) {
|
||||
if ( ! \is_array( $post_types ) ) {
|
||||
return $post_types;
|
||||
}
|
||||
$post_types[] = Story_Post_Type::POST_TYPE_SLUG;
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add VideoPress to allowed mime types.
|
||||
*
|
||||
* If the site does not support VideoPress, this will be filtered out.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param array{video?: string[]}|mixed $mime_types Associative array of allowed mime types per media type (image, audio, video).
|
||||
* @return array{video?: string[]}|mixed
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($mime_types is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function add_videopress( $mime_types ) {
|
||||
if ( ! \is_array( $mime_types ) ) {
|
||||
return $mime_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mime types config.
|
||||
*
|
||||
* @var array{video?: string[]} $mime_types
|
||||
*/
|
||||
$mime_types['video'][] = self::VIDEOPRESS_MIME_TYPE;
|
||||
|
||||
return $mime_types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter ajax query attachments args when accessed from the Web Stories editor.
|
||||
*
|
||||
* Only filters the response if the mime type matches exactly what Web Stories is looking for.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param array|mixed $args Query args.
|
||||
* @return array|mixed Filtered query args.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($args is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function filter_ajax_query_attachments_args( $args ) {
|
||||
if ( ! \is_array( $args ) || ! isset( $args['post_mime_type'] ) || ! \is_array( $args['post_mime_type'] ) ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
if ( \in_array( self::VIDEOPRESS_MIME_TYPE, $args['post_mime_type'], true ) ) {
|
||||
add_filter( 'wp_prepare_attachment_for_js', [ $this, 'filter_admin_ajax_response' ], 15, 2 );
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter admin ajax responses for VideoPress videos.
|
||||
*
|
||||
* Changes the video/videopress type back to mp4
|
||||
* and ensures MP4 source URLs are returned.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param array|mixed $data Array of prepared attachment data. @see wp_prepare_attachment_for_js().
|
||||
* @param WP_Post $attachment Attachment object.
|
||||
* @return array|mixed
|
||||
*
|
||||
* @phpstan-param AttachmentData $data
|
||||
* @phpstan-return AttachmentData|mixed
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @phpstan-return ($data is array<T> ? array<T> : mixed)
|
||||
*/
|
||||
public function filter_admin_ajax_response( $data, WP_Post $attachment ) {
|
||||
if ( self::VIDEOPRESS_MIME_TYPE !== $attachment->post_mime_type ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! \is_array( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Reset mime type back to mp4, as this is the correct value.
|
||||
$data['mime'] = 'video/mp4';
|
||||
$data['subtype'] = 'mp4';
|
||||
|
||||
// Mark video as optimized.
|
||||
$data[ $this->media_source_taxonomy::MEDIA_SOURCE_KEY ] = 'video-optimization';
|
||||
|
||||
/**
|
||||
* Jetpack adds an additional field to regular attachment metadata.
|
||||
*
|
||||
* @var array $metadata
|
||||
* @phpstan-var EnhancedAttachmentMetadata|false $metadata
|
||||
*/
|
||||
$metadata = wp_get_attachment_metadata( $attachment->ID );
|
||||
|
||||
if ( $metadata && isset( $metadata['videopress']['duration'], $data['media_details'] ) && \is_array( $data['media_details'] ) ) {
|
||||
$data['media_details']['length_formatted'] = $this->format_milliseconds( $metadata['videopress']['duration'] );
|
||||
$data['media_details']['length'] = (int) floor( $metadata['videopress']['duration'] / 1000 );
|
||||
}
|
||||
|
||||
if ( $metadata && isset( $data['url'], $metadata['videopress']['file_url_base']['https'], $metadata['videopress']['files']['hd']['mp4'] ) ) {
|
||||
$data['url'] = $metadata['videopress']['file_url_base']['https'] . $metadata['videopress']['files']['hd']['mp4'];
|
||||
}
|
||||
|
||||
// Get the correct poster with matching dimensions from VideoPress.
|
||||
if ( $metadata && isset( $data['featured_media_src'], $metadata['videopress']['poster'], $metadata['videopress']['width'], $metadata['videopress']['height'] ) ) {
|
||||
$data['featured_media_src'] = [
|
||||
'src' => $metadata['videopress']['poster'],
|
||||
'width' => $metadata['videopress']['width'],
|
||||
'height' => $metadata['videopress']['height'],
|
||||
'generated' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter REST API responses for VideoPress videos.
|
||||
*
|
||||
* Changes the video/videopress type back to mp4
|
||||
* and ensures MP4 source URLs are returned.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post $post The original attachment post.
|
||||
*/
|
||||
public function filter_rest_api_response( WP_REST_Response $response, WP_Post $post ): WP_REST_Response {
|
||||
if ( self::VIDEOPRESS_MIME_TYPE !== $post->post_mime_type ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response data.
|
||||
*
|
||||
* @var array<string, string|array<string, int|string>|bool> $data
|
||||
*/
|
||||
$data = $response->get_data();
|
||||
|
||||
// Reset mime type back to mp4, as this is the correct value.
|
||||
$data['mime_type'] = 'video/mp4';
|
||||
|
||||
// Mark video as optimized.
|
||||
$data[ $this->media_source_taxonomy::MEDIA_SOURCE_KEY ] = 'video-optimization';
|
||||
|
||||
/**
|
||||
* Jetpack adds an additional field to regular attachment metadata.
|
||||
*
|
||||
* @var EnhancedAttachmentMetadata|false $metadata
|
||||
*/
|
||||
$metadata = wp_get_attachment_metadata( $post->ID );
|
||||
|
||||
if ( $metadata && isset( $metadata['videopress']['duration'], $data['media_details'] ) && \is_array( $data['media_details'] ) ) {
|
||||
$data['media_details']['length_formatted'] = $this->format_milliseconds( $metadata['videopress']['duration'] );
|
||||
$data['media_details']['length'] = (int) floor( $metadata['videopress']['duration'] / 1000 );
|
||||
}
|
||||
|
||||
if ( $metadata && isset( $data['source_url'], $metadata['videopress']['file_url_base']['https'], $metadata['videopress']['files']['hd']['mp4'] ) ) {
|
||||
$data['source_url'] = $metadata['videopress']['file_url_base']['https'] . $metadata['videopress']['files']['hd']['mp4'];
|
||||
}
|
||||
|
||||
// Get the correct poster with matching dimensions from VideoPress.
|
||||
if ( $metadata && isset( $data['featured_media_src'], $metadata['videopress']['poster'], $metadata['videopress']['width'], $metadata['videopress']['height'] ) ) {
|
||||
$data['featured_media_src'] = [
|
||||
'src' => $metadata['videopress']['poster'],
|
||||
'width' => $metadata['videopress']['width'],
|
||||
'height' => $metadata['videopress']['height'],
|
||||
'generated' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$response->set_data( $data );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into added_post_meta.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param int $mid The meta ID after successful update.
|
||||
* @param int $object_id ID of the object metadata is for.
|
||||
* @param string $meta_key Metadata key.
|
||||
*/
|
||||
public function add_term( int $mid, int $object_id, string $meta_key ): void {
|
||||
if ( self::VIDEOPRESS_POSTER_META_KEY !== $meta_key ) {
|
||||
return;
|
||||
}
|
||||
if ( 'attachment' !== get_post_type( $object_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_set_object_terms( (int) $object_id, $this->media_source_taxonomy::TERM_POSTER_GENERATION, $this->media_source_taxonomy->get_taxonomy_slug() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Force Jetpack to see Web Stories as AMP.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param bool $is_amp_request Is the request supposed to return valid AMP content.
|
||||
* @return bool Whether the current request is an AMP request.
|
||||
*/
|
||||
public function force_amp_request( bool $is_amp_request ): bool {
|
||||
if ( ! $this->context->is_web_story() ) {
|
||||
return (bool) $is_amp_request;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format milliseconds into seconds.
|
||||
*
|
||||
* @since 1.7.2
|
||||
*
|
||||
* @param int $milliseconds Milliseconds to converted to minutes and seconds.
|
||||
*/
|
||||
protected function format_milliseconds( int $milliseconds ): string {
|
||||
$seconds = floor( $milliseconds / 1000 );
|
||||
|
||||
if ( $seconds >= 1 ) {
|
||||
$minutes = floor( $seconds / 60 );
|
||||
$seconds %= 60;
|
||||
} else {
|
||||
$seconds = 0;
|
||||
$minutes = 0;
|
||||
}
|
||||
|
||||
return sprintf( '%d:%02u', $minutes, $seconds );
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* Class New_Relic
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Context;
|
||||
use Google\Web_Stories\Infrastructure\Conditional;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
|
||||
/**
|
||||
* New Relic integration class.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
class New_Relic extends Service_Base implements Conditional {
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @var Context Context instance.
|
||||
*/
|
||||
private Context $context;
|
||||
|
||||
/**
|
||||
* Single constructor.
|
||||
*
|
||||
* @param Context $context Context instance.
|
||||
*/
|
||||
public function __construct( Context $context ) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on instantiation.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public function register(): void {
|
||||
$this->disable_autorum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action to use for registering the service.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @return string Registration action to use.
|
||||
*/
|
||||
public static function get_registration_action(): string {
|
||||
return 'template_redirect';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action priority to use for registering the service.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @return int Registration action priority to use.
|
||||
*/
|
||||
public static function get_registration_action_priority(): int {
|
||||
// Run at the same time as the output buffering.
|
||||
return PHP_INT_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the conditional object is currently needed.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @return bool Whether the conditional object is needed.
|
||||
*/
|
||||
public static function is_needed(): bool {
|
||||
return \function_exists( '\newrelic_disable_autorum' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the New Relic Browser agent on AMP responses.
|
||||
*
|
||||
* This prevents the New Relic from causing invalid AMP responses due the NREUM script it injects after the meta charset:
|
||||
*
|
||||
* https://docs.newrelic.com/docs/browser/new-relic-browser/troubleshooting/google-amp-validator-fails-due-3rd-party-script
|
||||
*
|
||||
* Sites with New Relic will need to specially configure New Relic for AMP:
|
||||
* https://docs.newrelic.com/docs/browser/new-relic-browser/installation/monitor-amp-pages-new-relic-browser
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public function disable_autorum(): void {
|
||||
if ( ! $this->context->is_web_story() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
\newrelic_disable_autorum();
|
||||
}
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Class NextGen_Gallery
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Service_Base;
|
||||
use Google\Web_Stories\Story_Post_Type;
|
||||
|
||||
/**
|
||||
* Class NextGen_Gallery.
|
||||
*/
|
||||
class NextGen_Gallery extends Service_Base {
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'run_ngg_resource_manager', [ $this, 'filter_run_ngg_resource_manager' ], PHP_INT_MAX );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action priority to use for registering the service.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @return int Registration action priority to use.
|
||||
*/
|
||||
public static function get_registration_action_priority(): int {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters NextGEN Gallery's resource manager behavior.
|
||||
*
|
||||
* Disables output buffering for Web Stories.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @see https://github.com/imagely/nextgen-gallery/blob/9736cc05e63b6b4cceb10b8a9a1de276f5c1ad4b/non_pope/class.photocrati_resource_manager.php
|
||||
*
|
||||
* @param bool|mixed $valid_request Whether NextGEN Gallery's output buffer should run.
|
||||
* @return bool|mixed Whether the output buffer should run.
|
||||
*/
|
||||
public function filter_run_ngg_resource_manager( $valid_request ) {
|
||||
$request_uri_path = $this->get_request_uri_path();
|
||||
|
||||
if (
|
||||
// Plain permalinks.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
! empty( $_GET[ Story_Post_Type::POST_TYPE_SLUG ] ) ||
|
||||
// Pretty permalinks.
|
||||
(
|
||||
$request_uri_path &&
|
||||
preg_match(
|
||||
'#/' . preg_quote( Story_Post_Type::REWRITE_SLUG, '#' ) . '/.*?$#',
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$request_uri_path
|
||||
)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $valid_request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current request path.
|
||||
*
|
||||
* @since 1.15.0
|
||||
*
|
||||
* @return string|null Request URI path on success, null on failure.
|
||||
*/
|
||||
private function get_request_uri_path(): ?string {
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! \is_string( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request URI.
|
||||
*
|
||||
* @var string $request_uri
|
||||
*/
|
||||
$request_uri = $_SERVER['REQUEST_URI'];
|
||||
|
||||
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
/**
|
||||
* Request URI path.
|
||||
*
|
||||
* @var string|null|false $path
|
||||
*/
|
||||
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
|
||||
|
||||
if ( ! $path ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Plugin_Status
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
/**
|
||||
* Class Plugin_Status.
|
||||
*/
|
||||
class Plugin_Status {
|
||||
/**
|
||||
* Array of arrays of plugin data, keyed by plugin file name.
|
||||
*
|
||||
* @see get_plugin_data()
|
||||
*
|
||||
* @var array<string, array<string, string|bool>>
|
||||
*/
|
||||
protected array $plugins = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! \function_exists( '\get_plugins' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
$this->plugins = get_plugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all plugin files with plugin data.
|
||||
*
|
||||
* @since 1.30.0
|
||||
*
|
||||
* @return array<string, array<string, string|bool>>
|
||||
*/
|
||||
public function get_plugins(): array {
|
||||
return $this->plugins;
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* Class ShortPixel.
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Service_Base;
|
||||
|
||||
/**
|
||||
* Class ShortPixel
|
||||
*/
|
||||
class ShortPixel extends Service_Base {
|
||||
|
||||
/**
|
||||
* Runs on instantiation.
|
||||
*
|
||||
* @since 1.23.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'shortpixel_image_urls', [ $this, 'image_urls' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures page template urls bypass optimisation.
|
||||
*
|
||||
* @since 1.23.0
|
||||
*
|
||||
* @param string[] $urls Urls that will be sent to optimisation.
|
||||
* @return string[] The filtered Urls.
|
||||
*/
|
||||
public function image_urls( array $urls ): array {
|
||||
return array_filter(
|
||||
$urls,
|
||||
static fn( $url ) => ! str_contains( $url, 'web-stories-page-template' )
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Site_Kit
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2020 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
use Google\Web_Stories\Analytics;
|
||||
use Google\Web_Stories\Context;
|
||||
use Google\Web_Stories\Service_Base;
|
||||
|
||||
/**
|
||||
* Class Site_Kit.
|
||||
*
|
||||
* @phpstan-type GtagOpt array{
|
||||
* vars: array{
|
||||
* gtag_id?: string
|
||||
* },
|
||||
* triggers?: array<string, mixed>
|
||||
* }
|
||||
*/
|
||||
class Site_Kit extends Service_Base {
|
||||
/**
|
||||
* Analytics instance.
|
||||
*
|
||||
* @var Analytics Analytics instance.
|
||||
*/
|
||||
protected Analytics $analytics;
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @var Context Context instance.
|
||||
*/
|
||||
private Context $context;
|
||||
|
||||
/**
|
||||
* Plugin_Status instance.
|
||||
*
|
||||
* @var Plugin_Status Plugin_Status instance.
|
||||
*/
|
||||
private Plugin_Status $plugin_status;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Analytics $analytics Analytics instance.
|
||||
* @param Context $context Context instance.
|
||||
* @param Plugin_Status $plugin_status Plugin_Status instance.
|
||||
*/
|
||||
public function __construct( Analytics $analytics, Context $context, Plugin_Status $plugin_status ) {
|
||||
$this->analytics = $analytics;
|
||||
$this->context = $context;
|
||||
$this->plugin_status = $plugin_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all hooks.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register(): void {
|
||||
add_filter( 'googlesitekit_amp_gtag_opt', [ $this, 'filter_site_kit_gtag_opt' ] );
|
||||
|
||||
if ( $this->is_analytics_module_active() ) {
|
||||
remove_action( 'web_stories_print_analytics', [ $this->analytics, 'print_analytics_tag' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters Site Kit's Google Analytics configuration.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param array|mixed $gtag_opt Array of gtag configuration options.
|
||||
* @return array|mixed Modified configuration options.
|
||||
*
|
||||
* @phpstan-param GtagOpt $gtag_opt
|
||||
*/
|
||||
public function filter_site_kit_gtag_opt( $gtag_opt ) {
|
||||
if (
|
||||
! \is_array( $gtag_opt ) ||
|
||||
! isset( $gtag_opt['vars']['gtag_id'] ) ||
|
||||
! $this->context->is_web_story()
|
||||
) {
|
||||
return $gtag_opt;
|
||||
}
|
||||
|
||||
$default_config = $this->analytics->get_default_configuration( $gtag_opt['vars']['gtag_id'] );
|
||||
$default_config['triggers'] = $default_config['triggers'] ?? [];
|
||||
|
||||
$gtag_opt['triggers'] ??= [];
|
||||
$gtag_opt['triggers'] = array_merge( $default_config['triggers'], $gtag_opt['triggers'] );
|
||||
|
||||
return $gtag_opt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Site Kit plugin status.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return array{installed: bool, active: bool, analyticsActive: bool, adsenseActive: bool, analyticsLink: string, adsenseLink: string} Plugin status.
|
||||
*/
|
||||
public function get_plugin_status(): array {
|
||||
$is_installed = \array_key_exists( 'google-site-kit/google-site-kit.php', $this->plugin_status->get_plugins() );
|
||||
$is_active = $this->is_plugin_active();
|
||||
$is_analytics_active = $this->is_analytics_module_active();
|
||||
$is_adsense_active = $this->is_adsense_module_active();
|
||||
|
||||
$analytics_link = __( 'https://wordpress.org/plugins/google-site-kit/', 'web-stories' );
|
||||
$adsense_link = __( 'https://wordpress.org/plugins/google-site-kit/', 'web-stories' );
|
||||
$dashboard = admin_url( 'admin.php?page=googlesitekit-dashboard' );
|
||||
$settings = admin_url( 'admin.php?page=googlesitekit-settings' );
|
||||
|
||||
if ( $is_active ) {
|
||||
$dashboard_capability = current_user_can( 'googlesitekit_view_dashboard' ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
|
||||
$settings_capability = current_user_can( 'googlesitekit_manage_options' ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
|
||||
|
||||
// If analytics is active and current user can view dashboard.
|
||||
if ( $is_analytics_active && $dashboard_capability ) {
|
||||
$analytics_link = $dashboard;
|
||||
} elseif ( $settings_capability ) {
|
||||
$analytics_link = $settings;
|
||||
} elseif ( $dashboard_capability ) {
|
||||
$analytics_link = $dashboard;
|
||||
}
|
||||
|
||||
// If adsense is active and current user can view dashboard.
|
||||
if ( $is_adsense_active && $dashboard_capability ) {
|
||||
$adsense_link = $dashboard;
|
||||
} elseif ( $settings_capability ) {
|
||||
$adsense_link = $settings;
|
||||
} elseif ( $dashboard_capability ) {
|
||||
$adsense_link = $dashboard;
|
||||
}
|
||||
} elseif ( $is_installed ) {
|
||||
if ( current_user_can( 'activate_plugin', 'google-site-kit/google-site-kit.php' ) ) {
|
||||
$analytics_link = admin_url( 'plugins.php' );
|
||||
$adsense_link = $analytics_link;
|
||||
}
|
||||
} elseif ( current_user_can( 'install_plugins' ) ) {
|
||||
$analytics_link = admin_url(
|
||||
add_query_arg(
|
||||
[
|
||||
's' => rawurlencode( __( 'Site Kit by Google', 'web-stories' ) ),
|
||||
'tab' => 'search',
|
||||
],
|
||||
'plugin-install.php'
|
||||
)
|
||||
);
|
||||
$adsense_link = $analytics_link;
|
||||
}
|
||||
|
||||
return [
|
||||
'installed' => $is_active || $is_installed,
|
||||
'active' => $is_active,
|
||||
'analyticsActive' => $is_analytics_active,
|
||||
'adsenseActive' => $is_adsense_active,
|
||||
'analyticsLink' => $analytics_link,
|
||||
'adsenseLink' => $adsense_link,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether Site Kit is active.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return bool Whether Site Kit is active.
|
||||
*/
|
||||
protected function is_plugin_active(): bool {
|
||||
return \defined( 'GOOGLESITEKIT_VERSION' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the built-in adsense module in Site Kit is active.
|
||||
*
|
||||
* @since 1.8.0
|
||||
*
|
||||
* @return bool Whether Site Kit's analytics module is active.
|
||||
*/
|
||||
protected function is_adsense_module_active(): bool {
|
||||
$adsense_module_active = \in_array( 'adsense', $this->get_site_kit_active_modules_option(), true );
|
||||
$adsense_options = (array) get_option( 'googlesitekit_adsense_settings' );
|
||||
$adsense_options_client_id = ! empty( $adsense_options['clientID'] );
|
||||
$adsense_options_use_snippet = ! empty( $adsense_options['useSnippet'] );
|
||||
$adsense_web_stories_ad_unit = ! empty( $adsense_options['webStoriesAdUnit'] );
|
||||
|
||||
return $adsense_module_active && $adsense_options_use_snippet && $adsense_web_stories_ad_unit && $adsense_options_client_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the built-in Analytics module in Site Kit is active.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return bool Whether Site Kit's analytics module is active.
|
||||
*/
|
||||
protected function is_analytics_module_active(): bool {
|
||||
$analytics_module_active = \in_array( 'analytics', $this->get_site_kit_active_modules_option(), true );
|
||||
$analytics_options = (array) get_option( 'googlesitekit_analytics_settings' );
|
||||
$analytics_use_snippet = ! empty( $analytics_options['useSnippet'] );
|
||||
|
||||
return $analytics_module_active && $analytics_use_snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option containing the active Site Kit modules.
|
||||
*
|
||||
* Checks two options as it was renamed at some point in Site Kit.
|
||||
*
|
||||
* Bails early if the Site Kit plugin itself is not active.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @see \Google\Site_Kit\Core\Modules\Modules::get_active_modules_option
|
||||
*
|
||||
* @return string[] List of active module slugs.
|
||||
*/
|
||||
protected function get_site_kit_active_modules_option(): array {
|
||||
if ( ! $this->is_plugin_active() ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string[]|false $option
|
||||
*/
|
||||
$option = get_option( 'googlesitekit_active_modules' );
|
||||
|
||||
if ( \is_array( $option ) ) {
|
||||
return $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy option value.
|
||||
*
|
||||
* @var string[]|false $legacy_option
|
||||
*/
|
||||
$legacy_option = get_option( 'googlesitekit-active-modules' );
|
||||
|
||||
if ( \is_array( $legacy_option ) ) {
|
||||
return $legacy_option;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerce
|
||||
*
|
||||
* @link https://github.com/googleforcreators/web-stories-wp
|
||||
*
|
||||
* @copyright 2022 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Google\Web_Stories\Integrations;
|
||||
|
||||
/**
|
||||
* Class WooCommerce.
|
||||
*/
|
||||
class WooCommerce {
|
||||
/**
|
||||
* Main plugin file.
|
||||
*/
|
||||
protected const PLUGIN = 'woocommerce/woocommerce.php';
|
||||
|
||||
/**
|
||||
* Plugin_Status instance.
|
||||
*
|
||||
* @var Plugin_Status Plugin_Status instance.
|
||||
*/
|
||||
private Plugin_Status $plugin_status;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Plugin_Status $plugin_status Plugin_Status instance.
|
||||
*/
|
||||
public function __construct( Plugin_Status $plugin_status ) {
|
||||
$this->plugin_status = $plugin_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WooCommerce plugin status.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @return array{installed: bool, active: bool, canManage: bool, link: string} Plugin status.
|
||||
*/
|
||||
public function get_plugin_status(): array {
|
||||
$is_installed = \array_key_exists( self::PLUGIN, $this->plugin_status->get_plugins() );
|
||||
$is_active = $this->is_plugin_active();
|
||||
$can_manage = false;
|
||||
$link = '';
|
||||
|
||||
if ( $is_active ) {
|
||||
$can_manage = current_user_can( 'manage_woocommerce' ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
|
||||
if ( $can_manage ) {
|
||||
$link = admin_url( 'admin.php?page=wc-admin' );
|
||||
}
|
||||
} elseif ( $is_installed ) {
|
||||
if ( current_user_can( 'activate_plugin', self::PLUGIN ) ) {
|
||||
$link = admin_url( 'plugins.php' );
|
||||
}
|
||||
} elseif ( current_user_can( 'install_plugins' ) ) {
|
||||
$link = admin_url(
|
||||
add_query_arg(
|
||||
[
|
||||
's' => rawurlencode( __( 'WooCommerce', 'web-stories' ) ),
|
||||
'tab' => 'search',
|
||||
],
|
||||
'plugin-install.php'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$link = __( 'https://wordpress.org/plugins/woocommerce/', 'web-stories' );
|
||||
}
|
||||
|
||||
return [
|
||||
'installed' => $is_active || $is_installed,
|
||||
'active' => $is_active,
|
||||
'canManage' => $can_manage,
|
||||
'link' => $link,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether WooCommerce is active.
|
||||
*
|
||||
* @since 1.21.0
|
||||
*
|
||||
* @return bool Whether WooCommerce is active.
|
||||
*/
|
||||
protected function is_plugin_active(): bool {
|
||||
return class_exists( 'WooCommerce', false );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user