diff --git a/inc/plugins/class-dynamic-content.php b/inc/plugins/class-dynamic-content.php index 2bf17a902..588e4e09f 100644 --- a/inc/plugins/class-dynamic-content.php +++ b/inc/plugins/class-dynamic-content.php @@ -92,7 +92,7 @@ public function apply_dynamic_content( $content ) { */ public static function dynamic_content_regex() { // Todo: Improve this Regex, it can't go on for like this. Soon it will be longer than the available space in the universe!!! - return '/[^"\'<>]+)["\']|data-id=["\'](?P[^"\'<>]+)["\']|data-before=["\'](?P[^"\'<>]+)["\']|data-after=["\'](?P[^"\'<>]+)["\']|data-length=["\'](?P[^"\'<>]+)["\']|data-date-type=["\'](?P[^"\'<>]+)["\']|data-date-format=["\'](?P[^"\'<>]+)["\']|data-date-custom=["\'](?P[^"\'<>]+)["\']|data-time-type=["\'](?P[^"\'<>]+)["\']|data-time-format=["\'](?P[^"\'<>]+)["\']|data-time-custom=["\'](?P[^"\'<>]+)["\']|data-term-type=["\'](?P[^"\'<>]+)["\']|data-term-separator=["\'](?P[^"\'<>]+)["\']|data-meta-key=["\'](?P[^"\'<>]+)["\']|data-parameter=["\'](?P[^"\'<>]+)["\']|data-format=["\'](?P[^"\'<>]+)["\']|data-context=["\'](?P[^"\'<>]+)["\']|data-taxonomy=["\'](?P[^"\'<>]+)["\']|[a-zA-Z-]+=["\'][^"\'<>]+["\']))*\s*>(?[^ $].*?)<\s*\/\s*o-dynamic>/'; + return '/[^"\'<>]+)["\']|data-id=["\'](?P[^"\'<>]+)["\']|data-before=["\'](?P[^"\'<>]+)["\']|data-after=["\'](?P[^"\'<>]+)["\']|data-length=["\'](?P[^"\'<>]+)["\']|data-date-type=["\'](?P[^"\'<>]+)["\']|data-date-format=["\'](?P[^"\'<>]+)["\']|data-date-custom=["\'](?P[^"\'<>]+)["\']|data-time-type=["\'](?P[^"\'<>]+)["\']|data-time-format=["\'](?P[^"\'<>]+)["\']|data-time-custom=["\'](?P[^"\'<>]+)["\']|data-term-type=["\'](?P[^"\'<>]+)["\']|data-term-separator=["\'](?P[^"\'<>]+)["\']|data-meta-key=["\'](?P[^"\'<>]+)["\']|data-parameter=["\'](?P[^"\'<>]+)["\']|data-format=["\'](?P[^"\'<>]+)["\']|data-context=["\'](?P[^"\'<>]+)["\']|data-taxonomy=["\'](?P[^"\'<>]+)["\']|data-archive-title-override-prefix=["\'](?P[^"\'<>]+)["\']|data-archive-title-prefix=["\'](?P[^"\'<>]*)["\']|[a-zA-Z-]+=["\'][^"\'<>]+["\']))*\s*>(?[^ $].*?)<\s*\/\s*o-dynamic>/'; } /** @@ -512,7 +512,7 @@ public function get_data( $data, $magic_tags ) { } if ( 'archiveTitle' === $data['type'] ) { - return get_the_archive_title(); + return $this->get_archive_title( $data ); } if ( 'archiveDescription' === $data['type'] ) { @@ -644,6 +644,33 @@ public function get_loggedin_email( $data ) { return esc_html( $email ); } + /** + * Get Archive Title. + * + * @param array< string, mixed > $data Dynamic Data. + * + * @return string + */ + public function get_archive_title( $data ) { + $override = isset( $data['archiveTitleOverridePrefix'] ) ? $data['archiveTitleOverridePrefix'] : ''; + + if ( 'true' !== $override && true !== $override && '1' !== $override ) { + return get_the_archive_title(); + } + + $custom_prefix = isset( $data['archiveTitlePrefix'] ) ? sanitize_text_field( (string) $data['archiveTitlePrefix'] ) : ''; + + $filter = function ( $prefix ) use ( $custom_prefix ) { + return $custom_prefix; + }; + + add_filter( 'get_the_archive_title_prefix', $filter ); + $title = get_the_archive_title(); + remove_filter( 'get_the_archive_title_prefix', $filter ); + + return $title; + } + /** * Get Archive Description. * diff --git a/src/blocks/plugins/dynamic-content/value/fields.js b/src/blocks/plugins/dynamic-content/value/fields.js index 8c2110e99..6f894c06f 100644 --- a/src/blocks/plugins/dynamic-content/value/fields.js +++ b/src/blocks/plugins/dynamic-content/value/fields.js @@ -17,6 +17,7 @@ import { ExternalLink, SelectControl, TextControl, + ToggleControl, PanelBody, Spinner } from '@wordpress/components'; @@ -41,7 +42,8 @@ import { getQueryStringFromObject, setUtm } from '../../../helpers/helper-functi let hasSettingsPanel = [ 'postExcerpt', 'date', - 'time' + 'time', + 'archiveTitle' ]; const dateFormats = { @@ -242,6 +244,29 @@ const Fields = ({ ) } + { 'archiveTitle' === attributes.type && ( + + changeAttributes({ + archiveTitleOverridePrefix: 'true' === attributes.archiveTitleOverridePrefix ? undefined : 'true', + archiveTitlePrefix: 'true' === attributes.archiveTitleOverridePrefix ? undefined : attributes.archiveTitlePrefix + }) } + /> + + { 'true' === attributes.archiveTitleOverridePrefix && ( + changeAttributes({ archiveTitlePrefix }) } + /> + ) } + + ) } + { applyFilters( 'otter.dynamicContent.text.controls', '', attributes, changeAttributes ) } ) } diff --git a/src/blocks/plugins/dynamic-content/value/index.js b/src/blocks/plugins/dynamic-content/value/index.js index 70aa99b1b..4bc4764b0 100644 --- a/src/blocks/plugins/dynamic-content/value/index.js +++ b/src/blocks/plugins/dynamic-content/value/index.js @@ -48,7 +48,9 @@ export const format = { metaKey: 'data-meta-key', parameter: 'data-parameter', format: 'data-format', - taxonomy: 'data-taxonomy' + taxonomy: 'data-taxonomy', + archiveTitleOverridePrefix: 'data-archive-title-override-prefix', + archiveTitlePrefix: 'data-archive-title-prefix' }, edit }; @@ -82,7 +84,7 @@ const withDynamicConditions = createHigherOrderComponent( BlockEdit => { elements.forEach( element => { const context = select( 'core/editor' ).getCurrentPostId(); - const attrs = pick( Object.assign({ context }, element.dataset ), [ 'type', 'context', 'before', 'after', 'length', 'dateType', 'dateFormat', 'dateCustom', 'timeType', 'timeFormat', 'timeCustom', 'termType', 'termSeparator', 'metaKey', 'taxonomy' ]); + const attrs = pick( Object.assign({ context }, element.dataset ), [ 'type', 'context', 'before', 'after', 'length', 'dateType', 'dateFormat', 'dateCustom', 'timeType', 'timeFormat', 'timeCustom', 'termType', 'termSeparator', 'metaKey', 'taxonomy', 'archiveTitleOverridePrefix', 'archiveTitlePrefix' ]); if ( 'postContent' === attrs.type ) { return; diff --git a/tests/test-dynamic-content.php b/tests/test-dynamic-content.php index 8b008c72a..26ada3265 100644 --- a/tests/test-dynamic-content.php +++ b/tests/test-dynamic-content.php @@ -43,6 +43,20 @@ class TestDynamicContent extends WP_UnitTestCase */ protected $post_id; + /** + * The test category ID. + * + * @var int + */ + protected $category_id; + + /** + * The test category slug. + * + * @var string + */ + protected $category_slug; + /** * Set up the test. */ @@ -412,6 +426,38 @@ public function test_archive_title() { $this->assertEquals( 'archiveTitle', $result['type'] ); } + /** + * Test the Archive Title query with the prefix override attributes. + */ + public function test_archive_title_with_prefix_override() { + $archive_title_query = '

Archive Title

'; + + $result = array(); + $num = Dynamic_Content::parse_dynamic_content_query( $archive_title_query, $result ); + $this->assertTrue( boolval( $num ) ); + $result = $result[0]; + + $this->assertEquals( 'archiveTitle', $result['type'] ); + $this->assertEquals( 'true', $result['archiveTitleOverridePrefix'] ); + $this->assertEquals( 'Topic:', $result['archiveTitlePrefix'] ); + } + + /** + * Test the Archive Title query with the override flag enabled and an empty prefix. + */ + public function test_archive_title_with_empty_prefix_override() { + $archive_title_query = '

Archive Title

'; + + $result = array(); + $num = Dynamic_Content::parse_dynamic_content_query( $archive_title_query, $result ); + $this->assertTrue( boolval( $num ) ); + $result = $result[0]; + + $this->assertEquals( 'archiveTitle', $result['type'] ); + $this->assertEquals( 'true', $result['archiveTitleOverridePrefix'] ); + $this->assertSame( '', $result['archiveTitlePrefix'] ); + } + /** * Test the Archive Description query. */ @@ -656,6 +702,82 @@ public function test_author_description_evaluation() { $this->assertEquals( '

', $result ); } + + /** + * Test the Archive Title evaluation with the default WordPress prefix. + */ + public function test_archive_title_evaluation_default_prefix() { + // Navigate to the category archive so get_the_archive_title() resolves. + $this->go_to( get_category_link( $this->category_id ) ); + + $archive_title_query = '

Archive Title

'; + $result = $this->dynamic_content->apply_dynamic_content( $archive_title_query ); + + // Default WordPress prefix for category archives must be preserved. + $this->assertStringContainsString( 'Category:', $result ); + $this->assertStringContainsString( 'Test Category', $result ); + } + + /** + * Test the Archive Title evaluation when the override flag is enabled with a custom prefix. + */ + public function test_archive_title_evaluation_with_custom_prefix() { + // Navigate to the category archive so get_the_archive_title() resolves. + $this->go_to( get_category_link( $this->category_id ) ); + + $archive_title_query = '

Archive Title

'; + $result = $this->dynamic_content->apply_dynamic_content( $archive_title_query ); + + // Custom prefix should replace the default "Category:" prefix. + $this->assertStringContainsString( 'Topic:', $result ); + $this->assertStringNotContainsString( 'Category:', $result ); + $this->assertStringContainsString( 'Test Category', $result ); + } + + /** + * Test the Archive Title evaluation when the override flag is enabled with an empty prefix. + */ + public function test_archive_title_evaluation_with_empty_prefix() { + // Navigate to the category archive so get_the_archive_title() resolves. + $this->go_to( get_category_link( $this->category_id ) ); + + $archive_title_query = '

Archive Title

'; + $result = $this->dynamic_content->apply_dynamic_content( $archive_title_query ); + + // Empty prefix should strip the default WordPress prefix entirely. + $this->assertStringNotContainsString( 'Category:', $result ); + $this->assertStringContainsString( 'Test Category', $result ); + } + + /** + * Test that the Archive Title prefix filter is removed after the dynamic value is rendered. + */ + public function test_archive_title_evaluation_does_not_leak_filter() { + // Navigate to the category archive so get_the_archive_title() resolves. + $this->go_to( get_category_link( $this->category_id ) ); + + $archive_title_query = '

Archive Title

'; + $this->dynamic_content->apply_dynamic_content( $archive_title_query ); + + // After rendering, a subsequent call to get_the_archive_title() must return + // the default WordPress prefix (proving the temporary filter was removed). + $this->assertStringContainsString( 'Category:', get_the_archive_title() ); + } + + /** + * Test that the Archive Title is unchanged when the override flag is not "true". + */ + public function test_archive_title_evaluation_override_flag_off() { + // Navigate to the category archive so get_the_archive_title() resolves. + $this->go_to( get_category_link( $this->category_id ) ); + + // The override flag is missing here, so the custom prefix must be ignored. + $archive_title_query = '

Archive Title

'; + $result = $this->dynamic_content->apply_dynamic_content( $archive_title_query ); + + $this->assertStringContainsString( 'Category:', $result ); + $this->assertStringNotContainsString( 'Topic:', $result ); + } /** * Test multiple dynamic content queries.