Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for anchor links #1044

Merged
merged 40 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ec4f4b8
Refactor parser class for stricter type declarations and efficiency
attackant Jan 15, 2024
2215a05
Refactor HTML cleaning in class-parser.php for clarity and improved f…
attackant Jan 15, 2024
03248b3
Add namespaces and improve code readability in Class_Body
attackant Jan 16, 2024
2a5bd6c
Refactor class-apple-news.php with strong types and fix bug where is_…
attackant Jan 16, 2024
c94a296
Formatting only
attackant Jan 16, 2024
5cd762d
Initialize $is_initialized to false and refactor settings check to fi…
attackant Jan 16, 2024
274192d
Improve Apple News push action error handling
attackant Jan 16, 2024
49c4d91
Remove anchor link handling from HTML parser
attackant Jan 16, 2024
64c0ae5
Update anchor tag and corresponding link handling in tests
attackant Jan 16, 2024
e4e29c8
Formatting, type initialization, readabilty changes
attackant Jan 16, 2024
23c4585
Add TODO, remove type hints
attackant Jan 16, 2024
b6370bf
Refactor link handling in test code
attackant Jan 16, 2024
79651db
phpcbf and fix array to string conversion error, add error handling f…
attackant Jan 16, 2024
a568529
Fix for non-static method being called statically, misc cleanup
attackant Jan 16, 2024
e702ca1
Roll back some type definitions in public methods
attackant Jan 16, 2024
62c698c
Cleanup and formatting
attackant Jan 16, 2024
a092cdb
PHPCS fixes
attackant Jan 17, 2024
61bc2c7
Add punctuation to comment in class-body.php
attackant Jan 17, 2024
f85f059
Add 'id' attribute permission to HTML tags in class-html.php
attackant Jan 17, 2024
6a3359f
Remove type hinting and refactor methods in class-parser.php
attackant Jan 17, 2024
00f8ca1
Correct TODO comments in class-request.php
attackant Jan 17, 2024
0a0f4da
Update allowed HTML tags' attributes in class-component
attackant Jan 17, 2024
fd244b1
Fix register_spec call
attackant Jan 17, 2024
d2505aa
Introduce HTML id to identifier conversion
attackant Jan 17, 2024
1697513
Refactor input types in Apple News class methods
attackant Jan 17, 2024
ed55ba8
Update duplicate identifier handling logic
attackant Jan 17, 2024
f7b02b0
Remove duplicate identifier handling and improve text flow
attackant Jan 17, 2024
70ea301
Simplify identifier handling in class-components.php
attackant Jan 17, 2024
c6360cf
Modify regular expression for extracting URLs in the function handle_…
attackant Jan 17, 2024
4c16ce1
Replace string comparison with empty() function in class-component.php
attackant Jan 17, 2024
b0a94e3
Fix typo
attackant Jan 17, 2024
2422e38
Start PR
attackant Jan 17, 2024
b1f25bf
phpcbf only
attackant Jan 17, 2024
27ce636
Refactor code for apple exporter header class
attackant Jan 17, 2024
8e38b31
phpcbf only
attackant Jan 18, 2024
e2cecf0
Add ID to heading in tests
attackant Jan 19, 2024
df57a19
Add ID identifier to heading component
attackant Jan 19, 2024
208774b
phpcbf formatting
attackant Jan 22, 2024
f310c10
Fix regex to so IDs are optional and update matches variables. Thank …
attackant Jan 22, 2024
c5afbf5
Merge pull request #1045 from alleyinteractive/issue-1039-b
attackant Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions admin/apple-actions/index/class-export.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use Apple_Exporter\Theme;
use Apple_Exporter\Third_Party\Jetpack_Tiled_Gallery;
use Apple_News;
use Apple_News\Admin\Automation;
use BC_Setup;

/**
* A class to handle an export request from the admin.
Expand Down Expand Up @@ -140,7 +140,7 @@ public function fetch_exporter() {
}
if ( ! empty( $post_thumb_url ) ) {
// If the post thumb URL is root-relative, convert it to fully-qualified.
if ( 0 === strpos( $post_thumb_url, '/' ) ) {
if ( str_starts_with( $post_thumb_url, '/' ) ) {
$post_thumb_url = site_url( $post_thumb_url );
}

Expand Down Expand Up @@ -185,7 +185,7 @@ public function fetch_exporter() {
if ( ! empty( $excerpt ) ) {
$content_normalized = strtolower( str_replace( ' ', '', wp_strip_all_tags( $content ) ) );
$excerpt_normalized = strtolower( str_replace( ' ', '', wp_strip_all_tags( $excerpt ) ) );
if ( false !== strpos( $content_normalized, $excerpt_normalized ) ) {
if ( str_contains( $content_normalized, $excerpt_normalized ) ) {
$excerpt = '';
}
}
Expand Down Expand Up @@ -247,7 +247,7 @@ public function fetch_exporter() {
*
* @since 2.3.0
*
* @param string $ date The date for the post.
* @param string $date The date for the post.
* @param int $post_id The ID of the post.
*/
$date = apply_filters( 'apple_news_exporter_date', $date, $post->ID );
Expand Down Expand Up @@ -545,7 +545,7 @@ private function get_brightcove_stillurl( $account_id, $video_id ) {
* hook.
*/
if ( ! class_exists( '\BC_CMS_API' ) && class_exists( '\BC_Setup' ) ) {
\BC_Setup::action_init();
( new BC_Setup() )->action_init();
}

// Ensure the account ID and video IDs are strings.
Expand Down Expand Up @@ -630,6 +630,7 @@ private function remove_tags( $html ) {
* Filter the content for markdown format.
*
* @param string $content The content to be filtered.
*
* @access private
* @return string
*/
Expand All @@ -645,14 +646,14 @@ private function remove_entities( $content ) {
/**
* Loads settings for the Exporter_Content from the WordPress post metadata.
*
* @since 0.4.0
* @return Settings
* @return Exporter_Content_Settings
* @access private
* @since 0.4.0
*/
private function fetch_content_settings() {
$settings = new Exporter_Content_Settings();
foreach ( get_post_meta( $this->id ) as $name => $value ) {
if ( 0 === strpos( $name, 'apple_news_' ) ) {
if ( str_starts_with( $name, 'apple_news_' ) ) {
$name = str_replace( 'apple_news_', '', $name );
$value = $value[0];
$settings->set( $name, $value );
Expand All @@ -665,15 +666,15 @@ private function fetch_content_settings() {
* Before this filter is called, the Exporter_Content_Settings object is
* initialized and merged with settings stored in postmeta for this post.
*
* @param Apple_Exporter\Exporter_Content_Settings $settings The content settings for this article.
* @param Exporter_Content_Settings $settings The content settings for this article.
*/
return apply_filters( 'apple_news_content_settings', $settings );
}

/**
* Sets the active theme for this session if explicitly set or mapped.
*/
private function set_theme() {
private function set_theme(): void {
$active_theme = Theme::get_active_theme_name();

/**
Expand Down
2 changes: 1 addition & 1 deletion admin/apple-actions/index/class-push.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ private function push( $user_id = null ) {

$this->clean_workspace();

if ( preg_match( '#WRONG_REVISION#', $e->getMessage() ) ) {
if ( str_contains( $e->getMessage(), 'WRONG_REVISION' ) ) {
throw new \Apple_Actions\Action_Exception( esc_html__( 'Apple News Error: It seems like the article was updated by another call. If the problem persists, try removing and pushing again.', 'apple-news' ) );
} else {
throw new \Apple_Actions\Action_Exception( esc_html__( 'There has been an error with the Apple News API: ', 'apple-news' ) . esc_html( $e->getMessage() ) );
Expand Down
111 changes: 100 additions & 11 deletions includes/apple-exporter/builders/class-components.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
use Apple_Exporter\Component_Factory;
use Apple_Exporter\Components\Component;
use Apple_Exporter\Components\Image;
use Apple_Exporter\Workspace;
use Apple_Exporter\Theme;
use Apple_Exporter\Workspace;
use Apple_News;
use DOMElement;
use DOMNode;

/**
Expand Down Expand Up @@ -67,6 +68,36 @@ protected function build() {
// Group body components to improve text flow at all orientations.
$components = $this->group_body_components( $components );

// Remove any identifiers that are duplicated.
$components = $this->remove_duplicate_identifiers( $components );

return $components;
}

/**
* Strip duplicated identifiers from components, leaving the component.
*
* @param array $components The array of components to remove duplicate identifiers from.
*
* @return array The updated array of components with duplicate identifiers removed.
*/
protected function remove_duplicate_identifiers( array $components ): array {
$identifiers = [];
foreach ( $components as $i => $component ) {
if ( ! empty( $component['identifier'] ) ) {
if ( in_array( $component['identifier'], $identifiers, true ) ) {
unset( $components[ $i ]['identifier'] );
} else {
$identifiers[] = $component['identifier'];
}
}

// If the component contains nested components, process them as well.
if ( isset( $component['components'] ) && is_array( $component['components'] ) ) {
$components[ $i ]['components'] = $this->remove_duplicate_identifiers( $component['components'] );
}
}

return $components;
}

Expand Down Expand Up @@ -130,7 +161,7 @@ private function add_pullquote_if_needed( &$components ) {
private function add_thumbnail_if_needed( &$components ) {

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

// Otherwise, iterate over the components and look for the first image.
foreach ( $components as $i => $component ) {
Expand Down Expand Up @@ -288,7 +319,7 @@ private function anchor_components( &$components ) {
private function anchor_lines_coefficient() {

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

return ceil( 18 / $theme->get_value( 'body_size' ) * 18 );
}
Expand All @@ -309,7 +340,7 @@ private function anchor_together( $component, $target_component ) {
}

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

// Get the component's anchor settings, if set.
$anchor_json = $component->get_json( 'anchor' );
Expand Down Expand Up @@ -364,7 +395,7 @@ private function anchor_together( $component, $target_component ) {
private function characters_per_line_anchored() {

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

// Get the body text size in points.
$body_size = $theme->get_value( 'body_size' );
Expand Down Expand Up @@ -426,10 +457,69 @@ private function clean_up_components( &$component ) {
return;
}

// Convert HTML IDs to identifiers.
$component = $this->convert_ids_to_identifiers( $component );

// Trim the fat.
$component['text'] = trim( $component['text'] );
}

/**
* Convert the 'id' attributes in the HTML text of a
* component to unique identifiers to support internal
* anchor links.
*
* @param Component $component The component whose 'id' attributes will be converted.
*
* @return Component The component with converted 'id' attributes.
* @since 2.4.4
*
* @access private
*/
private function convert_ids_to_identifiers( &$component ) {
// Dictionary to hold identifiers as keys with value the number of times each is found.
$identifiers = [];

// Searching for 'id' in the HTML and removing the attribute
// and store (valid) ones as 'identifier' on the component.
$component['text'] = preg_replace_callback(
'/\bid=["\'](.*?)["\']/',
function ( $matches ) use ( &$component, &$identifiers ) {
// If 'id' starts with a digit, it's skipped,
// as it's not a valid identifier and Apple News
// will reject it.
if ( preg_match( '/^\d/', $matches[1] ) ) {
return '';
}

// Saving the 'id' as the 'identifier'.
$identifier = $matches[1];

// If this identifier already exists skip it (is a duplicate).
if ( isset( $identifiers[ $identifier ] ) ) {
return '';
} else {
// If this is the first time we've encountered this identifier,
// add it to our dictionary.
$identifiers[ $identifier ] = true;
}

// Add 'identifier' to the component.
$component['identifier'] = $identifier;

// Returning an empty string to remove the
// 'id' attribute from the HTML.
return '';
},
$component['text']
);

// Remove unnecessary whitespaces in the HTML tags.
$component['text'] = preg_replace( '/\s*>/', '>', $component['text'] );

return $component;
}

/**
* Given an anchored component, estimate the minimum number of lines it occupies.
*
Expand Down Expand Up @@ -506,7 +596,7 @@ private function get_component_from_shortname( $shortname, $html = null ) {
/**
* Get a component from a node.
*
* @param \DOMElement $node The node to be examined.
* @param DOMElement $node The node to be examined.
*
* @access private
* @return array An array of components matching the node.
Expand Down Expand Up @@ -573,7 +663,7 @@ private function get_image_full_size_url( $url ) {
* @since 1.2.1
*
* @access private
* @return float An image ratio (width/height) for the given image.
* @return float|int An image ratio (width/height) for the given image.
*/
private function get_image_ratio( $url ) {

Expand Down Expand Up @@ -615,11 +705,10 @@ private function group_body_components( $components ) {
$new_components = [];
$cover_index = null;
$anchor_buffer = 0;
$prev = null;
$current = null;

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

// Loop through components, grouping as necessary.
foreach ( $components as $component ) {
Expand Down Expand Up @@ -746,7 +835,7 @@ private function group_body_components( $components ) {
private function meta_components() {

// Get information about the currently loaded theme.
$theme = \Apple_Exporter\Theme::get_used();
$theme = Theme::get_used();

// Attempt to get the component order.
$meta_component_order = $theme->get_value( 'meta_component_order' );
Expand Down Expand Up @@ -798,7 +887,7 @@ private function split_into_components() {

/**
* Loop though the first-level nodes of the body element. Components might
* include child-components, like an Cover and Image.
* include child-components, like a Cover and Image.
*/
$components = [];
foreach ( $this->content_nodes() as $node ) {
Expand Down
61 changes: 31 additions & 30 deletions includes/apple-exporter/class-html.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,39 @@ class HTML {
* @access private
* @var array
*/
private $allowed_html = [
private array $allowed_html = [
'a' => [
'href' => true,
'id' => true,
],
'aside' => [],
'b' => [],
'blockquote' => [],
'br' => [],
'caption' => [],
'cite' => [],
'code' => [],
'del' => [],
'em' => [],
'footer' => [],
'i' => [],
'li' => [],
'ol' => [],
'p' => [],
'pre' => [],
's' => [],
'samp' => [],
'strong' => [],
'sub' => [],
'sup' => [],
'table' => [],
'td' => [],
'th' => [],
'tr' => [],
'tbody' => [],
'thead' => [],
'tfoot' => [],
'ul' => [],
'aside' => [ 'id' => true ],
'b' => [ 'id' => true ],
'blockquote' => [ 'id' => true ],
'br' => [ 'id' => true ],
'caption' => [ 'id' => true ],
'cite' => [ 'id' => true ],
'code' => [ 'id' => true ],
'del' => [ 'id' => true ],
'em' => [ 'id' => true ],
'footer' => [ 'id' => true ],
'i' => [ 'id' => true ],
'li' => [ 'id' => true ],
'ol' => [ 'id' => true ],
'p' => [ 'id' => true ],
'pre' => [ 'id' => true ],
's' => [ 'id' => true ],
'samp' => [ 'id' => true ],
'strong' => [ 'id' => true ],
'sub' => [ 'id' => true ],
'sup' => [ 'id' => true ],
'table' => [ 'id' => true ],
'td' => [ 'id' => true ],
'th' => [ 'id' => true ],
'tr' => [ 'id' => true ],
'tbody' => [ 'id' => true ],
'thead' => [ 'id' => true ],
'tfoot' => [ 'id' => true ],
'ul' => [ 'id' => true ],
];

/**
Expand All @@ -74,7 +75,7 @@ public function format( $html ) {
// Strip out all tags and attributes other than what is allowed.
$html = wp_kses( $html, $this->allowed_html );

// Remove any tempty tags.
// Remove any empty tags.
$html = preg_replace( '/<([a-z0-9]+)[^>]*>\s*<\/\1>/', '', $html );

return $html;
Expand Down
Loading