mirror of
https://github.com/Automattic/wordpress-activitypub
synced 2024-10-18 14:23:31 +00:00
Support "read more" for Activity-Summary (#856)
* Support "read more" for Activity-Summary * add support for excerpt * use the same function also for the shortcode * add mentions, hashtags and links support to `the_excerpt` * do not make unnecessary extra db calls * fall back to default if options is empty * remove unused hook * some fixes and improvements based on the feedback of @MatzeKitt * add phpdoc * also extract hashtags from excerpt * use filter, so that it is possible to also support mentions and maybe tags in the future * Add PHPDoc * simplify code * fix test
This commit is contained in:
parent
caecaf3c47
commit
7806285d88
8 changed files with 108 additions and 126 deletions
|
@ -28,29 +28,14 @@ class Hashtag {
|
|||
* @return array the activity object array
|
||||
*/
|
||||
public static function filter_activity_object( $object_array ) {
|
||||
if ( empty( $object_array['summary'] ) ) {
|
||||
return $object_array;
|
||||
if ( ! empty( $object_array['summary'] ) ) {
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
}
|
||||
|
||||
\preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/', $object_array['summary'], $matches );
|
||||
foreach ( $matches[0] as $match_id => $match ) {
|
||||
$tag_object = \get_term_by( 'name', $matches[1][ $match_id ], 'post_tag' );
|
||||
if ( ! $tag_object ) {
|
||||
$tag_object = \get_term_by( 'name', $matches[1][ $match_id ], 'category' );
|
||||
}
|
||||
|
||||
if ( $tag_object ) {
|
||||
$link = \get_term_link( $tag_object, 'post_tag' );
|
||||
$object_array['tag'][] = [
|
||||
'type' => 'Hashtag',
|
||||
'href' => $link,
|
||||
'name' => $match,
|
||||
];
|
||||
}
|
||||
if ( ! empty( $object_array['content'] ) ) {
|
||||
$object_array['content'] = self::the_content( $object_array['content'] );
|
||||
}
|
||||
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
|
||||
return $object_array;
|
||||
}
|
||||
|
||||
|
@ -63,12 +48,20 @@ class Hashtag {
|
|||
* @return
|
||||
*/
|
||||
public static function insert_post( $id, $post ) {
|
||||
if ( \preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', $post->post_content, $match ) ) {
|
||||
$tags = \implode( ', ', $match[1] );
|
||||
$tags = array();
|
||||
|
||||
\wp_add_post_tags( $post->post_parent, $tags );
|
||||
if ( \preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', $post->post_content, $match ) ) {
|
||||
$tags = array_merge( $tags, $match[1] );
|
||||
}
|
||||
|
||||
if ( \preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', $post->post_excerpt, $match ) ) {
|
||||
$tags = array_merge( $tags, $match[1] );
|
||||
}
|
||||
|
||||
$tags = \implode( ', ', $tags );
|
||||
|
||||
\wp_add_post_tags( $post->ID, $tags );
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ class Link {
|
|||
* Initialize the class, registering WordPress hooks
|
||||
*/
|
||||
public static function init() {
|
||||
\add_filter( 'activitypub_extra_field_content', array( self::class, 'the_content' ), 10, 1 );
|
||||
\add_filter( 'activitypub_activity_object_array', array( self::class, 'filter_activity_object' ), 99 );
|
||||
}
|
||||
|
||||
|
@ -23,11 +24,13 @@ class Link {
|
|||
* @return array the activity object array
|
||||
*/
|
||||
public static function filter_activity_object( $object_array ) {
|
||||
if ( empty( $object_array['summary'] ) ) {
|
||||
return $object_array;
|
||||
if ( ! empty( $object_array['summary'] ) ) {
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
}
|
||||
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
if ( ! empty( $object_array['content'] ) ) {
|
||||
$object_array['content'] = self::the_content( $object_array['content'] );
|
||||
}
|
||||
|
||||
return $object_array;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class Mention {
|
|||
public static function init() {
|
||||
\add_filter( 'the_content', array( self::class, 'the_content' ), 99, 1 );
|
||||
\add_filter( 'comment_text', array( self::class, 'the_content' ), 10, 1 );
|
||||
\add_filter( 'activitypub_extra_field_content', array( self::class, 'the_content' ), 10, 1 );
|
||||
\add_filter( 'activitypub_extract_mentions', array( self::class, 'extract_mentions' ), 99, 2 );
|
||||
\add_filter( 'activitypub_activity_object_array', array( self::class, 'filter_activity_object' ), 99 );
|
||||
}
|
||||
|
@ -32,11 +33,13 @@ class Mention {
|
|||
* @return array the activity object array
|
||||
*/
|
||||
public static function filter_activity_object( $object_array ) {
|
||||
if ( empty( $object_array['summary'] ) ) {
|
||||
return $object_array;
|
||||
if ( ! empty( $object_array['summary'] ) ) {
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
}
|
||||
|
||||
$object_array['summary'] = self::the_content( $object_array['summary'] );
|
||||
if ( ! empty( $object_array['content'] ) ) {
|
||||
$object_array['content'] = self::the_content( $object_array['content'] );
|
||||
}
|
||||
|
||||
return $object_array;
|
||||
}
|
||||
|
@ -145,6 +148,6 @@ class Mention {
|
|||
$mentions[ $match ] = $link;
|
||||
}
|
||||
}
|
||||
return $mentions;
|
||||
return \array_unique( $mentions );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace Activitypub;
|
||||
|
||||
use function Activitypub\esc_hashtag;
|
||||
use function Activitypub\generate_post_summary;
|
||||
|
||||
class Shortcodes {
|
||||
/**
|
||||
|
@ -108,81 +109,7 @@ class Shortcodes {
|
|||
$excerpt_length = ACTIVITYPUB_EXCERPT_LENGTH;
|
||||
}
|
||||
|
||||
$excerpt = \get_post_field( 'post_excerpt', $item );
|
||||
|
||||
if ( 'attachment' === $item->post_type ) {
|
||||
// get title of attachment with fallback to alt text.
|
||||
$content = wp_get_attachment_caption( $item->ID );
|
||||
if ( empty( $content ) ) {
|
||||
$content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true );
|
||||
}
|
||||
} elseif ( '' === $excerpt ) {
|
||||
$content = \get_post_field( 'post_content', $item );
|
||||
|
||||
// An empty string will make wp_trim_excerpt do stuff we do not want.
|
||||
if ( '' !== $content ) {
|
||||
$excerpt = \strip_shortcodes( $content );
|
||||
|
||||
/** This filter is documented in wp-includes/post-template.php */
|
||||
$excerpt = \apply_filters( 'the_content', $excerpt );
|
||||
$excerpt = \str_replace( ']]>', ']]>', $excerpt );
|
||||
}
|
||||
}
|
||||
|
||||
// Strip out any remaining tags.
|
||||
$excerpt = \wp_strip_all_tags( $excerpt );
|
||||
|
||||
$excerpt_more = \apply_filters( 'activitypub_excerpt_more', ' […]' );
|
||||
$excerpt_more_len = strlen( $excerpt_more );
|
||||
|
||||
// We now have a excerpt, but we need to check it's length, it may be longer than we want for two reasons:
|
||||
//
|
||||
// * The user has entered a manual excerpt which is longer that what we want.
|
||||
// * No manual excerpt exists so we've used the content which might be longer than we want.
|
||||
//
|
||||
// Either way, let's trim it up if we need too. Also, don't forget to take into account the more indicator
|
||||
// as part of the total length.
|
||||
//
|
||||
|
||||
// Setup a variable to hold the current excerpts length.
|
||||
$current_excerpt_length = strlen( $excerpt );
|
||||
|
||||
// Setup a variable to keep track of our target length.
|
||||
$target_excerpt_length = $excerpt_length - $excerpt_more_len;
|
||||
|
||||
// Setup a variable to keep track of the current max length.
|
||||
$current_excerpt_max = $target_excerpt_length;
|
||||
|
||||
// This is a loop since we can't calculate word break the string after 'the_excpert' filter has run (we would break
|
||||
// all kinds of html tags), so we have to cut the excerpt down a bit at a time until we hit our target length.
|
||||
while ( $current_excerpt_length > $target_excerpt_length && $current_excerpt_max > 0 ) {
|
||||
// Trim the excerpt based on wordwrap() positioning.
|
||||
// Note: we're using <br> as the linebreak just in case there are any newlines existing in the excerpt from the user.
|
||||
// There won't be any <br> left after we've run wp_strip_all_tags() in the code above, so they're
|
||||
// safe to use here. It won't be included in the final excerpt as the substr() will trim it off.
|
||||
$excerpt = substr( $excerpt, 0, strpos( wordwrap( $excerpt, $current_excerpt_max, '<br>' ), '<br>' ) );
|
||||
|
||||
// If something went wrong, or we're in a language that wordwrap() doesn't understand,
|
||||
// just chop it off and don't worry about breaking in the middle of a word.
|
||||
if ( strlen( $excerpt ) > $excerpt_length - $excerpt_more_len ) {
|
||||
$excerpt = substr( $excerpt, 0, $current_excerpt_max );
|
||||
}
|
||||
|
||||
// Add in the more indicator.
|
||||
$excerpt = $excerpt . $excerpt_more;
|
||||
|
||||
// Run it through the excerpt filter which will add some html tags back in.
|
||||
$excerpt_filtered = apply_filters( 'the_excerpt', $excerpt );
|
||||
|
||||
// Now set the current excerpt length to this new filtered length.
|
||||
$current_excerpt_length = strlen( $excerpt_filtered );
|
||||
|
||||
// Check to see if we're over the target length.
|
||||
if ( $current_excerpt_length > $target_excerpt_length ) {
|
||||
// If so, remove 20 characters from the current max and run the loop again.
|
||||
$current_excerpt_max = $current_excerpt_max - 20;
|
||||
}
|
||||
}
|
||||
$excerpt = generate_post_summary( $item, $excerpt_length );
|
||||
|
||||
return \apply_filters( 'the_excerpt', $excerpt );
|
||||
}
|
||||
|
|
|
@ -37,6 +37,13 @@ class Extra_Fields {
|
|||
return apply_filters( 'activitypub_get_actor_extra_fields', $fields, $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the Extra Fields (Cutom Post Types) to ActivityPub Actor-Attachments.
|
||||
*
|
||||
* @param \WP_Post[] $fields The extra fields.
|
||||
*
|
||||
* @return array ActivityPub attachments.
|
||||
*/
|
||||
public static function fields_to_attachments( $fields ) {
|
||||
$attachments = array();
|
||||
\add_filter(
|
||||
|
@ -50,7 +57,6 @@ class Extra_Fields {
|
|||
|
||||
foreach ( $fields as $post ) {
|
||||
$content = \get_the_content( null, false, $post );
|
||||
$content = Link::the_content( $content, true );
|
||||
$content = \do_blocks( $content );
|
||||
$content = \wptexturize( $content );
|
||||
$content = \wp_filter_content_tags( $content );
|
||||
|
@ -58,6 +64,7 @@ class Extra_Fields {
|
|||
$content = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $content );
|
||||
$content = \strip_shortcodes( $content );
|
||||
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
|
||||
$content = \apply_filters( 'activitypub_extra_field_content', $content, $post );
|
||||
|
||||
$attachments[] = array(
|
||||
'type' => 'PropertyValue',
|
||||
|
|
|
@ -1116,3 +1116,62 @@ function enrich_content_data( $content, $regex, $regex_callback ) {
|
|||
|
||||
return $content_with_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a summary of a post.
|
||||
*
|
||||
* This function generates a summary of a post by extracting:
|
||||
*
|
||||
* 1. The post excerpt if it exists.
|
||||
* 2. The first part of the post content if it contains the <!--more--> tag.
|
||||
* 3. An excerpt of the post content if it is longer than the specified length.
|
||||
*
|
||||
* @param int|WP_Post $post The post ID or post object.
|
||||
* @param integer $length The maximum length of the summary.
|
||||
* Default is 500. It will ne ignored if the post excerpt
|
||||
* and the content above the <!--more--> tag.
|
||||
*
|
||||
* @return string The generated post summary.
|
||||
*/
|
||||
function generate_post_summary( $post, $length = 500 ) {
|
||||
$post = get_post( $post );
|
||||
|
||||
if ( ! $post ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = \sanitize_post_field( 'post_excerpt', $post->post_excerpt, $post->ID );
|
||||
|
||||
if ( $content ) {
|
||||
return \apply_filters( 'the_excerpt', $content );
|
||||
}
|
||||
|
||||
$content = \sanitize_post_field( 'post_content', $post->post_content, $post->ID );
|
||||
$content_parts = \get_extended( $content );
|
||||
|
||||
$excerpt_more = \apply_filters( 'activitypub_excerpt_more', '[…]' );
|
||||
$length = $length - strlen( $excerpt_more );
|
||||
|
||||
// Check for the <!--more--> tag.
|
||||
if (
|
||||
! empty( $content_parts['extended'] ) &&
|
||||
! empty( $content_parts['main'] )
|
||||
) {
|
||||
$content = $content_parts['main'] . ' ' . $excerpt_more;
|
||||
$length = null;
|
||||
}
|
||||
|
||||
$content = \html_entity_decode( $content );
|
||||
$content = \wp_strip_all_tags( $content );
|
||||
$content = \trim( $content );
|
||||
$content = \preg_replace( '/\R+/m', "\n\n", $content );
|
||||
$content = \preg_replace( '/[\r\t]/', '', $content );
|
||||
|
||||
if ( $length && \strlen( $content ) > $length ) {
|
||||
$content = \wordwrap( $content, $length, '</activitypub-summary>' );
|
||||
$content = \explode( '</activitypub-summary>', $content, 2 );
|
||||
$content = $content[0] . ' ' . $excerpt_more;
|
||||
}
|
||||
|
||||
return \apply_filters( 'the_excerpt', $content );
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use Activitypub\Collection\Users;
|
|||
use function Activitypub\esc_hashtag;
|
||||
use function Activitypub\is_single_user;
|
||||
use function Activitypub\get_enclosures;
|
||||
use function Activitypub\generate_post_summary;
|
||||
use function Activitypub\get_rest_url_by_path;
|
||||
use function Activitypub\site_supports_blocks;
|
||||
|
||||
|
@ -733,24 +734,7 @@ class Post extends Base {
|
|||
return \__( '(This post is being modified)', 'activitypub' );
|
||||
}
|
||||
|
||||
$content = \get_post_field( 'post_content', $this->wp_object->ID );
|
||||
$content = \html_entity_decode( $content );
|
||||
$content = \wp_strip_all_tags( $content );
|
||||
$content = \trim( $content );
|
||||
$content = \preg_replace( '/\R+/m', "\n\n", $content );
|
||||
$content = \preg_replace( '/[\r\t]/', '', $content );
|
||||
|
||||
$excerpt_more = \apply_filters( 'activitypub_excerpt_more', '[...]' );
|
||||
$length = 500;
|
||||
$length = $length - strlen( $excerpt_more );
|
||||
|
||||
if ( \strlen( $content ) > $length ) {
|
||||
$content = \wordwrap( $content, $length, '</activitypub-summary>' );
|
||||
$content = \explode( '</activitypub-summary>', $content, 2 );
|
||||
$content = $content[0];
|
||||
}
|
||||
|
||||
return $content . ' ' . $excerpt_more;
|
||||
return generate_post_summary( $this->wp_object );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -849,7 +833,8 @@ class Post extends Base {
|
|||
$template = "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
|
||||
break;
|
||||
default:
|
||||
$template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
|
||||
// phpcs:ignore Universal.Operators.DisallowShortTernary.Found
|
||||
$template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ) ?: ACTIVITYPUB_CUSTOM_POST_CONTENT;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -868,7 +853,12 @@ class Post extends Base {
|
|||
* @return array The list of @-Mentions.
|
||||
*/
|
||||
protected function get_mentions() {
|
||||
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->post_content, $this->wp_object );
|
||||
return apply_filters(
|
||||
'activitypub_extract_mentions',
|
||||
array(),
|
||||
$this->wp_object->post_content . ' ' . $this->wp_object->post_excerpt,
|
||||
$this->wp_object
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -89,7 +89,7 @@ class Test_Activitypub_Shortcodes extends WP_UnitTestCase {
|
|||
$content = do_shortcode( $content );
|
||||
wp_reset_postdata();
|
||||
|
||||
$this->assertEquals( "<p>Lorem ipsum […]</p>\n", $content );
|
||||
$this->assertEquals( "<p>Lorem ipsum dolor […]</p>\n", $content );
|
||||
Shortcodes::unregister();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue