From a95d5bde2340a8b9da5879ae54fcc26a2ecb1551 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Tue, 24 Sep 2024 13:47:25 -0500 Subject: [PATCH 1/7] Allow content warning --- includes/activity/class-base-object.php | 11 +++++++++ includes/class-blocks.php | 30 ++++++++++++++++++++++ includes/functions.php | 26 +++++++++++++++++++ includes/transformer/class-post.php | 7 ++++++ src/editor-plugin/block.json | 9 +++++++ src/editor-plugin/plugin.js | 33 +++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 src/editor-plugin/block.json create mode 100644 src/editor-plugin/plugin.js diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index 9a5cd3f83..d95b54e4f 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -445,6 +445,17 @@ class Base_Object { */ protected $replies; + /** + * Used to mark an object as containing sensitive content. + * Mastodon displays a content warning, requiring users to click + * through to view the content. + * + * @see https://docs.joinmastodon.org/spec/activitypub/#sensitive + * + * @var boolean + */ + protected $sensitive = false; + /** * Magic function to implement getter and setter * diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 5190feb13..f4cf7e364 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -14,6 +14,36 @@ public static function init() { \add_action( 'wp_enqueue_scripts', array( self::class, 'add_data' ) ); \add_action( 'enqueue_block_editor_assets', array( self::class, 'add_data' ) ); \add_action( 'load-post-new.php', array( self::class, 'handle_in_reply_to_get_param' ) ); + // Add editor plugin + \add_action( 'enqueue_block_editor_assets', array( self::class, 'enqueue_editor_assets' ) ); + \add_action( 'init', array( self::class, 'register_postmeta' ), 11 ); + } + + public static function register_postmeta() { + $ap_post_types = \get_post_types_by_support( 'activitypub' ); + foreach ( $ap_post_types as $post_type ) { + \register_post_meta( + $post_type, + 'activitypub_content_warning', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + } + } + + public static function enqueue_editor_assets() { + // check for our supported post types + $current_screen = \get_current_screen(); + $ap_post_types = \get_post_types_by_support( 'activitypub' ); + if ( ! $current_screen || ! in_array( $current_screen->post_type, $ap_post_types, true ) ) { + return; + } + $asset_data = include ACTIVITYPUB_PLUGIN_DIR . 'build/editor-plugin/plugin.asset.php'; + $plugin_url = plugins_url( 'build/editor-plugin/plugin.js', ACTIVITYPUB_PLUGIN_FILE ); + wp_enqueue_script( 'activitypub-block-editor', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true ); } /** diff --git a/includes/functions.php b/includes/functions.php index 3d894c253..7bd2903fd 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1177,6 +1177,11 @@ function generate_post_summary( $post, $length = 500 ) { return ''; } + $content_warning = get_content_warning( $post->ID ); + if ( $content_warning ) { + return $content_warning; + } + $content = \sanitize_post_field( 'post_excerpt', $post->post_excerpt, $post->ID ); if ( $content ) { @@ -1215,3 +1220,24 @@ function generate_post_summary( $post, $length = 500 ) { */ return $content; } + +/** + * Get the content warning of a post. + * + * @param int $post_id The post ID. + * + * @return string|false The content warning or false if not found. + */ +function get_content_warning( $post_id ) { + $post = get_post( $post_id ); + if ( ! $post ) { + return false; + } + + $warning = get_post_meta( $post->ID, 'activitypub_content_warning', true ); + if ( empty( $warning ) ) { + return false; + } + + return $warning; +} diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 3b9a9963d..7753f42e5 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -14,6 +14,7 @@ use function Activitypub\get_rest_url_by_path; use function Activitypub\is_user_type_disabled; use function Activitypub\generate_post_summary; +use function Activitypub\get_content_warning; /** * WordPress Post Transformer @@ -87,6 +88,12 @@ public function to_object() { ) ); + $content_warning = get_content_warning( $post ); + if ( ! empty( $content_warning ) ) { + $object->set_sensitive( true ); + $object->set_summary( $content_warning ); + } + return $object; } diff --git a/src/editor-plugin/block.json b/src/editor-plugin/block.json new file mode 100644 index 000000000..a8d510c7c --- /dev/null +++ b/src/editor-plugin/block.json @@ -0,0 +1,9 @@ +{ + "name": "editor-plugin", + "title": "Editor Plugin: not a block, but block.json is very useful.", + "category": "widgets", + "icon": "admin-comments", + "keywords": [ + ], + "editorScript": "file:./plugin.js" +} \ No newline at end of file diff --git a/src/editor-plugin/plugin.js b/src/editor-plugin/plugin.js new file mode 100644 index 000000000..7e95dcda2 --- /dev/null +++ b/src/editor-plugin/plugin.js @@ -0,0 +1,33 @@ +import { PluginDocumentSettingPanel } from '@wordpress/editor'; +import { registerPlugin } from '@wordpress/plugins'; +import { TextControl } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; + + +const EditorPlugin = () => { + const postType = useSelect( + ( select ) => select( 'core/editor' ).getCurrentPostType(), + [] + ); + const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); + + return ( + + { + setMeta( { ...meta, activitypub_content_warning: value } ); + } } + placeholder={ __( 'Optional content warning', 'activitypub' ) } + /> + + ); +} + +registerPlugin( 'activitypub-editor-plugin', { render: EditorPlugin } ); \ No newline at end of file From 68bc58a22242927939e51575b00d0c0b35db1f26 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Tue, 24 Sep 2024 13:48:28 -0500 Subject: [PATCH 2/7] build files --- build/editor-plugin/block.json | 8 ++++++++ build/editor-plugin/plugin.asset.php | 1 + build/editor-plugin/plugin.js | 1 + 3 files changed, 10 insertions(+) create mode 100644 build/editor-plugin/block.json create mode 100644 build/editor-plugin/plugin.asset.php create mode 100644 build/editor-plugin/plugin.js diff --git a/build/editor-plugin/block.json b/build/editor-plugin/block.json new file mode 100644 index 000000000..f41effc5f --- /dev/null +++ b/build/editor-plugin/block.json @@ -0,0 +1,8 @@ +{ + "name": "editor-plugin", + "title": "Editor Plugin: not a block, but block.json is very useful.", + "category": "widgets", + "icon": "admin-comments", + "keywords": [], + "editorScript": "file:./plugin.js" +} \ No newline at end of file diff --git a/build/editor-plugin/plugin.asset.php b/build/editor-plugin/plugin.asset.php new file mode 100644 index 000000000..67a3f1810 --- /dev/null +++ b/build/editor-plugin/plugin.asset.php @@ -0,0 +1 @@ + array('react', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => '88603987940fec29730d'); diff --git a/build/editor-plugin/plugin.js b/build/editor-plugin/plugin.js new file mode 100644 index 000000000..c16cd66e0 --- /dev/null +++ b/build/editor-plugin/plugin.js @@ -0,0 +1 @@ +(()=>{"use strict";const t=window.React,e=window.wp.editor,n=window.wp.plugins,i=window.wp.components,o=window.wp.data,a=window.wp.coreData,r=window.wp.i18n;(0,n.registerPlugin)("activitypub-editor-plugin",{render:()=>{const n=(0,o.useSelect)((t=>t("core/editor").getCurrentPostType()),[]),[w,c]=(0,a.useEntityProp)("postType",n,"meta");return(0,t.createElement)(e.PluginDocumentSettingPanel,{name:"activitypub",title:(0,r.__)("Fediverse","activitypub")},(0,t.createElement)(i.TextControl,{label:(0,r.__)("Content Warning","activitypub"),value:w?.activitypub_content_warning,onChange:t=>{c({...w,activitypub_content_warning:t})},placeholder:(0,r.__)("Optional content warning","activitypub")}))}})})(); \ No newline at end of file From b15d678b0975ac6016aac89391282afa0b3201e1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 24 Sep 2024 23:35:35 +0200 Subject: [PATCH 3/7] added missing namespace --- includes/activity/class-base-object.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/activity/class-base-object.php b/includes/activity/class-base-object.php index d95b54e4f..112ad4b4c 100644 --- a/includes/activity/class-base-object.php +++ b/includes/activity/class-base-object.php @@ -30,7 +30,8 @@ class Base_Object { const JSON_LD_CONTEXT = array( 'https://www.w3.org/ns/activitystreams', array( - 'Hashtag' => 'as:Hashtag', + 'Hashtag' => 'as:Hashtag', + 'sensitive' => 'as:sensitive', ), ); From 12185c0da048ea4809002e6d485b93c26b6dd477 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 24 Sep 2024 23:40:50 +0200 Subject: [PATCH 4/7] fix tests --- tests/test-class-activitypub-activity.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index f4a4fa9db..32a1f8304 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -34,9 +34,10 @@ function ( $mentions ) { public function test_object_transformation() { $test_array = array( - 'id' => 'https://example.com/post/123', - 'type' => 'Note', - 'content' => 'Hello world!', + 'id' => 'https://example.com/post/123', + 'type' => 'Note', + 'content' => 'Hello world!', + 'sensitive' => false, ); $object = \Activitypub\Activity\Base_Object::init_from_array( $test_array ); From 18b6a1ae38c3f9eb37f3ccfc10101e0a844df83b Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 25 Sep 2024 11:47:16 +0200 Subject: [PATCH 5/7] actors does not support sensitive (yet). --- includes/activity/class-actor.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/includes/activity/class-actor.php b/includes/activity/class-actor.php index 2d05539ac..80dc710b3 100644 --- a/includes/activity/class-actor.php +++ b/includes/activity/class-actor.php @@ -169,4 +169,15 @@ class Actor extends Base_Object { * @var boolean */ protected $manually_approves_followers = false; + + /** + * Used to mark an object as containing sensitive content. + * Mastodon displays a content warning, requiring users to click + * through to view the content. + * + * @see https://docs.joinmastodon.org/spec/activitypub/#sensitive + * + * @var boolean + */ + protected $sensitive = null; } From 7ff27f2345ed6b42ae75d5df52a3082e1baac6b9 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 25 Sep 2024 15:47:05 +0200 Subject: [PATCH 6/7] Update includes/functions.php --- includes/functions.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 7bd2903fd..767bf7215 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1177,11 +1177,6 @@ function generate_post_summary( $post, $length = 500 ) { return ''; } - $content_warning = get_content_warning( $post->ID ); - if ( $content_warning ) { - return $content_warning; - } - $content = \sanitize_post_field( 'post_excerpt', $post->post_excerpt, $post->ID ); if ( $content ) { From 1f2f1f998e31e80a88ee68a696d66d95870e193a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 25 Sep 2024 15:52:36 +0200 Subject: [PATCH 7/7] added `sanitize_callback` --- includes/class-blocks.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 1f54499f9..f2fea4089 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -26,9 +26,10 @@ public static function register_postmeta() { $post_type, 'activitypub_content_warning', array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', ) ); }