From dab54a884315c3bd19a775ba1f62a2277e9534d3 Mon Sep 17 00:00:00 2001 From: David Herrera Date: Mon, 18 Nov 2024 05:10:36 -0500 Subject: [PATCH] Allow content matching selectors to be excluded --- admin/apple-actions/index/class-export.php | 47 +++++++++++++++++++ ...-admin-apple-settings-section-advanced.php | 14 ++++++ .../class-admin-apple-settings-section.php | 1 + includes/apple-exporter/class-settings.php | 1 + .../apple-actions/index/test-class-export.php | 28 +++++++++++ 5 files changed, 91 insertions(+) diff --git a/admin/apple-actions/index/class-export.php b/admin/apple-actions/index/class-export.php index b12a9175..867cd94e 100644 --- a/admin/apple-actions/index/class-export.php +++ b/admin/apple-actions/index/class-export.php @@ -606,6 +606,7 @@ private function get_content( $post ) { $content = apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound // Clean up the HTML a little. + $content = $this->exclude_selectors( $content ); $content = $this->remove_tags( $content ); $content = $this->remove_entities( $content ); @@ -643,6 +644,52 @@ private function remove_entities( $content ) { return str_replace( '&', '&', $content ); } + /** + * Remove excluded selectors from the content. + * + * @param string $content The content to be filtered. + * @return string + */ + private function exclude_selectors( $content ) { + $raw_selectors = $this->get_setting( 'excluded_selectors' ); + + $selectors = explode( ',', $raw_selectors ); + $selectors = array_map( 'trim', $selectors ); + $selectors = array_filter( $selectors ); + + if ( count( $selectors ) === 0 ) { + return $content; + } + + libxml_use_internal_errors( true ); + $dom = new \DOMDocument(); + $dom->loadHTML( ''. $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); + $xpath = new \DOMXPath( $dom ); + libxml_clear_errors(); + + foreach ( $selectors as $selector ) { + $nodes = []; + + if ( str_starts_with( $selector, '#' ) ) { + $nodes = $xpath->query( '//*[@id="' . substr( $selector, 1 ) . '"]' ); + } + + if ( str_starts_with( $selector, '.' ) ) { + $nodes = $xpath->query( '//*[contains(concat(" ", normalize-space(@class), " "), " ' . substr( $selector, 1 ) . ' ")]' ); + } + + if ( is_iterable( $nodes ) ) { + foreach ( $nodes as $node ) { + $node->parentNode->removeChild( $node ); + } + } + } + + $content = $dom->saveHTML(); + + return $content; + } + /** * Loads settings for the Exporter_Content from the WordPress post metadata. * diff --git a/admin/settings/class-admin-apple-settings-section-advanced.php b/admin/settings/class-admin-apple-settings-section-advanced.php index fda8cd0b..17bb48b8 100644 --- a/admin/settings/class-admin-apple-settings-section-advanced.php +++ b/admin/settings/class-admin-apple-settings-section-advanced.php @@ -71,6 +71,16 @@ public function __construct( $page ) { 'description' => __( 'Enter a CSS class name that will be used to generate the Aside component. Do not prefix with a period.', 'apple-news' ), 'required' => false, ], + 'excluded_selectors' => [ + 'label' => __( 'Selectors', 'apple-news' ), + 'type' => 'text', + 'size' => 150, + 'description' => sprintf( + __( 'Enter a comma-separated list of CSS class or ID selectors, like %s. Elements in post content matching these selectors will be removed from the content published to Apple News.', 'apple-news' ), + '.my-class, #my-id', + ), + 'required' => false, + ], ]; // Add the groups. @@ -91,6 +101,10 @@ public function __construct( $page ) { 'label' => __( 'Aside Component', 'apple-news' ), 'settings' => [ 'aside_component_class' ], ], + 'selectors' => [ + 'label' => __( 'Excluded Elements', 'apple-news' ), + 'settings' => [ 'excluded_selectors' ], + ], ]; parent::__construct( $page ); diff --git a/admin/settings/class-admin-apple-settings-section.php b/admin/settings/class-admin-apple-settings-section.php index a96aab80..2954abbd 100644 --- a/admin/settings/class-admin-apple-settings-section.php +++ b/admin/settings/class-admin-apple-settings-section.php @@ -135,6 +135,7 @@ class Admin_Apple_Settings_Section extends Apple_News { 'b' => [], 'strong' => [], 'i' => [], + 'code' => [], 'em' => [], 'a' => [ 'href' => [], diff --git a/includes/apple-exporter/class-settings.php b/includes/apple-exporter/class-settings.php index fefb2220..dc1d8c7e 100644 --- a/includes/apple-exporter/class-settings.php +++ b/includes/apple-exporter/class-settings.php @@ -49,6 +49,7 @@ class Settings { 'post_types' => [ 'post' ], 'show_metabox' => 'yes', 'use_remote_images' => 'yes', + 'excluded_selectors' => '', ]; /** diff --git a/tests/admin/apple-actions/index/test-class-export.php b/tests/admin/apple-actions/index/test-class-export.php index 7ffa9ac6..64a12402 100644 --- a/tests/admin/apple-actions/index/test-class-export.php +++ b/tests/admin/apple-actions/index/test-class-export.php @@ -259,6 +259,34 @@ public function test_remove_entities() { ); } + /** + * Tests the ability to remove selectors from the content. + */ + public function test_remove_selectors() { + $post_id = $this->factory->post->create( + [ + 'post_content' => <<First paragraph

+

Second paragraph

+

Third paragraph

+

Fourth paragraph

+

Fifth paragraph

+

Sixth paragraph

+HTML, + ] + ); + + $this->settings->excluded_selectors = '.foo, #foo'; + + $export = new Export( $this->settings, $post_id ); + $exporter = $export->fetch_exporter(); + $content = $exporter->get_content()->content(); + + $this->assertStringNotContainsString( 'First paragraph', $content ); + $this->assertStringNotContainsString( 'Third paragraph', $content ); + $this->assertStringNotContainsString( 'Fifth paragraph', $content ); + } + /** * Tests the behavior of the apple_news_is_exporting() function. */