ProShell v2.0
Dashboard
Server Info
Server: 158.106.128.192
PHP: 8.2.33
home
planet5
public_html
bansibaba.com
wp-includes
2026-09-13 11:29:23
Editing: class-amp-core-block-handler.php
Cancel
Save Changes
<?php /** * Class AMP_Core_Block_Handler * * @package AMP */ use AmpProject\Dom\Document; use AmpProject\Html\Attribute; /** * Class AMP_Core_Block_Handler * * @since 1.0 * @internal */ class AMP_Core_Block_Handler extends AMP_Base_Embed_Handler { /** * Attribute to store the original width on a video or iframe just before WordPress removes it. * * @see AMP_Core_Block_Handler::preserve_widget_text_element_dimensions() * @see AMP_Core_Block_Handler::process_text_widgets() * @var string */ const AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME = 'amp-preserved-width'; /** * Attribute to store the original height on a video or iframe just before WordPress removes it. * * @see AMP_Core_Block_Handler::preserve_widget_text_element_dimensions() * @see AMP_Core_Block_Handler::process_text_widgets() * @var string */ const AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME = 'amp-preserved-height'; /** * Count of the category widgets encountered. * * @var int */ private $category_widget_count = 0; /** * Count of the navigation blocks encountered. * * @var int */ private $navigation_block_count = 0; /** * Methods to ampify blocks. * * @var array */ protected $block_ampify_methods = [ 'core/categories' => 'ampify_categories_block', 'core/archives' => 'ampify_archives_block', 'core/video' => 'ampify_video_block', 'core/file' => 'ampify_file_block', 'core/gallery' => 'ampify_gallery_block', 'core/navigation' => 'ampify_navigation_block', ]; /** * Register embed. */ public function register_embed() { add_filter( 'render_block', [ $this, 'filter_rendered_block' ], 0, 2 ); add_filter( 'widget_text_content', [ $this, 'preserve_widget_text_element_dimensions' ], PHP_INT_MAX ); } /** * Unregister embed. */ public function unregister_embed() { remove_filter( 'render_block', [ $this, 'filter_rendered_block' ], 0 ); remove_filter( 'widget_text_content', [ $this, 'preserve_widget_text_element_dimensions' ], PHP_INT_MAX ); } /** * Filters the content of a single block to make it AMP valid. * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * @return string Filtered block content. */ public function filter_rendered_block( $block_content, $block ) { if ( ! isset( $block['blockName'] ) ) { return $block_content; // @codeCoverageIgnore } if ( isset( $block['attrs'] ) && 'core/shortcode' !== $block['blockName'] ) { $injected_attributes = ''; $prop_attribute_mapping = [ 'ampCarousel' => 'data-amp-carousel', 'ampLightbox' => 'data-amp-lightbox', ]; foreach ( $prop_attribute_mapping as $prop => $attr ) { if ( isset( $block['attrs'][ $prop ] ) ) { $property_value = $block['attrs'][ $prop ]; if ( is_bool( $property_value ) ) { $property_value = $property_value ? 'true' : 'false'; } $injected_attributes .= sprintf( ' %s="%s"', $attr, esc_attr( $property_value ) ); } } if ( $injected_attributes ) { $block_content = preg_replace( '/(<\w+)/', '$1' . $injected_attributes, $block_content, 1 ); } } if ( isset( $this->block_ampify_methods[ $block['blockName'] ] ) ) { $method_name = $this->block_ampify_methods[ $block['blockName'] ]; $block_content = $this->{$method_name}( $block_content, $block ); } elseif ( 'core/image' === $block['blockName'] || 'core/audio' === $block['blockName'] ) { /* * While the video block placeholder just outputs an empty video element, the placeholders for image and * audio blocks output empty <img> and <audio> respectively. These will result in AMP validation errors, * so we need to empty out the block content to prevent this from happening. Note that <source> is used * for <img> because eventually the image block could use <picture>. */ if ( ! preg_match( '/src=|<source/', $block_content ) ) { $block_content = ''; } } return $block_content; } /** * Fix rendering of categories block when displayAsDropdown. * * This excludes the disallowed JS scrips, adds <form> tags, and uses on:change for <select>. * * @see render_block_core_categories() * * @param string $block_content Block content. * @return string Rendered. */ public function ampify_categories_block( $block_content ) { static $block_id = 0; $block_id++; $form_id = "wp-block-categories-dropdown-{$block_id}-form"; // Remove output of build_dropdown_script_block_core_categories(). $block_content = preg_replace( '#<script.+?</script>#s', '', $block_content ); $form = sprintf( '<form action="%s" method="get" target="_top" id="%s">', esc_url( home_url() ), esc_attr( $form_id ) ); $block_content = preg_replace( '#(<select)(.+</select>)#s', $form . '$1' . sprintf( ' on="change:%1$s.submit"', esc_attr( $form_id ) ) . '$2</form>', $block_content, 1 ); return $block_content; } /** * Fix rendering of archives block when displayAsDropdown. * * This replaces disallowed script with the use of on:change for <select>. * * @see render_block_core_archives() * * @param string $block_content Block content. * @return string Rendered. */ public function ampify_archives_block( $block_content ) { // Eliminate use of uniqid(). Core should be using wp_unique_id() here. static $block_id = 0; $block_id++; $block_content = preg_replace( '/(?<="wp-block-archives-)\w+(?=")/', $block_id, $block_content ); // Replace onchange with on attribute. $block_content = preg_replace( '/onchange=".+?"/', 'on="change:AMP.navigateTo(url=event.value)"', $block_content ); return $block_content; } /** * Ampify video block. * * Inject the video attachment's dimensions if available. This prevents having to try to look up the attachment * post by the video URL in `\AMP_Video_Sanitizer::filter_video_dimensions()`. * * @see \AMP_Video_Sanitizer::filter_video_dimensions() * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * @return string Filtered block content. */ public function ampify_video_block( $block_content, $block ) { if ( empty( $block['attrs']['id'] ) || 'attachment' !== get_post_type( $block['attrs']['id'] ) ) { return $block_content; } $meta_data = wp_get_attachment_metadata( $block['attrs']['id'] ); if ( ! isset( $meta_data['width'], $meta_data['height'] ) ) { return $block_content; } $block_content = preg_replace_callback( '/(?<=<video)\s[^>]+/', static function ( $matches ) use ( $meta_data ) { $attrs = $matches[0]; if ( ! preg_match( '/\s(width|height|style)=/', $attrs ) ) { $attrs .= sprintf( ' width="%1$d" height="%2$d" style="aspect-ratio:%1$d/%2$d"', $meta_data['width'], $meta_data['height'] ); } return $attrs; }, $block_content, 1 ); return $block_content; } /** * Ampify file block. * * Fix handling of PDF previews by dequeuing wp-block-library-file and ensuring preview element has 100% width. * * @see \AMP_Object_Sanitizer::sanitize_pdf() * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * @return string Filtered block content. */ public function ampify_file_block( $block_content, $block ) { if ( empty( $block['attrs']['displayPreview'] ) || empty( $block['attrs']['href'] ) || '.pdf' !== substr( wp_parse_url( $block['attrs']['href'], PHP_URL_PATH ), -4 ) ) { return $block_content; } add_action( 'wp_print_scripts', [ $this, 'dequeue_block_library_file_script' ], 0 ); add_action( 'wp_print_footer_scripts', [ $this, 'dequeue_block_library_file_script' ], 0 ); // In Twenty Twenty the PDF embed fails to render due to the parent of the embed having // the style rule `display: flex`. Ensuring the element has 100% width fixes that issue. $block_content = preg_replace( ':(?=</div>):', '<style id="amp-wp-file-block">.wp-block-file > .wp-block-file__embed { width:100% }</style>', $block_content, 1 ); return $block_content; } /** * Ampify gallery block. * * Apply data-amp-lightbox attribute only to descendant image blocks. * * @since 2.2.1 * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * @return string Filtered block content. */ public function ampify_gallery_block( $block_content, $block ) { // Skip legacy gallery blocks. if ( ! empty( $block['attrs']['ids'] ) ) { return $block_content; } $block_content = preg_replace( '/\sdata-amp-lightbox="\w+"/', '', $block_content ); // Bail out early if there are no images in the gallery or the lightbox feature is not enabled. if ( empty( $block['innerBlocks'] ) || empty( $block['attrs']['ampLightbox'] ) ) { return $block_content; } // Add data attributes to figure elements that are nested in the gallery block. // Note that the first match is the gallery block itself which doesn't need the data-amp-lightbox attribute. $figure_count = 0; $block_content = preg_replace_callback( '/(?<=<figure\s)/', static function () use ( &$figure_count ) { return 0 < $figure_count++ ? 'data-amp-lightbox="true" ' : ''; }, $block_content ); return $block_content; } /** * Dequeue wp-block-library-file script. */ public function dequeue_block_library_file_script() { wp_dequeue_script( 'wp-block-library-file' ); } /** * Ampify navigation block contained by <nav> element. * * @since 2.2.1 * * @param string $block_content The block content about to be appended. * @param array $block The full block, including name and attributes. * * @return string Filtered block content. */ public function ampify_navigation_block( $block_content, $block ) { if ( 0 === $this->navigation_block_count ) { add_action( 'wp_print_scripts', [ $this, 'dequeue_block_navigation_view_script' ], 0 ); add_action( 'wp_print_footer_scripts', [ $this, 'dequeue_block_navigation_view_script' ], 0 ); } $is_interactive_block = str_contains( preg_match( '/(?<=<nav)\s[^>]+/', $block_content, $matches ) ? $matches[0] : '', 'data-wp-interactive' ); $this->navigation_block_count++; $modal_state_property = "modal_{$this->navigation_block_count}_expanded"; // Set `aria-expanded` value of submenus whenever AMP state changes. $submenu_toggles_count = 0; $block_content = preg_replace_callback( '/(?<=<button)\s[^>]+/', static function ( $matches ) use ( $modal_state_property, &$submenu_toggles_count ) { $new_block_content = $matches[0]; if ( false === strpos( $new_block_content, 'wp-block-navigation-submenu__toggle' ) ) { return $new_block_content; } $submenu_toggles_count++; $submenu_state_property = str_replace( 'expanded', 'submenu_' . $submenu_toggles_count . '_expanded', $modal_state_property ); // Set `aria-expanded` value of submenus whenever AMP state changes. return str_replace( ' aria-expanded', sprintf( ' on="tap:AMP.setState({ %1$s: !%1$s })" [aria-expanded]="%1$s ? \'true\' : \'false\'" aria-expanded', esc_attr( $submenu_state_property ) ), $new_block_content ); }, $block_content ); // In case of the "Mobile" option value, the `overlayMenu` attribute is not set at all. if ( ! empty( $block['attrs']['overlayMenu'] ) && 'never' === $block['attrs']['overlayMenu'] ) { return $block_content; } $block_content = preg_replace_callback( '/(?<=<button)\s[^>]+/', static function ( $matches ) use ( $modal_state_property, $is_interactive_block ) { $new_block_content = $matches[0]; // Skip submenu toggles. if ( false !== strpos( $new_block_content, 'wp-block-navigation-submenu__toggle' ) ) { return $new_block_content; } if ( $is_interactive_block ) { // Replace `data-wp-on--click` or `data-wp-on-async--click` with AMP state on submenu open button. if ( false !== strpos( $new_block_content, 'wp-block-navigation__responsive-container-open' ) ) { $new_block_content = preg_replace( '/\sdata-wp-on(?:-async)?--click="[^"]+"/', sprintf( ' on="tap:AMP.setState({ %1$s: !%1$s })"', esc_attr( $modal_state_property ) ), $new_block_content ); } // Replace `data-wp-on--click` or `data-wp-on-async--click` with AMP state on submenu close button. if ( false !== strpos( $new_block_content, 'wp-block-navigation__responsive-container-close' ) ) { $new_block_content = preg_replace( '/\sdata-wp-on(?:-async)?--click="[^"]+"/', sprintf( ' on="tap:AMP.setState({ %1$s: !%1$s })"', esc_attr( $modal_state_property ) ), $new_block_content ); } } else { // Replace micromodal toggle logic bound with buttons with AMP state to open the modal. $new_block_content = preg_replace( '/\sdata-micromodal-trigger="modal-\w+"/', sprintf( ' on="tap:AMP.setState({ %1$s: !%1$s })"', esc_attr( $modal_state_property ) ), $new_block_content ); // Replace micromodal toggle logic bound with buttons with AMP state to close the modal. $new_block_content = str_replace( ' data-micromodal-close', sprintf( ' on="tap:AMP.setState({ %1$s: !%1$s })"', esc_attr( $modal_state_property ) ), $new_block_content ); } // Set `aria-expanded` value whenever AMP state changes. return str_replace( ' aria-expanded', sprintf( ' [aria-expanded]="%s ? \'true\' : \'false\'" aria-expanded', esc_attr( $modal_state_property ) ), $new_block_content ); }, $block_content ); // Change a responsive container class name and aria-hidden value based on the AMP state. $block_content = preg_replace_callback( '/(?><.+\sclass="([^"]*wp-block-navigation__responsive-container(?>\s[^"]*)?)"[^>]*>)/', static function ( $matches ) use ( $modal_state_property ) { $new_block_content = str_replace( ' class=', sprintf( ' [aria-hidden]="%1$s ? \'false\' : \'true\'" aria-hidden="true" [class]="%1$s ? \'%2$s is-menu-open has-modal-open\' : \'%2$s\'" class=', esc_attr( $modal_state_property ), esc_attr( $matches[1] ) ), $matches[0] ); return $new_block_content; }, $block_content ); return $block_content; } /** * Dequeue wp-block-navigation-view script. * * @since 2.2.1 */ public function dequeue_block_navigation_view_script() { wp_dequeue_script( 'wp-block-navigation-view' ); wp_dequeue_script( 'wp-block-navigation-view-2' ); } /** * Sanitize widgets that are not added via Gutenberg. * * @param Document $dom Document. * @param array $args Args passed to sanitizer. */ public function sanitize_raw_embeds( Document $dom, $args = [] ) { $this->process_categories_widgets( $dom ); $this->process_archives_widgets( $dom, $args ); $this->process_text_widgets( $dom ); } /** * Process "Categories" widgets. * * @since 2.0 * * @param Document $dom Document. */ private function process_categories_widgets( Document $dom ) { $selects = $dom->xpath->query( '//form/select[ @name = "cat" ]' ); foreach ( $selects as $select ) { if ( ! $select instanceof DOMElement ) { continue; // @codeCoverageIgnore } $form = $select->parentNode; if ( ! $form instanceof DOMElement || ! $form->parentNode instanceof DOMElement ) { continue; // @codeCoverageIgnore } $script = $dom->xpath->query( './/script[ contains( text(), "onCatChange" ) ]', $form->parentNode )->item( 0 ); if ( ! $script instanceof DOMElement ) { continue; // @codeCoverageIgnore } $this->category_widget_count++; $id = sprintf( 'amp-wp-widget-categories-%d', $this->category_widget_count ); $form->setAttribute( 'id', $id ); AMP_DOM_Utils::add_amp_action( $select, 'change', sprintf( '%s.submit', $id ) ); $script->parentNode->removeChild( $script ); } } /** * Process "Archives" widgets. * * @since 2.0 * * @param Document $dom Select node retrieved from the widget. * @param array $args Args passed to sanitizer. */ private function process_archives_widgets( Document $dom, $args = [] ) { $selects = $dom->xpath->query( '//select[ @name = "archive-dropdown" and starts-with( @id, "archives-dropdown-" ) ]' ); foreach ( $selects as $select ) { if ( ! $select instanceof DOMElement ) { continue; // @codeCoverageIgnore } $script = $dom->xpath->query( './/script[ contains( text(), "onSelectChange" ) ]', $select->parentNode )->item( 0 ); if ( $script ) { $script->parentNode->removeChild( $script ); } elseif ( $select->hasAttribute( 'onchange' ) ) { // Special condition for WordPress<=5.1. $select->removeAttribute( 'onchange' ); } else { continue; } AMP_DOM_Utils::add_amp_action( $select, 'change', 'AMP.navigateTo(url=event.value)' ); // When AMP-to-AMP linking is enabled, ensure links go to the AMP version. if ( ! empty( $args['amp_to_amp_linking_enabled'] ) ) { foreach ( $dom->xpath->query( '//option[ @value != "" ]', $select ) as $option ) { /** * Option element. * * @var DOMElement $option */ $option->setAttribute( 'value', amp_add_paired_endpoint( $option->getAttribute( 'value' ) ) ); } } } } /** * Preserve dimensions of elements in a Text widget to later restore to circumvent WordPress core stripping them out. * * Core strips out the dimensions to prevent the element being made too wide for the sidebar. This is not a concern * in AMP because of responsive sizing. So this logic is here to undo what core is doing. * * @since 2.0 * @see WP_Widget_Text::inject_video_max_width_style() * @see AMP_Core_Block_Handler::process_text_widgets() * * @param string $content Content. * @return string Content. */ public function preserve_widget_text_element_dimensions( $content ) { $content = preg_replace_callback( '#<(video|iframe|object|embed)\s[^>]*>#si', static function ( $matches ) { $html = $matches[0]; $html = preg_replace( '/(?=\sheight="(\d+)")/', ' ' . self::AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME . '="$1" ', $html ); $html = preg_replace( '/(?=\swidth="(\d+)")/', ' ' . self::AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME . '="$1" ', $html ); return $html; }, $content ); return $content; } /** * Process "Text" widgets. * * @since 2.0 * @see AMP_Core_Block_Handler::preserve_widget_text_element_dimensions() * * @param Document $dom Select node retrieved from the widget. */ private function process_text_widgets( Document $dom ) { foreach ( $dom->xpath->query( '//div[ @class = "textwidget" ]' ) as $text_widget ) { // Restore the width/height attributes which were preserved in preserve_widget_text_element_dimensions. foreach ( $dom->xpath->query( sprintf( './/*[ @%s or @%s ]', self::AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME, self::AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME ), $text_widget ) as $element ) { /** @var DOMElement $element */ if ( $element->hasAttribute( self::AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME ) ) { $element->setAttribute( Attribute::WIDTH, $element->getAttribute( self::AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME ) ); $element->removeAttribute( self::AMP_PRESERVED_WIDTH_ATTRIBUTE_NAME ); } if ( $element->hasAttribute( self::AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME ) ) { $element->setAttribute( Attribute::HEIGHT, $element->getAttribute( self::AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME ) ); $element->removeAttribute( self::AMP_PRESERVED_HEIGHT_ATTRIBUTE_NAME ); } } /* * Remove inline width style which is added to video shortcode but which overruns the container. * Normally this width gets overridden by wp-mediaelement.css to be max-width: 100%, but since * MediaElement.js is not used in AMP this stylesheet is not included. In any case, videos in AMP are * responsive so this is built-in. Note also the style rule for .wp-video in amp-default.css. */ foreach ( $dom->xpath->query( './/div[ @class = "wp-video" and @style ]', $text_widget ) as $element ) { /** @var DOMElement $element */ $element->removeAttribute( 'style' ); } } } }
Upload Files
Cancel
Upload
Create New
Cancel
Create
Change Permissions
Cancel
Save
Change Date
Cancel
Save
Rename Item
Cancel
Save
Confirm Delete
Are you sure you want to delete the selected items?
Cancel
Delete