Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
<?php
/**
* Carousel_Renderer class.
*
* @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\Renderer\Stories;
/**
* Carousel_Renderer class.
*
* Note: This class is useful to render stories in carousel view type.
* Do not instantiate this class directly, pass `view_type` argument
* to `Story_Query` which will handle the instantiation of the class.
*/
class Carousel_Renderer extends Renderer {
/**
* Script handle.
*/
public const SCRIPT_HANDLE = 'web-stories-carousel';
/**
* Perform initial setup for object.
*
* @since 1.5.0
*
* @return void
*/
public function init(): void {
parent::init();
$this->load_assets();
}
/**
* Enqueue assets.
*
* @since 1.5.0
*
* @return void
*/
public function load_assets(): void {
parent::load_assets();
$this->assets->register_script_asset( self::SCRIPT_HANDLE );
$this->assets->register_style_asset( self::SCRIPT_HANDLE );
wp_localize_script(
self::SCRIPT_HANDLE,
'webStoriesCarouselSettings',
$this->get_carousel_settings()
);
}
/**
* Renders the stories output for given attributes.
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*
* @since 1.5.0
*
* @param array<string,mixed> $args Array of rendering arguments.
* @return string Rendered stories output.
*/
public function render( array $args = [] ): string {
if ( ! $this->valid() ) {
return '';
}
parent::render( $args );
$container_classes = $this->get_container_classes();
$container_styles = $this->get_container_styles();
ob_start();
?>
<div class="<?php echo esc_attr( $container_classes ); ?>" data-id="<?php echo esc_attr( (string) $this->instance_id ); ?>">
<div class="web-stories-list__inner-wrapper <?php echo esc_attr( 'carousel-' . $this->instance_id ); ?>" style="<?php echo esc_attr( $container_styles ); ?>">
<?php
if ( ! $this->context->is_amp() ) {
$this->assets->enqueue_script_asset( self::SCRIPT_HANDLE );
$this->assets->enqueue_style_asset( self::SCRIPT_HANDLE );
?>
<div class="web-stories-list__carousel <?php echo esc_attr( $this->get_view_type() ); ?>" data-id="<?php echo esc_attr( 'carousel-' . $this->instance_id ); ?>">
<?php
array_map(
function () {
$this->render_single_story_content();
$this->next();
},
$this->stories
);
?>
</div>
<div tabindex="0" aria-label="<?php esc_attr_e( 'Previous', 'web-stories' ); ?>" class="glider-prev"></div>
<div tabindex="0" aria-label="<?php esc_attr_e( 'Next', 'web-stories' ); ?>" class="glider-next"></div>
<?php
} else {
?>
<amp-carousel
width="1"
height="1"
layout="intrinsic"
type="carousel"
role="region"
aria-label="<?php esc_attr_e( 'Web Stories', 'web-stories' ); ?>"
>
<?php
array_map(
function () {
$this->render_single_story_content();
$this->next();
},
$this->stories
);
?>
</amp-carousel>
<?php
}
$this->maybe_render_archive_link();
?>
</div>
</div>
<?php
$content = (string) ob_get_clean();
/**
* Filters the Carousel renderer stories content.
*
* @since 1.5.0
*
* @param string $content Stories content.
*/
return apply_filters( 'web_stories_carousel_renderer_stories_content', $content );
}
/**
* Get Carousel settings.
*
* @since 1.5.0
*
* @return array<string,array<string,bool>> Carousel settings.
*/
protected function get_carousel_settings(): array {
return [
'config' => [
'isRTL' => is_rtl(),
],
];
}
}

View File

@@ -0,0 +1,212 @@
<?php
/**
* Class BaseFieldState.
*/
/**
* 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\Renderer\Stories\FieldState;
use Google\Web_Stories\Interfaces\Field;
use Google\Web_Stories\Interfaces\FieldState;
use Google\Web_Stories\Renderer\Stories\Fields\BaseField;
use Google\Web_Stories\Story_Post_Type;
/**
* Class BaseFieldState.
*/
class BaseFieldState implements FieldState {
/**
* Post type has archive.
*/
protected bool $has_archive = false;
/**
* Constructor.
*
* @param Story_Post_Type $story_post_type Story_Post_Type instance.
*/
public function __construct( Story_Post_Type $story_post_type ) {
$this->has_archive = (bool) $story_post_type->get_has_archive();
}
/**
* Image alignment FieldState.
*
* @since 1.5.0
*
* @return Field
*/
public function image_alignment() {
return new BaseField(
[
'label' => __( 'Image Alignment', 'web-stories' ),
'show' => false,
'hidden' => true,
]
);
}
/**
* Excerpt FieldState.
*
* @since 1.5.0
*
* @return Field
*/
public function excerpt() {
return new BaseField(
[
'label' => __( 'Display Excerpt', 'web-stories' ),
'show' => false,
'hidden' => true,
]
);
}
/**
* Author Field State.
*
* @since 1.5.0
*
* @return Field
*/
public function author() {
return new BaseField(
[
'label' => __( 'Display Author', 'web-stories' ),
'show' => true,
'hidden' => false,
]
);
}
/**
* Date field state.
*
* @since 1.5.0
*
* @return Field
*/
public function date() {
return new BaseField(
[
'label' => __( 'Display Date', 'web-stories' ),
'show' => false,
'hidden' => false,
]
);
}
/**
* Archive link field state.
*
* @since 1.5.0
*
* @return Field
*/
public function archive_link() {
return new BaseField(
[
'label' => __( 'Display Archive Link', 'web-stories' ),
'show' => $this->has_archive,
'hidden' => ! $this->has_archive,
]
);
}
/**
* Title field state.
*
* @since 1.5.0
*
* @return Field
*/
public function title() {
return new BaseField(
[
'label' => __( 'Display Title', 'web-stories' ),
'show' => true,
'hidden' => true,
]
);
}
/**
* Sharp corners field state.
*
* @since 1.5.0
*
* @return Field
*/
public function sharp_corners() {
return new BaseField(
[
'label' => __( 'Use Sharp Corners', 'web-stories' ),
'show' => false,
'hidden' => false,
]
);
}
/**
* Circle size field.
*
* @since 1.5.0
*
* @return BaseField
*/
public function circle_size() {
return new BaseField(
[
'label' => __( 'Circle Size', 'web-stories' ),
'show' => false,
]
);
}
/**
* Number of columns field.
*
* @since 1.5.0
*
* @return BaseField
*/
public function number_of_columns() {
return new BaseField(
[
'label' => __( 'Number of Columns', 'web-stories' ),
'show' => false,
]
);
}
/**
* Prepare a field object.
*
* @since 1.5.0
*
* @param array<string,bool|string> $args Arguments to build field.
* @return BaseField
*/
protected function prepare_field( array $args ): BaseField {
return new BaseField( $args );
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Carousel view based controls state.
*/
/**
* 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\Renderer\Stories\FieldState;
use Google\Web_Stories\Renderer\Stories\Fields\BaseField;
/**
* Class CarouselView.
*/
final class CarouselView extends BaseFieldState {
/**
* Author field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function author() {
$label = parent::author()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => false,
'hidden' => false,
]
);
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* Circle view based controls state.
*/
/**
* 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\Renderer\Stories\FieldState;
use Google\Web_Stories\Renderer\Stories\Fields\BaseField;
/**
* Class CircleView.
*/
final class CircleView extends BaseFieldState {
/**
* Title field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function title() {
$label = parent::title()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => false,
'hidden' => false,
]
);
}
/**
* Author field.
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function author() {
$label = parent::author()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => false,
'hidden' => true,
]
);
}
/**
* Date field.
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function date() {
$label = parent::date()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => false,
'hidden' => true,
]
);
}
/**
* Sharp corners field.
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function sharp_corners() {
$label = parent::sharp_corners()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => false,
'hidden' => true,
]
);
}
/**
* Circle size field.
*
* @return BaseField
*/
public function circle_size() {
$label = parent::circle_size()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => true,
'hidden' => false,
]
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* List view based controls state.
*/
/**
* 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\Renderer\Stories\FieldState;
use Google\Web_Stories\Renderer\Stories\Fields\BaseField;
/**
* Class GridView.
*/
final class GridView extends BaseFieldState {
/**
* Number of columns field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function number_of_columns() {
$label = parent::number_of_columns()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => true,
'hidden' => false,
]
);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* List view based controls state.
*/
/**
* 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\Renderer\Stories\FieldState;
use Google\Web_Stories\Renderer\Stories\Fields\BaseField;
/**
* Class ListView.
*/
final class ListView extends BaseFieldState {
/**
* Excerpt field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function excerpt() {
$label = parent::excerpt()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => true,
'hidden' => false,
]
);
}
/**
* Author field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function date() {
$label = parent::date()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => true,
'hidden' => false,
]
);
}
/**
* Image alignment field.
*
* @since 1.5.0
*
* @return \Google\Web_Stories\Interfaces\Field|BaseField
*/
public function image_alignment() {
$label = parent::image_alignment()->label();
return $this->prepare_field(
[
'label' => $label,
'show' => true,
'hidden' => false,
]
);
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* Field state factory.
*
* @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\Renderer\Stories\FieldStateFactory;
use Google\Web_Stories\Infrastructure\Injector;
use Google\Web_Stories\Interfaces\FieldState;
use Google\Web_Stories\Interfaces\FieldStateFactory;
use Google\Web_Stories\Renderer\Stories\FieldState\CarouselView;
use Google\Web_Stories\Renderer\Stories\FieldState\CircleView;
use Google\Web_Stories\Renderer\Stories\FieldState\GridView;
use Google\Web_Stories\Renderer\Stories\FieldState\ListView;
/**
* Class Factory
*/
class Factory implements FieldStateFactory {
/**
* Injector instance.
*
* @var Injector Injector instance.
*/
private Injector $injector;
/**
* Factory constructor.
*
* @param Injector $injector Injector instance.
*/
public function __construct( Injector $injector ) {
$this->injector = $injector;
}
/**
* Returns field state for the provided view type.
*
* @since 1.5.0
*
* @param string $view View Type.
* @return FieldState
*/
public function get_field( $view = 'grid' ) {
switch ( $view ) {
case 'grid':
/**
* GridView instance.
*
* @var FieldState
*/
$field_state = $this->injector->make( GridView::class );
break;
case 'list':
/**
* ListView instance.
*
* @var FieldState
*/
$field_state = $this->injector->make( ListView::class );
break;
case 'circles':
/**
* CircleView instance.
*
* @var FieldState
*/
$field_state = $this->injector->make( CircleView::class );
break;
case 'carousel':
/**
* CarouselView instance.
*
* @var FieldState
*/
$field_state = $this->injector->make( CarouselView::class );
break;
default:
/**
* CircleView instance.
*
* @var FieldState $default_field_state
*/
$default_field_state = $this->injector->make( CircleView::class );
/**
* Filters the field state object.
*
* This depicts
*
* @since 1.5.0
*
* @param FieldState $default_field_state Field state object.
*/
$field_state = apply_filters( 'web_stories_default_field_state', $default_field_state );
}
return $field_state;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* Base field class.
*/
/**
* 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\Renderer\Stories\Fields;
use Google\Web_Stories\Interfaces\Field;
/**
* Class BaseField.
*/
class BaseField implements Field {
/**
* Field label.
*/
private string $label;
/**
* Whether the field is enabled.
*/
private bool $hidden;
/**
* Whether to display the field.
*/
private bool $show;
/**
* BaseField constructor.
*
* @param array<string,string|bool> $args Arguments.
*/
public function __construct( array $args ) {
$this->label = isset( $args['label'] ) ? (string) $args['label'] : '';
$this->hidden = ! isset( $args['hidden'] ) || $args['hidden'];
$this->show = ! isset( $args['show'] ) || $args['show'];
}
/**
* Label for the field.
*
* @return string
*/
public function label(): string {
return $this->label;
}
/**
* Flag for field display.
*
* @return bool
*/
public function show(): bool {
return $this->show;
}
/**
* Whether the field is hidden.
*
* @return bool
*/
public function hidden(): bool {
return $this->hidden;
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Generic_Renderer class.
*
* @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\Renderer\Stories;
/**
* Generic_Renderer class.
*
* This is named as `Generic` as this renderer class
* will be used to output multiple view types, like:
*
* 1. Circle
* 2. Grid
* 3. List
*
* Since, markup for all these views type is similar, Generic Renderer
* can be used to render the stories.
*/
class Generic_Renderer extends Renderer {
/**
* Perform initial setup for object.
*
* @since 1.5.0
*
* @return void
*/
public function init(): void {
parent::init();
$this->load_assets();
}
/**
* Renders the stories output for given attributes.
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*
* @since 1.5.0
*
* @param array<string,mixed> $args Array of rendering arguments.
* @return string Rendered stories output.
*/
public function render( array $args = [] ): string {
if ( ! $this->valid() ) {
return '';
}
parent::render( $args );
$container_classes = $this->get_container_classes();
$container_styles = $this->get_container_styles();
ob_start();
?>
<div class="<?php echo esc_attr( $container_classes ); ?>" data-id="<?php echo esc_attr( (string) $this->instance_id ); ?>">
<div class="web-stories-list__inner-wrapper" style="<?php echo esc_attr( $container_styles ); ?>">
<?php
array_map(
function () {
$this->render_single_story_content();
$this->next();
},
$this->stories
);
$this->maybe_render_archive_link();
?>
</div>
</div>
<?php
$view_type = $this->get_view_type();
$content = (string) ob_get_clean();
/**
* Filters the Generic renderer stories content.
*
* The dynamic portion of the hook `$this->get_view_type()` refers to the story view type.
*
* @since 1.5.0
*
* @param string $content Stories content.
*/
return apply_filters( "web_stories_{$view_type}_renderer_stories_content", $content );
}
}

View File

@@ -0,0 +1,843 @@
<?php
/**
* Stories Renderer Base class.
*
* @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\Renderer\Stories;
use Google\Web_Stories\AMP_Story_Player_Assets;
use Google\Web_Stories\Assets;
use Google\Web_Stories\Context;
use Google\Web_Stories\Interfaces\Renderer as RenderingInterface;
use Google\Web_Stories\Model\Story;
use Google\Web_Stories\Services;
use Google\Web_Stories\Story_Post_Type;
use Google\Web_Stories\Story_Query;
use Iterator;
use WP_Post;
/**
* Renderer class.
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
*
* @phpstan-import-type StoryAttributes from \Google\Web_Stories\Story_Query
*
* @implements Iterator<int, Story>
*/
abstract class Renderer implements RenderingInterface, Iterator {
/**
* Web Stories stylesheet handle.
*/
public const STYLE_HANDLE = 'web-stories-list-styles';
/**
* Web Stories stylesheet handle.
*/
public const LIGHTBOX_SCRIPT_HANDLE = 'web-stories-lightbox';
/**
* Number of instances invoked. Kept it static to keep track.
*/
protected static int $instances = 0;
/**
* Assets instance.
*
* @var Assets Assets instance.
*/
protected Assets $assets;
/**
* Context instance.
*
* @var Context Context instance.
*/
protected Context $context;
/**
* Object ID for the Renderer class.
* To enable support for multiple carousels and lightboxes
* on the same page, we needed to identify each Renderer instance.
*
* This variable is used to add appropriate class to the Web Stories
* wrapper.
*/
protected int $instance_id = 0;
/**
* Story_Query instance.
*
* @var Story_Query Story_Query instance.
*/
protected Story_Query $query;
/**
* Story attributes
*
* @var array<string, string|int|bool> An array of story attributes.
* @phpstan-var StoryAttributes
*/
protected array $attributes = [];
/**
* Story posts.
*
* @var Story[] An array of story posts.
*/
protected array $stories = [];
/**
* Holds required html for the lightbox.
*
* @var string A string of lightbox markup.
*/
protected string $lightbox_html = '';
/**
* Pointer to iterate over stories.
*/
private int $position = 0;
/**
* Height for displaying story.
*/
protected int $height = 308;
/**
* Width for displaying story.
*/
protected int $width = 185;
/**
* Whether content overlay is enabled for story.
*/
protected bool $content_overlay;
/**
* Constructor
*
* @since 1.5.0
*
* @param Story_Query $query Story_Query instance.
*/
public function __construct( Story_Query $query ) {
$this->query = $query;
$this->attributes = $this->query->get_story_attributes();
$this->content_overlay = $this->attributes['show_title'] || $this->attributes['show_date'] || $this->attributes['show_author'] || $this->attributes['show_excerpt'];
// TODO, find a way to inject this a cleaner way.
$injector = Services::get_injector();
/**
* Assets instance.
*
* @var Assets $assets Assets instance.
*/
$assets = $injector->make( Assets::class );
/**
* Context instance.
*
* @var Context $context Context instance.
*/
$context = $injector->make( Context::class );
$this->assets = $assets;
$this->context = $context;
}
/**
* Output markup for amp stories.
*
* @since 1.5.0
*
* @param array<string,mixed> $args Array of rendering arguments.
* @return string
*/
public function render( array $args = [] ): string {
++self::$instances;
$this->instance_id = self::$instances;
foreach ( $args as $key => $val ) {
if ( property_exists( $this, $key ) ) {
$this->{$key} = $val;
}
}
return '';
}
/**
* Retrieve current story.
*
* @since 1.5.0
*
* @return Story|null
*/
public function current(): ?Story {
return $this->stories[ $this->position ] ?? null;
}
/**
* Retrieve next story.
*
* @since 1.5.0
*
* @return void
*/
public function next(): void {
++$this->position;
}
/**
* Retrieve the key for current node in list.
*
* @since 1.5.0
*
* @return bool|float|int|string|void|null
*/
#[\ReturnTypeWillChange]
public function key() {
return $this->position;
}
/**
* Check if current position is valid.
*
* @since 1.5.0
*
* @return bool
*/
public function valid(): bool {
return isset( $this->stories[ $this->position ] );
}
/**
* Reset pointer to start of the list.
*
* @since 1.5.0
*
* @return void
*/
public function rewind(): void {
$this->position = 0;
}
/**
* Perform initial setup for object.
*
* @since 1.5.0
*
* @return void
*/
public function init(): void {
$this->stories = array_filter( array_map( [ $this, 'prepare_stories' ], $this->query->get_stories() ) );
add_action( 'wp_footer', [ $this, 'render_stories_lightbox' ] );
add_action( 'amp_post_template_footer', [ $this, 'render_stories_lightbox' ] );
}
/**
* Initializes renderer functionality.
*
* @since 1.5.0
*
* @return void
*/
public function load_assets(): void {
if ( wp_style_is( self::STYLE_HANDLE, 'registered' ) ) {
return;
}
// Web Stories styles for AMP and non-AMP pages.
$this->assets->register_style_asset( self::STYLE_HANDLE );
// Web Stories lightbox script.
$this->assets->register_script_asset( self::LIGHTBOX_SCRIPT_HANDLE, [ AMP_Story_Player_Assets::SCRIPT_HANDLE ] );
if ( \defined( 'AMPFORWP_VERSION' ) ) {
add_action( 'amp_post_template_css', [ $this, 'add_amp_post_template_css' ] );
}
}
/**
* Prints required inline CSS when using the AMP for WP plugin.
*
* @since 1.13.0
*
* @return void
*/
public function add_amp_post_template_css(): void {
$path = $this->assets->get_base_path( sprintf( 'assets/css/%s%s.css', self::STYLE_HANDLE, is_rtl() ? '-rtl' : '' ) );
if ( is_readable( $path ) ) {
$css = file_get_contents( $path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
echo $css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Returns story item data.
*
* @SuppressWarnings(PHPMD.NPathComplexity)
*
* @since 1.5.0
*
* @param object $post Array of post objects.
* @return Story|null Story object or null if given post
*/
public function prepare_stories( $post ): ?Story {
if ( ! $post instanceof WP_Post ) {
return null;
}
$story_data = [];
// TODO: get from field state instead.
if ( ! $this->is_view_type( 'circles' ) ) {
if ( isset( $this->attributes['show_author'] ) && true === $this->attributes['show_author'] ) {
$author_id = absint( get_post_field( 'post_author', $post->ID ) );
$story_data['author'] = get_the_author_meta( 'display_name', $author_id );
}
if ( isset( $this->attributes['show_date'] ) && true === $this->attributes['show_date'] ) {
/* translators: Date format, see https://www.php.net/manual/en/datetime.format.php */
$story_data['date'] = get_the_date( __( 'M j, Y', 'web-stories' ), $post->ID );
}
}
$story_data['classes'] = $this->get_single_story_classes();
$story = new Story( $story_data );
$story->load_from_post( $post );
return $story;
}
/**
* Render story markup.
*
* @since 1.5.0
*
* @return void
*/
public function render_single_story_content(): void {
/**
* Story object.
*
* @var Story $story
*/
$story = $this->current();
$single_story_classes = $this->get_single_story_classes();
$lightbox_state = 'lightbox' . $story->get_id() . $this->instance_id;
// No need to load these styles on admin as editor styles are being loaded by the block.
if ( ! is_admin() || ( \defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) ) {
// Web Stories Styles for AMP and non-AMP pages.
$this->assets->enqueue_style_asset( self::STYLE_HANDLE );
}
if ( $this->context->is_amp() ) {
?>
<div
class="<?php echo esc_attr( $single_story_classes ); ?>"
on="<?php echo esc_attr( sprintf( 'tap:AMP.setState({%1$s: ! %1$s})', $lightbox_state ) ); ?>"
tabindex="0"
role="button"
>
<?php $this->render_story_with_poster(); ?>
</div>
<?php
} else {
$this->assets->enqueue_style( AMP_Story_Player_Assets::SCRIPT_HANDLE );
$this->assets->enqueue_script( AMP_Story_Player_Assets::SCRIPT_HANDLE );
$this->assets->enqueue_script_asset( self::LIGHTBOX_SCRIPT_HANDLE );
?>
<div class="<?php echo esc_attr( $single_story_classes ); ?>">
<?php $this->render_story_with_poster(); ?>
</div>
<?php
}
}
/**
* Renders the lightbox markup for non-amp pages.
*
* @since 1.5.0
*
* @return void
*/
public function render_stories_with_lightbox(): void {
$data = [
'controls' => [
[
'name' => 'close',
'position' => 'start',
],
[
'name' => 'skip-next',
],
],
'behavior' => [
'autoplay' => false,
],
];
?>
<div class="web-stories-list__lightbox">
<amp-story-player width="3.6" height="6" layout="responsive">
<script type="application/json">
<?php echo wp_json_encode( $data ); ?>
</script>
<?php echo wp_kses_post( $this->lightbox_html ); ?>
</amp-story-player>
</div>
<?php
}
/**
* Renders the lightbox markup for non-amp pages.
*
* @since 1.5.0
*
* @return void
*/
public function render_stories_with_lightbox_amp(): void {
// Have to ignore this as the escaping functions are stripping off 'amp-bind' custom attribute '[class]'.
echo $this->lightbox_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Generated with properly escaped data.
}
/**
* Renders stories lightbox on 'wp_footer'.
*
* @return void
*/
public function render_stories_lightbox(): void {
// Return if we don't have anything to render.
if ( empty( $this->lightbox_html ) ) {
return;
}
?>
<div class="web-stories-list__lightbox-wrapper <?php echo esc_attr( 'ws-lightbox-' . $this->instance_id ); ?>">
<?php
if ( $this->context->is_amp() ) {
$this->render_stories_with_lightbox_amp();
} else {
$this->render_stories_with_lightbox();
}
?>
</div>
<?php
}
/**
* Verifies the current view type.
*
* @since 1.5.0
*
* @param string $view_type View type to check.
* @return bool Whether or not current view type matches the one passed.
*/
protected function is_view_type( $view_type ): bool {
return ( ! empty( $this->attributes['view_type'] ) && $view_type === $this->attributes['view_type'] );
}
/**
* Get view type for stories.
*
* @since 1.5.0
*
* @return string
*/
protected function get_view_type(): string {
return ! empty( $this->attributes['view_type'] ) ? $this->attributes['view_type'] : 'circles';
}
/**
* Renders stories archive link if the 'show_archive_link' attribute is set to true.
*
* @since 1.5.0
*
* @return void
*/
protected function maybe_render_archive_link(): void {
if (
empty( $this->attributes['show_archive_link'] ) ||
true !== $this->attributes['show_archive_link'] ||
empty( $this->attributes['archive_link_label'] )
) {
return;
}
$web_stories_archive = get_post_type_archive_link( Story_Post_Type::POST_TYPE_SLUG );
if ( empty( $web_stories_archive ) ) {
return;
}
?>
<div class="web-stories-list__archive-link">
<a href="<?php echo esc_url( $web_stories_archive ); ?>">
<?php echo esc_html( $this->attributes['archive_link_label'] ); ?>
</a>
</div>
<?php
}
/**
* Gets the classes for renderer container.
*
* @since 1.5.0
*
* @return string
*/
protected function get_view_classes(): string {
$view_classes = [];
$view_classes[] = ! empty( $this->attributes['view_type'] ) ? sprintf( 'is-view-type-%1$s', $this->attributes['view_type'] ) : 'is-view-type-circles';
if ( $this->is_view_type( 'grid' ) && ! empty( $this->attributes['number_of_columns'] ) ) {
$view_classes[] = sprintf( 'columns-%1$d', $this->attributes['number_of_columns'] );
}
if ( ! $this->is_view_type( 'circles' ) && ! empty( $this->attributes['sharp_corners'] ) ) {
$view_classes[] = 'is-style-squared';
} else {
$view_classes[] = 'is-style-default';
}
if ( $this->is_view_type( 'circles' ) && ! empty( $this->attributes['show_title'] ) ) {
$view_classes[] = 'has-title';
}
if ( $this->is_view_type( 'circles' ) || $this->is_view_type( 'carousel' ) ) {
$view_classes[] = 'is-carousel';
}
return implode( ' ', $view_classes );
}
/**
* Gets the classes for renderer container.
*
* @since 1.5.0
*
* @return string
*/
protected function get_container_classes(): string {
$container_classes = [];
$container_classes[] = 'web-stories-list';
$container_classes[] = ! empty( $this->attributes['align'] ) ? sprintf( 'align%1$s', $this->attributes['align'] ) : 'alignnone';
$container_classes[] = ! empty( $this->attributes['class'] ) ? $this->attributes['class'] : '';
if ( ! empty( $this->attributes['show_archive_link'] ) ) {
$container_classes[] = 'has-archive-link';
}
$container_classes = array_filter( $container_classes );
$view_type_classes = $this->get_view_classes();
return sprintf( '%1$s %2$s', implode( ' ', $container_classes ), $view_type_classes );
}
/**
* Gets the single story container classes.
*
* @since 1.5.0
*
* @return string
*/
protected function get_single_story_classes(): string {
$single_story_classes = [];
$single_story_classes[] = 'web-stories-list__story';
if ( $this->context->is_amp() ) {
$single_story_classes[] = 'web-stories-list__story--amp';
}
if ( ! empty( $this->attributes['image_alignment'] ) && ( 'right' === $this->attributes['image_alignment'] ) ) {
$single_story_classes[] = 'image-align-right';
}
$classes = implode( ' ', $single_story_classes );
/**
* Filters the web stories renderer single story classes.
*
* @since 1.5.0
*
* @param string $classes Single story classes.
*/
return apply_filters( 'web_stories_renderer_single_story_classes', $classes );
}
/**
* Gets the single story container styles.
*
* @since 1.5.0
*
* @return string Style string.
*/
protected function get_container_styles(): string {
$story_styles = ! empty( $this->attributes['circle_size'] ) && $this->is_view_type( 'circles' ) ?
sprintf( '--ws-circle-size:%1$dpx', $this->attributes['circle_size'] ) :
'';
$story_styles .= $this->is_view_type( 'carousel' ) ? sprintf( '--ws-story-max-width:%1$dpx', $this->width ) : '';
/**
* Filters the web stories renderer single story classes.
*
* @since 1.5.0
*
* @param string $story_styles Single story classes.
*/
return apply_filters( 'web_stories_renderer_container_styles', $story_styles );
}
/**
* Renders a story with story's poster image.
*
* @since 1.5.0
*
* @return void
*/
protected function render_story_with_poster(): void {
/**
* Story object.
*
* @var Story $story
*/
$story = $this->current();
$poster_url = $story->get_poster_portrait();
$poster_srcset = $story->get_poster_srcset();
$poster_sizes = $story->get_poster_sizes();
if ( ! $poster_url ) {
?>
<div class="web-stories-list__story-poster">
<div class="web-stories-list__story-poster-placeholder">
<a href="<?php echo esc_url( $story->get_url() ); ?>" <?php $this->render_link_attributes(); ?>>
<?php echo esc_html( $story->get_title() ); ?>
</a>
</div>
</div>
<?php
} else {
?>
<div class="web-stories-list__story-poster">
<a href="<?php echo esc_url( $story->get_url() ); ?>" <?php $this->render_link_attributes(); ?>>
<img
src="<?php echo esc_url( $poster_url ); ?>"
alt="<?php echo esc_attr( $story->get_title() ); ?>"
width="<?php echo absint( $this->width ); ?>"
height="<?php echo absint( $this->height ); ?>"
<?php if ( ! empty( $poster_srcset ) ) { ?>
srcset="<?php echo esc_attr( $poster_srcset ); ?>"
<?php } ?>
<?php if ( ! empty( $poster_sizes ) ) { ?>
sizes="<?php echo esc_attr( $poster_sizes ); ?>"
<?php } ?>
loading="lazy"
decoding="async"
>
</a>
</div>
<?php
}
$this->get_content_overlay();
if ( ! $this->context->is_amp() ) {
$this->generate_lightbox_html( $story );
} else {
$this->generate_amp_lightbox_html_amp( $story );
}
}
/**
* Render additional link attributes.
*
* Allows customization of html attributes in the web stories widget anchor tag loop
* Converts array into escaped inline html attributes.
*
* @since 1.17.0
*
* @return void
*/
protected function render_link_attributes(): void {
/**
* The current story.
*
* @var Story $story
*/
$story = $this->current();
/**
* Filters the link attributes added to a story's <a> tag.
*
* @since 1.17.0
*
* @param array $attributes Key value array of attribute name to attribute value.
* @param Story $story The current story instance.
* @param int $position The current story's position within the list.
* @param string $view_type The current view type.
*/
$attributes = apply_filters( 'web_stories_renderer_link_attributes', [], $story, $this->position, $this->get_view_type() );
$attrs = [];
if ( ! empty( $attributes ) ) {
foreach ( $attributes as $attribute => $value ) {
$attrs[] = wp_kses_one_attr( $attribute . '="' . esc_attr( $value ) . '"', 'a' );
}
}
$attrs = array_filter( $attrs ); // Filter out empty values rejected by KSES.
//phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
echo implode( ' ', $attrs );
}
/**
* Renders the content overlay markup.
*
* @since 1.5.0
*
* @return void
*/
protected function get_content_overlay(): void {
/**
* Story object.
*
* @var Story $story
*/
$story = $this->current();
if ( empty( $this->content_overlay ) ) {
return;
}
?>
<div class="web-stories-list__story-content-overlay">
<?php if ( ! empty( $this->attributes['show_title'] ) ) { ?>
<div class="story-content-overlay__title">
<?php echo esc_html( $story->get_title() ); ?>
</div>
<?php } ?>
<?php if ( ! empty( $this->attributes['show_excerpt'] ) ) { ?>
<div class="story-content-overlay__excerpt">
<?php echo esc_html( $story->get_excerpt() ); ?>
</div>
<?php } ?>
<?php if ( ! empty( $story->get_author() ) ) { ?>
<div class="story-content-overlay__author">
<?php
/* translators: byline. %s: author name. */
echo esc_html( sprintf( __( 'By %s', 'web-stories' ), $story->get_author() ) );
?>
</div>
<?php } ?>
<?php if ( ! empty( $story->get_date() ) ) { ?>
<time class="story-content-overlay__date">
<?php
/* translators: %s: publish date. */
echo esc_html( sprintf( __( 'On %s', 'web-stories' ), $story->get_date() ) );
?>
</time>
<?php } ?>
</div>
<?php
}
/**
* Generate HTML for the non-AMP page request.
*
* @since 1.5.0
*
* @param Story $story Current Story.
* @return void
*/
protected function generate_lightbox_html( $story ): void {
// Start collecting markup for the lightbox stories. This way we don't have to re-run the loop.
ob_start();
// Collect story links to fill-in non-AMP lightbox 'amp-story-player'.
?>
<a href="<?php echo esc_url( $story->get_url() ); ?>" <?php $this->render_link_attributes(); ?>><?php echo esc_html( $story->get_title() ); ?></a>
<?php
$this->lightbox_html .= ob_get_clean();
}
/**
* Markup for the lightbox used on AMP pages.
*
* @since 1.5.0
*
* @param Story $story Current Story.
* @return void
*/
protected function generate_amp_lightbox_html_amp( $story ): void {
// Start collecting markup for the lightbox stories. This way we don't have to re-run the loop.
ob_start();
$lightbox_state = 'lightbox' . $story->get_id() . $this->instance_id;
$lightbox_id = 'lightbox-' . $story->get_id() . $this->instance_id;
?>
<amp-lightbox
id="<?php echo esc_attr( $lightbox_id ); ?>"
[open]="<?php echo esc_attr( $lightbox_state ); ?>"
layout="nodisplay"
on="lightboxClose:AMP.setState({<?php echo esc_attr( $lightbox_state ); ?>: false})"
role="button"
tabindex="0"
>
<div class="web-stories-list__lightbox show">
<button type="button"
class="story-lightbox__close-button"
on="tap:<?php echo esc_attr( $lightbox_id ); ?>.close"
aria-label="<?php esc_attr_e( 'Close', 'web-stories' ); ?>"
>
<span class="story-lightbox__close-button--stick"></span>
<span class="story-lightbox__close-button--stick"></span>
</button>
<amp-story-player
width="3.6"
height="6"
layout="responsive"
>
<a href="<?php echo esc_url( $story->get_url() ); ?>" <?php $this->render_link_attributes(); ?>><?php echo esc_html( $story->get_title() ); ?></a>
</amp-story-player>
</div>
</amp-lightbox>
<?php
$this->lightbox_html .= ob_get_clean();
}
}