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

Enable Mastodon Apps: support profile editing, blog user #788

Merged
merged 42 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
71fc2c8
Support for `api/v1/accounts/update_credentials` route
mattwiebe Jun 23, 2024
29db809
Properly integrate with `mastodon_api_mapback_user_id`
mattwiebe Jun 25, 2024
8ef9566
s/set/save/_
mattwiebe Jun 25, 2024
6fbc881
Only set properties allowed by `Account`
mattwiebe Jun 26, 2024
0737a74
better user -> blog ID conversion strategy
mattwiebe Jun 26, 2024
a33cbd2
Consolidate info single `save()` function
mattwiebe Jun 26, 2024
9195851
Better update handling
mattwiebe Jun 26, 2024
0813733
account for site_(icon|logo) duality
mattwiebe Jun 26, 2024
9d33f3d
Populate note in source to unlock profile editing
mattwiebe Jun 26, 2024
59fe2eb
Adjust to filter approach
mattwiebe Jun 26, 2024
394063d
no longer using Blog class
mattwiebe Jun 27, 2024
daa12f7
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Jul 1, 2024
c624802
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Jul 8, 2024
c371b97
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Jul 9, 2024
074684f
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Jul 18, 2024
a86d145
fix blog user
pfefferle Jul 18, 2024
278a1b2
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Jul 18, 2024
9e615cc
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Jul 19, 2024
ba9c3f0
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Jul 24, 2024
2950898
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Jul 25, 2024
a19f59b
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Jul 29, 2024
f1eb441
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Jul 30, 2024
9607002
fix bad merge
mattwiebe Jul 30, 2024
0367637
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Aug 2, 2024
d357d86
Use current header and icon locations
mattwiebe Aug 2, 2024
6803bdb
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Aug 9, 2024
c4b65bc
move all `save(name)` functions to discrete `update_name` functions
mattwiebe Aug 9, 2024
864cb01
Handle submitted `fields_attributes` aks Extra Fields
mattwiebe Aug 12, 2024
aa4b568
handle deletes, really use the incoming fields
mattwiebe Aug 12, 2024
03f23d1
publish new posts
mattwiebe Aug 12, 2024
a2bed42
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Aug 14, 2024
5bb67b4
only make blocks for block-supporting sites
mattwiebe Aug 14, 2024
750b039
moar site_supports_blocks
mattwiebe Aug 15, 2024
287daec
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Sep 13, 2024
ce2316d
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Sep 16, 2024
d4084d6
proper EMA loading
mattwiebe Sep 16, 2024
404588b
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Sep 17, 2024
fcdf8e1
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Sep 17, 2024
365af49
move EMA extra fields functions into EMA class
mattwiebe Sep 17, 2024
f5a55b4
remove mastodon 4 field notice
mattwiebe Sep 20, 2024
d022392
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
mattwiebe Sep 20, 2024
a600761
Merge branch 'master' into add/enable-mastodon-apps-profile-editing
pfefferle Sep 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
6 changes: 5 additions & 1 deletion includes/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ public static function admin_notices() {
if ( 'edit' === $current_screen->base && Extra_Fields::is_extra_fields_post_type( $current_screen->post_type ) ) {
?>
<div class="notice" style="margin: 0; background: none; border: none; box-shadow: none; padding: 15px 0 0 0; font-size: 14px;">
<?php esc_html_e( 'These are extra fields that are used for your ActivityPub profile. You can use your homepage, social profiles, pronouns, age, anything you want.', 'activitypub' ); ?>
<?php
esc_html_e( 'These are extra fields that are used for your ActivityPub profile. You can use your homepage, social profiles, pronouns, age, anything you want.', 'activitypub' );
echo '<br />';
esc_html_e( 'Note that Mastodon clients will only show four fields.', 'activitypub' );
Copy link
Member

@pfefferle pfefferle Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not completely correct! You can only add 4 extra fields through the Mastodon frontend, but it displays way more than these 4 when a different platform supports/provides it.

See my blog as example:

Screenshot 2024-09-20 at 09 40 50 Screenshot 2024-09-20 at 09 44 26

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find, I foolishly believed something I read in an API doc rather than testing things :D

?>
</div>
<?php
}
Expand Down
93 changes: 79 additions & 14 deletions includes/collection/class-extra-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use WP_Query;
use Activitypub\Collection\Users;

use function Activitypub\site_supports_blocks;

class Extra_Fields {

const USER_POST_TYPE = 'ap_extrafield';
Expand Down Expand Up @@ -37,6 +39,23 @@ public static function get_actor_fields( $user_id ) {
return apply_filters( 'activitypub_get_actor_extra_fields', $fields, $user_id );
}

private static function get_formatted_content( $post ) {
$content = \get_the_content( null, false, $post );
$content = Link::the_content( $content, true );
if ( site_supports_blocks() ) {
$content = \do_blocks( $content );
}
$content = \wptexturize( $content );
$content = \wp_filter_content_tags( $content );
// replace script and style elements
$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 );

return $content;
}

/**
* Transforms the Extra Fields (Cutom Post Types) to ActivityPub Actor-Attachments.
*
Expand All @@ -56,16 +75,7 @@ function( $rel ) {
);

foreach ( $fields as $post ) {
$content = \get_the_content( null, false, $post );
$content = \do_blocks( $content );
$content = \wptexturize( $content );
$content = \wp_filter_content_tags( $content );
// replace script and style elements
$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 );

$content = self::get_formatted_content( $post );
$attachments[] = array(
'type' => 'PropertyValue',
'name' => \get_the_title( $post ),
Expand Down Expand Up @@ -211,10 +221,7 @@ function( $rel ) {
'post_title' => $title,
'post_status' => 'publish',
'post_author' => $user_id,
'post_content' => sprintf(
'<!-- wp:paragraph --><p>%s</p><!-- /wp:paragraph -->',
Link::the_content( $url )
),
'post_content' => self::make_paragraph_block( Link::the_content( $url ) ),
'comment_status' => 'closed',
'menu_order' => $menu_order,
);
Expand All @@ -231,6 +238,64 @@ function( $rel ) {
return $extra_fields;
}

public static function get_extra_fields_for_mastodon_api( $user_id ) {
$ret = array();
$fields = self::get_actor_fields( $user_id );

foreach ( $fields as $field ) {
$ret[] = array(
'name' => $field->post_title,
'value' => self::get_formatted_content( $field ),
);
}

return $ret;
}

private static function make_paragraph_block( $content ) {
if ( ! site_supports_blocks() ) {
return $content;
}
return '<!-- wp:paragraph --><p>' . $content . '</p><!-- /wp:paragraph -->';
}

public static function set_extra_fields_from_mastodon_api( $user_id, $fields ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have all "enable mastodon apps" functions in the integration file and not mix them up with the "core" files!?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup sounds good to me

// The Mastodon API submits a simple hash, every field.
// We can reasonably assume a similar order for our operations below.
$ids = wp_list_pluck( self::get_actor_fields( $user_id ), 'ID' );
$is_blog = self::is_blog( $user_id );
$post_type = $is_blog ? self::BLOG_POST_TYPE : self::USER_POST_TYPE;

foreach ( $fields as $i => $field ) {
$post_id = $ids[ $i ] ?? null;
$has_post = $post_id && \get_post( $post_id );
$args = array(
'post_title' => $field['name'],
'post_content' => self::make_paragraph_block( $field['value'] ),
);

if ( $has_post ) {
$args['ID'] = $ids[ $i ];
\wp_update_post( $args );
} else {
$args['post_type'] = $post_type;
$args['post_status'] = 'publish';
if ( ! $is_blog ) {
$args['post_author'] = $user_id;
}
\wp_insert_post( $args );
}
}

// Delete any remaining fields.
if ( \count( $fields ) < \count( $ids ) ) {
$to_delete = \array_slice( $ids, \count( $fields ) );
foreach ( $to_delete as $id ) {
\wp_delete_post( $id, true );
}
}
}

/**
* Checks if the user is the blog user.
* @param int $user_id The user ID.
Expand Down
54 changes: 50 additions & 4 deletions includes/model/class-blog.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ public function get_preferred_username() {
* @return array The User-Icon.
*/
public function get_icon() {
// try site icon first
$icon_id = get_option( 'site_icon' );
// try site_logo, falling back to site_icon, first
$icon_id = get_option( 'site_logo', get_option( 'site_icon' ) );

// try custom logo second
if ( ! $icon_id ) {
Expand Down Expand Up @@ -390,11 +390,57 @@ public function get_indexable() {
}

/**
* Get the User-Hashtags.
* Update the User-Name.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_name( $value ) {
return \update_option( 'blogname', $value );
}

/**
* Update the User-Description.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_summary( $value ) {
return \update_option( 'blogdescription', $value );
}

/**
* Update the User-Icon.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_icon( $value ) {
if ( ! wp_attachment_is_image( $value ) ) {
return false;
}
return \update_option( 'site_logo', $value ) && \update_option( 'site_icon', $value );
}

/**
* Update the User-Header-Image.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_header( $value ) {
if ( ! wp_attachment_is_image( $value ) ) {
return false;
}
return \update_option( 'activitypub_header_image', $value );
}

/**
* Get the User - Hashtags .
*
* @see https://docs.joinmastodon.org/spec/activitypub/#Hashtag
*
* @return array The User-Hashtags.
* @return array The User - Hashtags .
*/
public function get_tag() {
$hashtags = array();
Expand Down
64 changes: 60 additions & 4 deletions includes/model/class-user.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ public function get_preferred_username() {
}

public function get_icon() {
$icon = \get_user_option( 'activitypub_icon', $this->_id );
if ( wp_attachment_is_image( $icon ) ) {
return array(
'type' => 'Image',
'url' => esc_url( wp_get_attachment_url( $icon ) ),
);
}

$icon = \esc_url(
\get_avatar_url(
$this->_id,
Expand All @@ -153,12 +161,12 @@ public function get_image() {
$header_image = get_user_option( 'activitypub_header_image', $this->_id );
$image_url = null;

if ( $header_image ) {
pfefferle marked this conversation as resolved.
Show resolved Hide resolved
$image_url = \wp_get_attachment_url( $header_image );
if ( ! $header_image && \has_header_image() ) {
$image_url = \get_header_image();
}

if ( ! $image_url && \has_header_image() ) {
$image_url = \get_header_image();
if ( $header_image ) {
$image_url = \wp_get_attachment_url( $header_image );
}

if ( $image_url ) {
Expand Down Expand Up @@ -278,4 +286,52 @@ public function get_indexable() {
return false;
}
}


/**
* Update the User-Name.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_name( $value ) {
$userdata = [ 'ID' => $this->_id, 'display_name' => $value ];
return \wp_update_user( $userdata );
}

/**
* Update the User-Description.
*
* @param mixed $value The new value.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_summary( $value ) {
return \update_user_option( $this->_id, 'activitypub_description', $value );
}

/**
* Update the User-Icon.
*
* @param mixed $value The new value. Should be an attachment ID.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_icon( $value ) {
if ( ! wp_attachment_is_image( $value ) ) {
return false;
}
return update_user_option( $this->_id, 'activitypub_icon', $value );
}

/**
* Update the User-Header-Image.
*
* @param mixed $value The new value. Should be an attachment ID.
* @return bool True if the attribute was updated, false otherwise.
*/
public function update_header( $value ) {
if ( ! wp_attachment_is_image( $value ) ) {
return false;
}
return \update_user_option( $this->_id, 'activitypub_header_image', $value );
}
}
Loading
Loading