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.
*/