From 08e6407b9eaf8fefccd889e1a92dfe7de9c55b11 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 12:53:15 +0100 Subject: [PATCH 01/17] Add Extra Fields block for ActivityPub profiles Introduces a new 'Fediverse Profile Fields' block to display extra profile fields from ActivityPub user profiles. Includes block registration, editor and server-side rendering logic, styles for default, compact, and cards layouts, and integration into the block registration process. --- build/extra-fields/block.json | 72 ++++++++ build/extra-fields/index.asset.php | 1 + build/extra-fields/index.js | 1 + build/extra-fields/render.php | 75 ++++++++ build/extra-fields/style-index-rtl.css | 1 + build/extra-fields/style-index.css | 1 + includes/class-blocks.php | 1 + src/extra-fields/block.json | 66 +++++++ src/extra-fields/edit.js | 233 +++++++++++++++++++++++++ src/extra-fields/index.js | 20 +++ src/extra-fields/render.php | 75 ++++++++ src/extra-fields/save.js | 10 ++ src/extra-fields/style.scss | 131 ++++++++++++++ 13 files changed, 687 insertions(+) create mode 100644 build/extra-fields/block.json create mode 100644 build/extra-fields/index.asset.php create mode 100644 build/extra-fields/index.js create mode 100644 build/extra-fields/render.php create mode 100644 build/extra-fields/style-index-rtl.css create mode 100644 build/extra-fields/style-index.css create mode 100644 src/extra-fields/block.json create mode 100644 src/extra-fields/edit.js create mode 100644 src/extra-fields/index.js create mode 100644 src/extra-fields/render.php create mode 100644 src/extra-fields/save.js create mode 100644 src/extra-fields/style.scss diff --git a/build/extra-fields/block.json b/build/extra-fields/block.json new file mode 100644 index 000000000..f5d48dceb --- /dev/null +++ b/build/extra-fields/block.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "name": "activitypub/extra-fields", + "apiVersion": 3, + "version": "1.0.0", + "title": "Fediverse Profile Fields", + "category": "widgets", + "description": "Display extra profile fields from ActivityPub user profiles", + "textdomain": "activitypub", + "icon": "list-view", + "supports": { + "html": false, + "align": [ + "wide", + "full" + ], + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "__experimentalBorder": { + "radius": true, + "width": true, + "color": true, + "style": true + }, + "shadow": true, + "typography": { + "fontSize": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "styles": [ + { + "name": "default", + "label": "Default", + "isDefault": true + }, + { + "name": "compact", + "label": "Compact" + }, + { + "name": "cards", + "label": "Cards" + } + ], + "attributes": { + "selectedUser": { + "type": "string", + "default": "blog" + }, + "maxFields": { + "type": "number", + "default": 0 + } + }, + "usesContext": [ + "postType", + "postId" + ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "render": "file:./render.php" +} \ No newline at end of file diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php new file mode 100644 index 000000000..73bfebd0a --- /dev/null +++ b/build/extra-fields/index.asset.php @@ -0,0 +1 @@ + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '1712fbc44f0f619e729a'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js new file mode 100644 index 000000000..07460abea --- /dev/null +++ b/build/extra-fields/index.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,t={489:(e,t,i)=>{const s=window.wp.blocks,l=window.wp.i18n,r=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,s.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:s,maxFields:c}=e,[p,h]=(0,o.useState)([]),[v,b]=(0,o.useState)(!1),[y,x]=(0,o.useState)(null),f=(0,r.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),w=(0,a.useSelect)(e=>{const t=e("core/editor");return t?t.getCurrentPostAttribute("author"):null},[]),_=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[s,r]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:s}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&s("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>r(!0)).catch(()=>r(!1))},[n,c,u]);const p=n||(u&&s?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,l.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,l.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0,authorId:w}),g="blog"===s?0:"inherit"===s?w||null:s;(0,o.useEffect)(()=>{null!==g?(b(!0),x(null),d()({path:`/activitypub/1.0/actors/${g}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);h(t),b(!1)}).catch(e=>{x(e.message),b(!1)})):h([])},[g]);const m=c>0?p.slice(0,c):p,j=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},s=e.backgroundColor,l=i.color?.background;return s?{backgroundColor:`var(--wp--preset--color--${s})`}:l?{backgroundColor:l}:{}})();return"inherit"!==s||w?v?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):y?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,l.__)("Error loading extra fields:","activitypub")," ",y]})})}):0===m.length?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:m.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:j,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},t))})})]}):(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("This block will display extra fields based on the post author when published.","activitypub")})})})},save:function(){return null}})}},i={};function s(e){var l=i[e];if(void 0!==l)return l.exports;var r=i[e]={exports:{}};return t[e](r,r.exports,s),r.exports}s.m=t,e=[],s.O=(t,i,l,r)=>{if(!i){var n=1/0;for(d=0;d=r)&&Object.keys(s.O).every(e=>s.O[e](i[o]))?i.splice(o--,1):(a=!1,r0&&e[d-1][2]>r;d--)e[d]=e[d-1];e[d]=[i,l,r]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},s.d=(e,t)=>{for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};s.O.j=t=>0===e[t];var t=(t,i)=>{var l,r,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(l in a)s.o(a,l)&&(s.m[l]=a[l]);if(o)var d=o(s)}for(t&&t(i);cs(489));l=s.O(l)})(); \ No newline at end of file diff --git a/build/extra-fields/render.php b/build/extra-fields/render.php new file mode 100644 index 000000000..46ef8d108 --- /dev/null +++ b/build/extra-fields/render.php @@ -0,0 +1,75 @@ +context ); + +// If user ID couldn't be determined, return empty. +if ( null === $user_id ) { + return ''; +} + +// Get extra fields for this user. +$fields = Extra_Fields::get_actor_fields( $user_id ); + +// Apply max fields limit if set. +$max_fields = $attributes['maxFields'] ?? 0; +if ( $max_fields > 0 && count( $fields ) > $max_fields ) { + $fields = array_slice( $fields, 0, $max_fields ); +} + +// Return empty on frontend if no fields (hide block). +if ( empty( $fields ) ) { + return ''; +} + +// Get block wrapper attributes. +$wrapper_attributes = get_block_wrapper_attributes( + array( + 'class' => 'activitypub-extra-fields-block-wrapper', + ) +); + +// Extract background color for cards style. +$background_color = ''; +$card_style = ''; + +// Check if this is the cards style by looking at className attribute or wrapper classes. +$is_cards_style = ( isset( $attributes['className'] ) && str_contains( $attributes['className'], 'is-style-cards' ) ) + || str_contains( $wrapper_attributes, 'is-style-cards' ); + +if ( $is_cards_style ) { + // Check for background color in various formats. + if ( isset( $attributes['backgroundColor'] ) ) { + $background_color = sprintf( 'var(--wp--preset--color--%s)', $attributes['backgroundColor'] ); + } elseif ( isset( $attributes['style']['color']['background'] ) ) { + $background_color = $attributes['style']['color']['background']; + } + + if ( $background_color ) { + $card_style = sprintf( ' style="background-color: %s;"', esc_attr( $background_color ) ); + } +} +?> +
> +
+ +
> +
post_title ); ?>
+
+
+ +
+
diff --git a/build/extra-fields/style-index-rtl.css b/build/extra-fields/style-index-rtl.css new file mode 100644 index 000000000..23fdbaf33 --- /dev/null +++ b/build/extra-fields/style-index-rtl.css @@ -0,0 +1 @@ +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed;width:auto}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap;width:auto}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/build/extra-fields/style-index.css b/build/extra-fields/style-index.css new file mode 100644 index 000000000..865608094 --- /dev/null +++ b/build/extra-fields/style-index.css @@ -0,0 +1 @@ +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed;width:auto}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap;width:auto}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/includes/class-blocks.php b/includes/class-blocks.php index bc3e0f280..1615e9685 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -76,6 +76,7 @@ public static function handle_in_reply_to_get_param() { * Register the blocks. */ public static function register_blocks() { + \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/extra-fields' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/follow-me' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/followers' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/reactions' ); diff --git a/src/extra-fields/block.json b/src/extra-fields/block.json new file mode 100644 index 000000000..7175f48d7 --- /dev/null +++ b/src/extra-fields/block.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "name": "activitypub/extra-fields", + "apiVersion": 3, + "version": "1.0.0", + "title": "Fediverse Profile Fields", + "category": "widgets", + "description": "Display extra profile fields from ActivityPub user profiles", + "textdomain": "activitypub", + "icon": "list-view", + "supports": { + "html": false, + "align": [ "wide", "full" ], + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "__experimentalBorder": { + "radius": true, + "width": true, + "color": true, + "style": true + }, + "shadow": true, + "typography": { + "fontSize": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "styles": [ + { + "name": "default", + "label": "Default", + "isDefault": true + }, + { + "name": "compact", + "label": "Compact" + }, + { + "name": "cards", + "label": "Cards" + } + ], + "attributes": { + "selectedUser": { + "type": "string", + "default": "blog" + }, + "maxFields": { + "type": "number", + "default": 0 + } + }, + "usesContext": [ "postType", "postId" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "render": "file:./render.php" +} diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js new file mode 100644 index 000000000..8bacac37d --- /dev/null +++ b/src/extra-fields/edit.js @@ -0,0 +1,233 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; +import { PanelBody, SelectControl, RangeControl, Placeholder, Spinner } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { useState, useEffect } from '@wordpress/element'; +import apiFetch from '@wordpress/api-fetch'; + +/** + * Internal dependencies + */ +import { useUserOptions } from '../shared/use-user-options'; + +/** + * Editor component for Extra Fields block. + * + * @param {Object} props Component props. + * @param {Object} props.attributes Block attributes. + * @param {Function} props.setAttributes Function to set attributes. + * @param {Object} props.context Block context. + * @return {Element} Component element. + */ +export default function Edit( { attributes, setAttributes, context } ) { + const { selectedUser, maxFields } = attributes; + const [ fields, setFields ] = useState( [] ); + const [ isLoading, setIsLoading ] = useState( false ); + const [ error, setError ] = useState( null ); + + const blockProps = useBlockProps( { + className: 'activitypub-extra-fields-block-wrapper', + } ); + + // Get author ID from context + const authorId = useSelect( ( select ) => { + const editor = select( 'core/editor' ); + if ( ! editor ) { + return null; + } + return editor.getCurrentPostAttribute( 'author' ); + }, [] ); + + // Get user options for dropdown + const userOptions = useUserOptions( { + withInherit: true, + authorId, + } ); + + // Determine which user ID to fetch + const getUserId = () => { + if ( selectedUser === 'blog' ) { + return 0; + } + + if ( selectedUser === 'inherit' ) { + if ( authorId ) { + return authorId; + } + return null; + } + + return selectedUser; + }; + + const userId = getUserId(); + + // Fetch extra fields + useEffect( () => { + if ( userId === null ) { + setFields( [] ); + return; + } + + setIsLoading( true ); + setError( null ); + + apiFetch( { + path: `/activitypub/1.0/actors/${ userId }`, + headers: { Accept: 'application/activity+json' }, + } ) + .then( ( actor ) => { + // Extract fields from attachment array + const attachments = actor.attachment || []; + // Filter to only PropertyValue types (the main format) + const propertyValues = attachments.filter( ( item ) => item.type === 'PropertyValue' ); + setFields( propertyValues ); + setIsLoading( false ); + } ) + .catch( ( err ) => { + setError( err.message ); + setIsLoading( false ); + } ); + }, [ userId ] ); + + // Apply max fields limit for preview + const displayFields = maxFields > 0 ? fields.slice( 0, maxFields ) : fields; + + // Extract background color for cards style + const getCardStyle = () => { + const isCardsStyle = attributes.className?.includes( 'is-style-cards' ); + if ( ! isCardsStyle ) { + return {}; + } + + // Get background color from block attributes + const style = attributes.style || {}; + const backgroundColor = attributes.backgroundColor; + const customColor = style.color?.background; + + if ( backgroundColor ) { + return { + backgroundColor: `var(--wp--preset--color--${ backgroundColor })`, + }; + } else if ( customColor ) { + return { + backgroundColor: customColor, + }; + } + + return {}; + }; + + const cardStyle = getCardStyle(); + + // Render placeholder if inherit mode but no author + if ( selectedUser === 'inherit' && ! authorId ) { + return ( +
+ +

+ { __( + 'This block will display extra fields based on the post author when published.', + 'activitypub' + ) } +

+
+
+ ); + } + + // Render loading state + if ( isLoading ) { + return ( +
+ + + +
+ ); + } + + // Render error state + if ( error ) { + return ( +
+ +

+ { __( 'Error loading extra fields:', 'activitypub' ) } { error } +

+
+
+ ); + } + + // Render empty state + if ( displayFields.length === 0 ) { + return ( + <> + + + setAttributes( { selectedUser: value } ) } + /> + setAttributes( { maxFields: value } ) } + min={ 0 } + max={ 20 } + help={ __( 'Limit the number of fields displayed. 0 = show all.', 'activitypub' ) } + /> + + +
+ +

{ __( 'No extra fields found. Add fields in your profile settings.', 'activitypub' ) }

+
+
+ + ); + } + + return ( + <> + + + setAttributes( { selectedUser: value } ) } + /> + setAttributes( { maxFields: value } ) } + min={ 0 } + max={ 20 } + help={ __( 'Limit the number of fields displayed. 0 = show all.', 'activitypub' ) } + /> + + +
+
+ { displayFields.map( ( field, index ) => ( +
+
{ field.name }
+
+
+ ) ) } +
+
+ + ); +} diff --git a/src/extra-fields/index.js b/src/extra-fields/index.js new file mode 100644 index 000000000..4e47b43e5 --- /dev/null +++ b/src/extra-fields/index.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; +import './style.scss'; + +/** + * Register the Extra Fields block. + */ +registerBlockType( metadata.name, { + edit, + save, +} ); diff --git a/src/extra-fields/render.php b/src/extra-fields/render.php new file mode 100644 index 000000000..46ef8d108 --- /dev/null +++ b/src/extra-fields/render.php @@ -0,0 +1,75 @@ +context ); + +// If user ID couldn't be determined, return empty. +if ( null === $user_id ) { + return ''; +} + +// Get extra fields for this user. +$fields = Extra_Fields::get_actor_fields( $user_id ); + +// Apply max fields limit if set. +$max_fields = $attributes['maxFields'] ?? 0; +if ( $max_fields > 0 && count( $fields ) > $max_fields ) { + $fields = array_slice( $fields, 0, $max_fields ); +} + +// Return empty on frontend if no fields (hide block). +if ( empty( $fields ) ) { + return ''; +} + +// Get block wrapper attributes. +$wrapper_attributes = get_block_wrapper_attributes( + array( + 'class' => 'activitypub-extra-fields-block-wrapper', + ) +); + +// Extract background color for cards style. +$background_color = ''; +$card_style = ''; + +// Check if this is the cards style by looking at className attribute or wrapper classes. +$is_cards_style = ( isset( $attributes['className'] ) && str_contains( $attributes['className'], 'is-style-cards' ) ) + || str_contains( $wrapper_attributes, 'is-style-cards' ); + +if ( $is_cards_style ) { + // Check for background color in various formats. + if ( isset( $attributes['backgroundColor'] ) ) { + $background_color = sprintf( 'var(--wp--preset--color--%s)', $attributes['backgroundColor'] ); + } elseif ( isset( $attributes['style']['color']['background'] ) ) { + $background_color = $attributes['style']['color']['background']; + } + + if ( $background_color ) { + $card_style = sprintf( ' style="background-color: %s;"', esc_attr( $background_color ) ); + } +} +?> +
> +
+ +
> +
post_title ); ?>
+
+
+ +
+
diff --git a/src/extra-fields/save.js b/src/extra-fields/save.js new file mode 100644 index 000000000..414590768 --- /dev/null +++ b/src/extra-fields/save.js @@ -0,0 +1,10 @@ +/** + * Save function for Extra Fields block. + * + * This block uses server-side rendering, so the save function returns null. + * + * @return {null} No save content. + */ +export default function save() { + return null; +} diff --git a/src/extra-fields/style.scss b/src/extra-fields/style.scss new file mode 100644 index 000000000..87286d3d3 --- /dev/null +++ b/src/extra-fields/style.scss @@ -0,0 +1,131 @@ +/** + * Styles for the Extra Fields block. + */ + +.activitypub-extra-fields-block-wrapper { + // Add padding to content when background or border is set (default and compact styles) + &.has-background .activitypub-extra-fields, + &.has-border .activitypub-extra-fields { + padding: 1rem; + } + + // Remove default padding for cards style + &.is-style-cards.has-background .activitypub-extra-fields, + &.is-style-cards.has-border .activitypub-extra-fields { + padding: 0; + } +} + +.activitypub-extra-fields { + margin: 0; + padding: 0; + list-style: none; +} + +.activitypub-extra-field { + margin-bottom: 1em; + + &:last-child { + margin-bottom: 0; + } + + dt { + font-weight: 600; + margin-bottom: 0.25em; + color: inherit; + } + + dd { + margin-left: 0; + margin-bottom: 0; + color: inherit; + + a { + color: inherit; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + } +} + +/** + * Compact Style + * Displays fields in a table-like format with aligned labels and values. + */ +.is-style-compact { + .activitypub-extra-fields { + display: table; + width: auto; + table-layout: fixed; + } + + .activitypub-extra-field { + display: table-row; + margin-bottom: 0; + + dt { + display: table-cell; + padding-bottom: 0.5em; + margin-bottom: 0; + padding-right: 10px; + white-space: nowrap; + vertical-align: baseline; + width: auto; + text-overflow: ellipsis; + + &::after { + content: ':'; + } + } + + dd { + display: table-cell; + padding-bottom: 0.5em; + vertical-align: baseline; + word-break: break-word; + } + } +} + +/** + * Cards Style + * Displays each field in its own bordered card with padding. + */ +.is-style-cards { + // Remove wrapper background for cards style - background goes on individual cards only + &.has-background { + background: transparent !important; + + .activitypub-extra-fields { + padding: 1rem; + } + } + + .activitypub-extra-field { + border: 1px solid #ddd; + border-radius: 8px; + padding: 1em; + margin-bottom: 1em; + background: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + + &:last-child { + margin-bottom: 0; + } + + dt { + font-size: 0.9em; + text-transform: uppercase; + letter-spacing: 0.5px; + color: currentColor; + margin-bottom: 0.5em; + } + + dd { + font-size: 1em; + } + } +} From a98502f376d4ec7e994c3aa74793073dd2609d32 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:05:29 +0100 Subject: [PATCH 02/17] Add docblock to Extra Fields block render callback Introduces a PHP docblock for the render callback in src/extra-fields/render.php, clarifying the types and purpose of the $attributes and $block variables for improved code readability and maintainability. --- src/extra-fields/render.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/extra-fields/render.php b/src/extra-fields/render.php index 46ef8d108..d506f63ef 100644 --- a/src/extra-fields/render.php +++ b/src/extra-fields/render.php @@ -8,13 +8,15 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; -/* @var array $attributes Block attributes. */ $attributes = wp_parse_args( $attributes ); -/* @var WP_Block $block Block instance. */ - -// Get user ID from selectedUser attribute. -$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); +/** + * Render callback for the Extra Fields block. + * + * @var array $attributes Block attributes. + * @var WP_Block $block Block instance. + */ +$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable // If user ID couldn't be determined, return empty. if ( null === $user_id ) { From 584ebcf48df6295e93d6977ca24e055911d80f44 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:08:31 +0100 Subject: [PATCH 03/17] Update build/extra-fields/render.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- build/extra-fields/render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/extra-fields/render.php b/build/extra-fields/render.php index 46ef8d108..14b279af6 100644 --- a/build/extra-fields/render.php +++ b/build/extra-fields/render.php @@ -14,7 +14,7 @@ /* @var WP_Block $block Block instance. */ // Get user ID from selectedUser attribute. -$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); +$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog' ); // If user ID couldn't be determined, return empty. if ( null === $user_id ) { From 1e1532b4a809b649b9d6102da00f8b8738535f33 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:08:44 +0100 Subject: [PATCH 04/17] Update src/extra-fields/style.scss Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/extra-fields/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extra-fields/style.scss b/src/extra-fields/style.scss index 87286d3d3..cdc365236 100644 --- a/src/extra-fields/style.scss +++ b/src/extra-fields/style.scss @@ -105,11 +105,11 @@ } .activitypub-extra-field { - border: 1px solid #ddd; + border: 1px solid var(--wp--preset--color--contrast-2, #ddd); border-radius: 8px; padding: 1em; margin-bottom: 1em; - background: #fff; + background: var(--wp--preset--color--base, #fff); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); &:last-child { From 05a20b421ea734d54dafa7dac72f6ef21e367f6f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:08:51 +0100 Subject: [PATCH 05/17] Update src/extra-fields/edit.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/extra-fields/edit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index 8bacac37d..14d50618c 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -44,7 +44,6 @@ export default function Edit( { attributes, setAttributes, context } ) { // Get user options for dropdown const userOptions = useUserOptions( { withInherit: true, - authorId, } ); // Determine which user ID to fetch From b4271a36d19ff6bef27c02f35a13a38745f05d7f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:09:51 +0100 Subject: [PATCH 06/17] Update src/extra-fields/edit.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/extra-fields/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index 14d50618c..b974d0359 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -216,7 +216,7 @@ export default function Edit( { attributes, setAttributes, context } ) {
{ displayFields.map( ( field, index ) => ( -
+
{ field.name }
Date: Tue, 11 Nov 2025 13:11:31 +0100 Subject: [PATCH 07/17] Add tests and improve extra fields block styles Introduces comprehensive Jest tests for the Extra Fields block edit component. Refines card and compact styles to use CSS variables for colors and removes unnecessary width properties. Updates PHP render logic to pass block context for user resolution and improves key usage in React field rendering. --- build/extra-fields/index.asset.php | 2 +- build/extra-fields/index.js | 2 +- build/extra-fields/render.php | 12 +- build/extra-fields/style-index-rtl.css | 2 +- build/extra-fields/style-index.css | 2 +- src/extra-fields/__tests__/edit.test.js | 465 ++++++++++++++++++++++++ src/extra-fields/edit.js | 6 +- src/extra-fields/style.scss | 2 - 8 files changed, 481 insertions(+), 12 deletions(-) create mode 100644 src/extra-fields/__tests__/edit.test.js diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php index 73bfebd0a..96a8d4429 100644 --- a/build/extra-fields/index.asset.php +++ b/build/extra-fields/index.asset.php @@ -1 +1 @@ - array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '1712fbc44f0f619e729a'); + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'aa5f020aacde684c5c7c'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js index 07460abea..ea0188a34 100644 --- a/build/extra-fields/index.js +++ b/build/extra-fields/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={489:(e,t,i)=>{const s=window.wp.blocks,l=window.wp.i18n,r=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,s.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:s,maxFields:c}=e,[p,h]=(0,o.useState)([]),[v,b]=(0,o.useState)(!1),[y,x]=(0,o.useState)(null),f=(0,r.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),w=(0,a.useSelect)(e=>{const t=e("core/editor");return t?t.getCurrentPostAttribute("author"):null},[]),_=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[s,r]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:s}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&s("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>r(!0)).catch(()=>r(!1))},[n,c,u]);const p=n||(u&&s?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,l.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,l.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0,authorId:w}),g="blog"===s?0:"inherit"===s?w||null:s;(0,o.useEffect)(()=>{null!==g?(b(!0),x(null),d()({path:`/activitypub/1.0/actors/${g}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);h(t),b(!1)}).catch(e=>{x(e.message),b(!1)})):h([])},[g]);const m=c>0?p.slice(0,c):p,j=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},s=e.backgroundColor,l=i.color?.background;return s?{backgroundColor:`var(--wp--preset--color--${s})`}:l?{backgroundColor:l}:{}})();return"inherit"!==s||w?v?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):y?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,l.__)("Error loading extra fields:","activitypub")," ",y]})})}):0===m.length?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:m.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:j,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},t))})})]}):(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("This block will display extra fields based on the post author when published.","activitypub")})})})},save:function(){return null}})}},i={};function s(e){var l=i[e];if(void 0!==l)return l.exports;var r=i[e]={exports:{}};return t[e](r,r.exports,s),r.exports}s.m=t,e=[],s.O=(t,i,l,r)=>{if(!i){var n=1/0;for(d=0;d=r)&&Object.keys(s.O).every(e=>s.O[e](i[o]))?i.splice(o--,1):(a=!1,r0&&e[d-1][2]>r;d--)e[d]=e[d-1];e[d]=[i,l,r]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},s.d=(e,t)=>{for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};s.O.j=t=>0===e[t];var t=(t,i)=>{var l,r,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(l in a)s.o(a,l)&&(s.m[l]=a[l]);if(o)var d=o(s)}for(t&&t(i);cs(489));l=s.O(l)})(); \ No newline at end of file +(()=>{"use strict";var e,t={489:(e,t,i)=>{const s=window.wp.blocks,l=window.wp.i18n,r=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,s.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:s,maxFields:c}=e,[p,h]=(0,o.useState)([]),[v,b]=(0,o.useState)(!1),[y,x]=(0,o.useState)(null),f=(0,r.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),w=(0,a.useSelect)(e=>{const t=e("core/editor");return t?t.getCurrentPostAttribute("author"):null},[]),m=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[s,r]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:s}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&s("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>r(!0)).catch(()=>r(!1))},[n,c,u]);const p=n||(u&&s?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,l.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,l.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0}),_="blog"===s?0:"inherit"===s?w||null:s;(0,o.useEffect)(()=>{null!==_?(b(!0),x(null),d()({path:`/activitypub/1.0/actors/${_}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);h(t),b(!1)}).catch(e=>{x(e.message),b(!1)})):h([])},[_]);const g=c>0?p.slice(0,c):p,j=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},s=e.backgroundColor,l=i.color?.background;return s?{backgroundColor:`var(--wp--preset--color--${s})`}:l?{backgroundColor:l}:{}})();return"inherit"!==s||w?v?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):y?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,l.__)("Error loading extra fields:","activitypub")," ",y]})})}):0===g.length?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:m,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:m,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:g.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:j,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("This block will display extra fields based on the post author when published.","activitypub")})})})},save:function(){return null}})}},i={};function s(e){var l=i[e];if(void 0!==l)return l.exports;var r=i[e]={exports:{}};return t[e](r,r.exports,s),r.exports}s.m=t,e=[],s.O=(t,i,l,r)=>{if(!i){var n=1/0;for(d=0;d=r)&&Object.keys(s.O).every(e=>s.O[e](i[o]))?i.splice(o--,1):(a=!1,r0&&e[d-1][2]>r;d--)e[d]=e[d-1];e[d]=[i,l,r]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},s.d=(e,t)=>{for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};s.O.j=t=>0===e[t];var t=(t,i)=>{var l,r,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(l in a)s.o(a,l)&&(s.m[l]=a[l]);if(o)var d=o(s)}for(t&&t(i);cs(489));l=s.O(l)})(); \ No newline at end of file diff --git a/build/extra-fields/render.php b/build/extra-fields/render.php index 14b279af6..d506f63ef 100644 --- a/build/extra-fields/render.php +++ b/build/extra-fields/render.php @@ -8,13 +8,15 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; -/* @var array $attributes Block attributes. */ $attributes = wp_parse_args( $attributes ); -/* @var WP_Block $block Block instance. */ - -// Get user ID from selectedUser attribute. -$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog' ); +/** + * Render callback for the Extra Fields block. + * + * @var array $attributes Block attributes. + * @var WP_Block $block Block instance. + */ +$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable // If user ID couldn't be determined, return empty. if ( null === $user_id ) { diff --git a/build/extra-fields/style-index-rtl.css b/build/extra-fields/style-index-rtl.css index 23fdbaf33..6fb491f57 100644 --- a/build/extra-fields/style-index-rtl.css +++ b/build/extra-fields/style-index-rtl.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed;width:auto}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap;width:auto}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/build/extra-fields/style-index.css b/build/extra-fields/style-index.css index 865608094..339d33b64 100644 --- a/build/extra-fields/style-index.css +++ b/build/extra-fields/style-index.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed;width:auto}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap;width:auto}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:#fff;border:1px solid #ddd;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/src/extra-fields/__tests__/edit.test.js b/src/extra-fields/__tests__/edit.test.js new file mode 100644 index 000000000..5589d7871 --- /dev/null +++ b/src/extra-fields/__tests__/edit.test.js @@ -0,0 +1,465 @@ +/** + * @jest-environment jsdom + */ + +import { render, screen, waitFor } from '@testing-library/react'; +import { useSelect } from '@wordpress/data'; +import apiFetch from '@wordpress/api-fetch'; +import Edit from '../edit'; + +// Mock WordPress dependencies +jest.mock( '@wordpress/data', () => ( { + useSelect: jest.fn(), +} ) ); + +jest.mock( '@wordpress/api-fetch' ); + +jest.mock( '@wordpress/i18n', () => ( { + __: ( text ) => text, +} ) ); + +jest.mock( '@wordpress/block-editor', () => ( { + useBlockProps: jest.fn( ( props ) => props ), + InspectorControls: ( { children } ) =>
{ children }
, +} ) ); + +jest.mock( '@wordpress/components', () => ( { + PanelBody: ( { children, title } ) => ( +
+ { children } +
+ ), + SelectControl: ( { label, value, options, onChange } ) => ( +
+ + +
+ ), + RangeControl: ( { label, value, onChange, min, max, help } ) => ( +
+ + onChange( parseInt( e.target.value, 10 ) ) } + /> + { help } +
+ ), + Placeholder: ( { label, children } ) => ( +
+ { children } +
+ ), + Spinner: () =>
Loading...
, +} ) ); + +jest.mock( '../../shared/use-user-options', () => ( { + useUserOptions: jest.fn( () => [ + { value: 'blog', label: 'Blog' }, + { value: 'inherit', label: 'Inherit from post author' }, + { value: '1', label: 'Admin' }, + ] ), +} ) ); + +// Suppress console warnings for testing +const originalError = console.error; + +describe( 'Extra Fields Edit Component', () => { + beforeAll( () => { + console.error = jest.fn(); + } ); + + afterAll( () => { + console.error = originalError; + } ); + + beforeEach( () => { + // Reset mocks before each test + jest.clearAllMocks(); + + // Default mock for useSelect + useSelect.mockReturnValue( 1 ); // Default authorId + + // Default mock for apiFetch + apiFetch.mockResolvedValue( { + attachment: [ + { + type: 'PropertyValue', + name: 'Website', + value: 'example.com', + }, + { + type: 'PropertyValue', + name: 'Location', + value: 'San Francisco, CA', + }, + ], + } ); + } ); + + afterEach( () => { + jest.clearAllMocks(); + } ); + + test( 'renders inspector controls with user and max fields settings', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'blog', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByTestId( 'inspector-controls' ) ).toBeTruthy(); + } ); + + expect( screen.getByTestId( 'select-control' ) ).toBeTruthy(); + expect( screen.getByTestId( 'range-control' ) ).toBeTruthy(); + } ); + + test( 'fetches and displays extra fields', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + render( ); + + // Should show loading initially + expect( screen.getByTestId( 'spinner' ) ).toBeTruthy(); + + // Wait for data to load + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + expect( screen.getByText( 'Location' ) ).toBeTruthy(); + expect( apiFetch ).toHaveBeenCalledWith( { + path: '/activitypub/1.0/actors/1', + headers: { Accept: 'application/activity+json' }, + } ); + } ); + + test( 'shows placeholder when inherit mode but no author', () => { + useSelect.mockReturnValue( null ); // No author + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'inherit', + maxFields: 0, + }; + + render( ); + + expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); + expect( + screen.getByText( 'This block will display extra fields based on the post author when published.' ) + ).toBeTruthy(); + } ); + + test( 'shows error message when API fetch fails', async () => { + apiFetch.mockRejectedValue( new Error( 'Network error' ) ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByText( /Error loading extra fields/i ) ).toBeTruthy(); + } ); + + expect( screen.getByText( /Network error/i ) ).toBeTruthy(); + } ); + + test( 'shows empty state when no fields available', async () => { + apiFetch.mockResolvedValue( { + attachment: [], + } ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); + } ); + + expect( screen.getByText( 'No extra fields found. Add fields in your profile settings.' ) ).toBeTruthy(); + } ); + + test( 'limits displayed fields when maxFields is set', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 1, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + // Should only show first field + expect( screen.queryByText( 'Location' ) ).toBeNull(); + } ); + + test( 'fetches blog user fields when selectedUser is blog', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'blog', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( apiFetch ).toHaveBeenCalledWith( { + path: '/activitypub/1.0/actors/0', + headers: { Accept: 'application/activity+json' }, + } ); + } ); + } ); + + test( 'fetches author fields when selectedUser is inherit', async () => { + useSelect.mockReturnValue( 5 ); // authorId = 5 + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'inherit', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( apiFetch ).toHaveBeenCalledWith( { + path: '/activitypub/1.0/actors/5', + headers: { Accept: 'application/activity+json' }, + } ); + } ); + } ); + + test( 'filters attachment array to only PropertyValue types', async () => { + apiFetch.mockResolvedValue( { + attachment: [ + { + type: 'PropertyValue', + name: 'Website', + value: 'example.com', + }, + { + type: 'Link', + name: 'Other Link', + href: 'https://other.com', + }, + { + type: 'PropertyValue', + name: 'Location', + value: 'SF', + }, + ], + } ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + // Should show PropertyValue items + expect( screen.getByText( 'Location' ) ).toBeTruthy(); + + // Should not show Link type + expect( screen.queryByText( 'Other Link' ) ).toBeNull(); + } ); + + test( 'applies card style with background color when className includes is-style-cards', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + className: 'is-style-cards', + backgroundColor: 'primary', + }; + + const { container } = render( + + ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + // Check if card style is applied + const fields = container.querySelectorAll( '.activitypub-extra-field' ); + expect( fields.length ).toBeGreaterThan( 0 ); + } ); + + test( 'applies card style with custom background color', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + className: 'is-style-cards', + style: { + color: { + background: '#ff0000', + }, + }, + }; + + const { container } = render( + + ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + const fields = container.querySelectorAll( '.activitypub-extra-field' ); + expect( fields.length ).toBeGreaterThan( 0 ); + } ); + + test( 'does not apply background color when not cards style', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + className: 'is-style-compact', + backgroundColor: 'primary', + }; + + const { container } = render( + + ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + const fields = container.querySelectorAll( '.activitypub-extra-field' ); + expect( fields.length ).toBeGreaterThan( 0 ); + } ); + + test( 'renders HTML content safely using dangerouslySetInnerHTML', async () => { + apiFetch.mockResolvedValue( { + attachment: [ + { + type: 'PropertyValue', + name: 'Website', + value: 'Click here', + }, + ], + } ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + const { container } = render( + + ); + + await waitFor( () => { + expect( screen.getByText( 'Website' ) ).toBeTruthy(); + } ); + + // Check that HTML is rendered + const link = container.querySelector( 'a[href="https://example.com"]' ); + expect( link ).toBeTruthy(); + expect( link.textContent ).toBe( 'Click here' ); + } ); + + test( 'refetches fields when userId changes', async () => { + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + const { rerender } = render( + + ); + + await waitFor( () => { + expect( apiFetch ).toHaveBeenCalledTimes( 1 ); + } ); + + // Change user + rerender( + + ); + + await waitFor( () => { + expect( apiFetch ).toHaveBeenCalledTimes( 2 ); + } ); + + expect( apiFetch ).toHaveBeenLastCalledWith( { + path: '/activitypub/1.0/actors/2', + headers: { Accept: 'application/activity+json' }, + } ); + } ); + + test( 'handles empty attachment array in actor response', async () => { + apiFetch.mockResolvedValue( { + // No attachment property + } ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: '1', + maxFields: 0, + }; + + render( ); + + await waitFor( () => { + expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); + } ); + + expect( screen.getByText( 'No extra fields found. Add fields in your profile settings.' ) ).toBeTruthy(); + } ); + + test( 'does not fetch when userId is null', () => { + useSelect.mockReturnValue( null ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'inherit', + maxFields: 0, + }; + + render( ); + + // Should not call apiFetch + expect( apiFetch ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index b974d0359..e5797a938 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -216,7 +216,11 @@ export default function Edit( { attributes, setAttributes, context } ) {
{ displayFields.map( ( field, index ) => ( -
+
{ field.name }
Date: Tue, 11 Nov 2025 13:13:48 +0100 Subject: [PATCH 08/17] Remove unused $block parameter in user ID retrieval Simplifies the Extra Fields block render callback by removing the unused $block->context argument from Blocks::get_user_id. This change clarifies the function call and eliminates unnecessary code. --- build/extra-fields/render.php | 8 +++----- src/extra-fields/render.php | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build/extra-fields/render.php b/build/extra-fields/render.php index d506f63ef..73bf30d8c 100644 --- a/build/extra-fields/render.php +++ b/build/extra-fields/render.php @@ -8,15 +8,13 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; -$attributes = wp_parse_args( $attributes ); - /** * Render callback for the Extra Fields block. * - * @var array $attributes Block attributes. - * @var WP_Block $block Block instance. + * @var array $attributes Block attributes. */ -$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable +$attributes = wp_parse_args( $attributes ); +$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog' ); // If user ID couldn't be determined, return empty. if ( null === $user_id ) { diff --git a/src/extra-fields/render.php b/src/extra-fields/render.php index d506f63ef..73bf30d8c 100644 --- a/src/extra-fields/render.php +++ b/src/extra-fields/render.php @@ -8,15 +8,13 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; -$attributes = wp_parse_args( $attributes ); - /** * Render callback for the Extra Fields block. * - * @var array $attributes Block attributes. - * @var WP_Block $block Block instance. + * @var array $attributes Block attributes. */ -$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog', $block->context ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable +$attributes = wp_parse_args( $attributes ); +$user_id = Blocks::get_user_id( $attributes['selectedUser'] ?? 'blog' ); // If user ID couldn't be determined, return empty. if ( null === $user_id ) { From 5f77e9b69354d32e7506ed7f056a29f6befbc678 Mon Sep 17 00:00:00 2001 From: Automattic Bot Date: Tue, 11 Nov 2025 14:15:35 +0200 Subject: [PATCH 09/17] Add changelog --- .github/changelog/2439-from-description | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/changelog/2439-from-description diff --git a/.github/changelog/2439-from-description b/.github/changelog/2439-from-description new file mode 100644 index 000000000..d82f6f7fb --- /dev/null +++ b/.github/changelog/2439-from-description @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Added a new Fediverse Profile Fields block to display ActivityPub profile details, featuring default, compact, and card layouts with flexible user selection options. From 8f8e458eee24279a7d8cd883b7b2adbee44ba932 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 13:21:13 +0100 Subject: [PATCH 10/17] Add tests for Extra Fields block rendering Introduces unit tests for the Activitypub Extra Fields block, covering rendering for blog and user contexts, maxFields attribute, empty fields, cards style, background color, and HTML content preservation. Enhances test coverage for block functionality and edge cases. --- .../tests/includes/class-test-blocks.php | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/tests/phpunit/tests/includes/class-test-blocks.php b/tests/phpunit/tests/includes/class-test-blocks.php index 9579994a1..0553c57d1 100644 --- a/tests/phpunit/tests/includes/class-test-blocks.php +++ b/tests/phpunit/tests/includes/class-test-blocks.php @@ -8,6 +8,7 @@ namespace Activitypub\Tests; use Activitypub\Blocks; +use Activitypub\Collection\Extra_Fields; use Activitypub\Collection\Interactions; use function Activitypub\object_to_uri; @@ -18,6 +19,75 @@ * @coversDefaultClass \Activitypub\Blocks */ class Test_Blocks extends \WP_UnitTestCase { + + /** + * User ID for Extra Fields block tests. + * + * @var int + */ + private static $extra_fields_user_id; + + /** + * Set up before class. + * + * @param \WP_UnitTest_Factory $factory Factory instance. + */ + public static function wpSetUpBeforeClass( $factory ) { + // Create test user for Extra Fields block tests. + self::$extra_fields_user_id = $factory->user->create( + array( + 'user_login' => 'extrafieldsuser', + 'user_email' => 'extrafields@example.com', + ) + ); + + // Create some extra fields for the user. + $factory->post->create( + array( + 'post_type' => Extra_Fields::USER_POST_TYPE, + 'post_title' => 'Website', + 'post_content' => '

example.com

', + 'post_status' => 'publish', + 'post_author' => self::$extra_fields_user_id, + 'menu_order' => 10, + ) + ); + + $factory->post->create( + array( + 'post_type' => Extra_Fields::USER_POST_TYPE, + 'post_title' => 'Location', + 'post_content' => '

San Francisco, CA

', + 'post_status' => 'publish', + 'post_author' => self::$extra_fields_user_id, + 'menu_order' => 20, + ) + ); + + $factory->post->create( + array( + 'post_type' => Extra_Fields::USER_POST_TYPE, + 'post_title' => 'Pronouns', + 'post_content' => '

they/them

', + 'post_status' => 'publish', + 'post_author' => self::$extra_fields_user_id, + 'menu_order' => 30, + ) + ); + + // Create extra fields for blog. + $factory->post->create( + array( + 'post_type' => Extra_Fields::BLOG_POST_TYPE, + 'post_title' => 'Blog Website', + 'post_content' => '

blog.example.com

', + 'post_status' => 'publish', + 'post_author' => self::$extra_fields_user_id, + 'menu_order' => 10, + ) + ); + } + /** * Test register_post_meta. * @@ -498,4 +568,150 @@ public function filter_pleroma_object( $response, $url ) { return $response; } + + /** + * Test Extra Fields block rendering with blog user. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + * @covers \Activitypub\Collection\Extra_Fields::get_formatted_content + */ + public function test_render_extra_fields_block_with_blog_user() { + $block_markup = ''; + $output = do_blocks( $block_markup ); + + $this->assertStringContainsString( 'activitypub-extra-fields-block-wrapper', $output ); + $this->assertStringContainsString( 'Blog Website', $output ); + $this->assertStringContainsString( 'blog.example.com', $output ); + } + + /** + * Test Extra Fields block rendering with specific user ID. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + * @covers \Activitypub\Collection\Extra_Fields::get_formatted_content + */ + public function test_render_extra_fields_block_with_specific_user() { + $block_markup = sprintf( + '', + self::$extra_fields_user_id + ); + $output = do_blocks( $block_markup ); + + $this->assertStringContainsString( 'Website', $output ); + $this->assertStringContainsString( 'example.com', $output ); + $this->assertStringContainsString( 'Location', $output ); + $this->assertStringContainsString( 'San Francisco, CA', $output ); + $this->assertStringContainsString( 'Pronouns', $output ); + $this->assertStringContainsString( 'they/them', $output ); + } + + /** + * Test Extra Fields block maxFields attribute limits output. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + */ + public function test_render_extra_fields_block_with_max_fields() { + $block_markup = sprintf( + '', + self::$extra_fields_user_id + ); + $output = do_blocks( $block_markup ); + + // Should contain first two fields. + $this->assertStringContainsString( 'Website', $output ); + $this->assertStringContainsString( 'Location', $output ); + + // Should not contain third field. + $this->assertStringNotContainsString( 'Pronouns', $output ); + $this->assertStringNotContainsString( 'they/them', $output ); + } + + /** + * Test Extra Fields block with no extra fields returns empty. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + */ + public function test_render_extra_fields_block_with_no_fields() { + $user_id = self::factory()->user->create( + array( + 'user_login' => 'emptyuser', + 'user_email' => 'empty@example.com', + ) + ); + + // Prevent default extra fields from being created. + add_filter( + 'activitypub_get_actor_extra_fields', + function ( $fields, $uid ) use ( $user_id ) { + if ( $uid === $user_id ) { + return array(); + } + return $fields; + }, + 10, + 2 + ); + + $block_markup = sprintf( + '', + $user_id + ); + $output = do_blocks( $block_markup ); + + $this->assertEmpty( $output ); + + remove_all_filters( 'activitypub_get_actor_extra_fields' ); + } + + /** + * Test Extra Fields block with cards style and background color. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + */ + public function test_render_extra_fields_block_with_cards_style() { + $block_markup = sprintf( + '', + self::$extra_fields_user_id + ); + $output = do_blocks( $block_markup ); + + $this->assertStringContainsString( 'is-style-cards', $output ); + $this->assertStringContainsString( 'var(--wp--preset--color--primary)', $output ); + } + + /** + * Test Extra Fields block preserves HTML in field content. + * + * @covers ::get_user_id + * @covers \Activitypub\Collection\Extra_Fields::get_actor_fields + * @covers \Activitypub\Collection\Extra_Fields::get_formatted_content + */ + public function test_render_extra_fields_block_preserves_html() { + $field_id = self::factory()->post->create( + array( + 'post_type' => Extra_Fields::USER_POST_TYPE, + 'post_title' => 'Rich Content', + 'post_content' => '

Visit my site at test.com

', + 'post_status' => 'publish', + 'post_author' => self::$extra_fields_user_id, + 'menu_order' => 40, + ) + ); + + $block_markup = sprintf( + '', + self::$extra_fields_user_id + ); + $output = do_blocks( $block_markup ); + + $this->assertStringContainsString( 'my site', $output ); + $this->assertStringContainsString( ' Date: Tue, 11 Nov 2025 14:51:55 +0100 Subject: [PATCH 11/17] Improve author detection and controls in Extra Fields block Enhanced the logic for detecting the post author in the Extra Fields block, including support for Query Loop context. Inspector controls are now always rendered, even when no author is present in inherit mode, improving recovery and usability. Added tests for context-based author detection and updated PHP render logic to skip output for ActivityPub requests and feeds. --- build/extra-fields/index.asset.php | 2 +- build/extra-fields/index.js | 2 +- build/extra-fields/render.php | 6 ++ src/extra-fields/__tests__/edit.test.js | 64 +++++++++++-- src/extra-fields/edit.js | 118 +++++++++++++----------- src/extra-fields/render.php | 6 ++ 6 files changed, 136 insertions(+), 62 deletions(-) diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php index 96a8d4429..c63f75989 100644 --- a/build/extra-fields/index.asset.php +++ b/build/extra-fields/index.asset.php @@ -1 +1 @@ - array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'aa5f020aacde684c5c7c'); + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '6fa49668f44308452c4d'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js index ea0188a34..60570ae18 100644 --- a/build/extra-fields/index.js +++ b/build/extra-fields/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={489:(e,t,i)=>{const s=window.wp.blocks,l=window.wp.i18n,r=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,s.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:s,maxFields:c}=e,[p,h]=(0,o.useState)([]),[v,b]=(0,o.useState)(!1),[y,x]=(0,o.useState)(null),f=(0,r.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),w=(0,a.useSelect)(e=>{const t=e("core/editor");return t?t.getCurrentPostAttribute("author"):null},[]),m=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[s,r]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:s}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&s("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>r(!0)).catch(()=>r(!1))},[n,c,u]);const p=n||(u&&s?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,l.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,l.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0}),_="blog"===s?0:"inherit"===s?w||null:s;(0,o.useEffect)(()=>{null!==_?(b(!0),x(null),d()({path:`/activitypub/1.0/actors/${_}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);h(t),b(!1)}).catch(e=>{x(e.message),b(!1)})):h([])},[_]);const g=c>0?p.slice(0,c):p,j=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},s=e.backgroundColor,l=i.color?.background;return s?{backgroundColor:`var(--wp--preset--color--${s})`}:l?{backgroundColor:l}:{}})();return"inherit"!==s||w?v?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):y?(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,l.__)("Error loading extra fields:","activitypub")," ",y]})})}):0===g.length?(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:m,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(r.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,l.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,l.__)("User","activitypub"),value:s,options:m,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,l.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,l.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})}),(0,u.jsx)("div",{...f,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:g.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:j,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsx)("div",{...f,children:(0,u.jsx)(n.Placeholder,{label:(0,l.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,l.__)("This block will display extra fields based on the post author when published.","activitypub")})})})},save:function(){return null}})}},i={};function s(e){var l=i[e];if(void 0!==l)return l.exports;var r=i[e]={exports:{}};return t[e](r,r.exports,s),r.exports}s.m=t,e=[],s.O=(t,i,l,r)=>{if(!i){var n=1/0;for(d=0;d=r)&&Object.keys(s.O).every(e=>s.O[e](i[o]))?i.splice(o--,1):(a=!1,r0&&e[d-1][2]>r;d--)e[d]=e[d-1];e[d]=[i,l,r]},s.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return s.d(t,{a:t}),t},s.d=(e,t)=>{for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};s.O.j=t=>0===e[t];var t=(t,i)=>{var l,r,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(l in a)s.o(a,l)&&(s.m[l]=a[l]);if(o)var d=o(s)}for(t&&t(i);cs(489));l=s.O(l)})(); \ No newline at end of file +(()=>{"use strict";var e,t={489:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:p,postType:h}=null!=i?i:{},[v,b]=(0,o.useState)([]),[y,f]=(0,o.useState)(!1),[w,g]=(0,o.useState)(null),x=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),m=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(p&&h&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",h,p))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",h,p))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[p,h]),_=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,u]);const p=n||(u&&r?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0}),j="blog"===r?0:"inherit"===r?m||null:r;(0,o.useEffect)(()=>{null!==j?(f(!0),g(null),d()({path:`/activitypub/1.0/actors/${j}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);b(t),f(!1)}).catch(e=>{g(e.message),f(!1)})):b([])},[j]);const P=c>0?v.slice(0,c):v,k=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),F=(0,u.jsx)(l.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||m?y?(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):w?(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",w]})})}):0===P.length?(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:P.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:k,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:function(){return null}})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(489));s=r.O(s)})(); \ No newline at end of file diff --git a/build/extra-fields/render.php b/build/extra-fields/render.php index 73bf30d8c..1f51a7285 100644 --- a/build/extra-fields/render.php +++ b/build/extra-fields/render.php @@ -8,6 +8,12 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; +use function Activitypub\is_activitypub_request; + +if ( is_activitypub_request() || is_feed() ) { + return ''; +} + /** * Render callback for the Extra Fields block. * diff --git a/src/extra-fields/__tests__/edit.test.js b/src/extra-fields/__tests__/edit.test.js index 5589d7871..cd555b86e 100644 --- a/src/extra-fields/__tests__/edit.test.js +++ b/src/extra-fields/__tests__/edit.test.js @@ -74,6 +74,9 @@ jest.mock( '../../shared/use-user-options', () => ( { const originalError = console.error; describe( 'Extra Fields Edit Component', () => { + let mockEditorStore; + let mockCoreStore; + beforeAll( () => { console.error = jest.fn(); } ); @@ -83,11 +86,31 @@ describe( 'Extra Fields Edit Component', () => { } ); beforeEach( () => { - // Reset mocks before each test + // Reset mocks before each test. jest.clearAllMocks(); - // Default mock for useSelect - useSelect.mockReturnValue( 1 ); // Default authorId + mockEditorStore = { + getCurrentPostAttribute: jest.fn().mockReturnValue( 1 ), + }; + + mockCoreStore = { + getEditedEntityRecord: jest.fn().mockReturnValue( null ), + getEntityRecord: jest.fn().mockReturnValue( null ), + }; + + useSelect.mockImplementation( ( callback ) => + callback( ( storeName ) => { + if ( 'core/editor' === storeName ) { + return mockEditorStore; + } + + if ( 'core' === storeName ) { + return mockCoreStore; + } + + return null; + } ) + ); // Default mock for apiFetch apiFetch.mockResolvedValue( { @@ -152,7 +175,7 @@ describe( 'Extra Fields Edit Component', () => { } ); test( 'shows placeholder when inherit mode but no author', () => { - useSelect.mockReturnValue( null ); // No author + mockEditorStore.getCurrentPostAttribute.mockReturnValue( null ); const setAttributes = jest.fn(); const attributes = { @@ -163,6 +186,7 @@ describe( 'Extra Fields Edit Component', () => { render( ); expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); + expect( screen.getByTestId( 'inspector-controls' ) ).toBeTruthy(); expect( screen.getByText( 'This block will display extra fields based on the post author when published.' ) ).toBeTruthy(); @@ -241,7 +265,7 @@ describe( 'Extra Fields Edit Component', () => { } ); test( 'fetches author fields when selectedUser is inherit', async () => { - useSelect.mockReturnValue( 5 ); // authorId = 5 + mockEditorStore.getCurrentPostAttribute.mockReturnValue( 5 ); // authorId = 5 const setAttributes = jest.fn(); const attributes = { @@ -259,6 +283,34 @@ describe( 'Extra Fields Edit Component', () => { } ); } ); + test( 'uses context author when inherit mode is inside Query Loop', async () => { + mockEditorStore.getCurrentPostAttribute.mockReturnValue( null ); + mockCoreStore.getEditedEntityRecord.mockReturnValue( { + author: 9, + } ); + + const setAttributes = jest.fn(); + const attributes = { + selectedUser: 'inherit', + maxFields: 0, + }; + + render( + + ); + + await waitFor( () => { + expect( apiFetch ).toHaveBeenCalledWith( { + path: '/activitypub/1.0/actors/9', + headers: { Accept: 'application/activity+json' }, + } ); + } ); + } ); + test( 'filters attachment array to only PropertyValue types', async () => { apiFetch.mockResolvedValue( { attachment: [ @@ -449,7 +501,7 @@ describe( 'Extra Fields Edit Component', () => { } ); test( 'does not fetch when userId is null', () => { - useSelect.mockReturnValue( null ); + mockEditorStore.getCurrentPostAttribute.mockReturnValue( null ); const setAttributes = jest.fn(); const attributes = { diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index e5797a938..10d3f1e03 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -24,6 +24,7 @@ import { useUserOptions } from '../shared/use-user-options'; */ export default function Edit( { attributes, setAttributes, context } ) { const { selectedUser, maxFields } = attributes; + const { postId: contextPostId, postType: contextPostType } = context ?? {}; const [ fields, setFields ] = useState( [] ); const [ isLoading, setIsLoading ] = useState( false ); const [ error, setError ] = useState( null ); @@ -32,14 +33,33 @@ export default function Edit( { attributes, setAttributes, context } ) { className: 'activitypub-extra-fields-block-wrapper', } ); - // Get author ID from context - const authorId = useSelect( ( select ) => { - const editor = select( 'core/editor' ); - if ( ! editor ) { + // Get author ID from context or current post depending on editor. + const authorId = useSelect( + ( select ) => { + const editorStore = select( 'core/editor' ); + const coreStore = select( 'core' ); + + if ( contextPostId && contextPostType && coreStore ) { + const editedRecord = + coreStore.getEditedEntityRecord?.( 'postType', contextPostType, contextPostId ) ?? null; + if ( editedRecord?.author ) { + return editedRecord.author; + } + + const record = coreStore.getEntityRecord?.( 'postType', contextPostType, contextPostId ) ?? null; + if ( record?.author ) { + return record.author; + } + } + + if ( editorStore && editorStore.getCurrentPostAttribute ) { + return editorStore.getCurrentPostAttribute( 'author' ); + } + return null; - } - return editor.getCurrentPostAttribute( 'author' ); - }, [] ); + }, + [ contextPostId, contextPostType ] + ); // Get user options for dropdown const userOptions = useUserOptions( { @@ -122,19 +142,43 @@ export default function Edit( { attributes, setAttributes, context } ) { const cardStyle = getCardStyle(); - // Render placeholder if inherit mode but no author + const settingsPanel = ( + + + setAttributes( { selectedUser: value } ) } + /> + setAttributes( { maxFields: value } ) } + min={ 0 } + max={ 20 } + help={ __( 'Limit the number of fields displayed. 0 = show all.', 'activitypub' ) } + /> + + + ); + + // Render placeholder if inherit mode but no author. Keep controls mounted for recovery. if ( selectedUser === 'inherit' && ! authorId ) { return ( -
- -

- { __( - 'This block will display extra fields based on the post author when published.', - 'activitypub' - ) } -

-
-
+ <> + { settingsPanel } +
+ +

+ { __( + 'This block will display extra fields based on the post author when published.', + 'activitypub' + ) } +

+
+
+ ); } @@ -166,24 +210,7 @@ export default function Edit( { attributes, setAttributes, context } ) { if ( displayFields.length === 0 ) { return ( <> - - - setAttributes( { selectedUser: value } ) } - /> - setAttributes( { maxFields: value } ) } - min={ 0 } - max={ 20 } - help={ __( 'Limit the number of fields displayed. 0 = show all.', 'activitypub' ) } - /> - - + { settingsPanel }

{ __( 'No extra fields found. Add fields in your profile settings.', 'activitypub' ) }

@@ -195,24 +222,7 @@ export default function Edit( { attributes, setAttributes, context } ) { return ( <> - - - setAttributes( { selectedUser: value } ) } - /> - setAttributes( { maxFields: value } ) } - min={ 0 } - max={ 20 } - help={ __( 'Limit the number of fields displayed. 0 = show all.', 'activitypub' ) } - /> - - + { settingsPanel }
{ displayFields.map( ( field, index ) => ( diff --git a/src/extra-fields/render.php b/src/extra-fields/render.php index 73bf30d8c..1f51a7285 100644 --- a/src/extra-fields/render.php +++ b/src/extra-fields/render.php @@ -8,6 +8,12 @@ use Activitypub\Blocks; use Activitypub\Collection\Extra_Fields; +use function Activitypub\is_activitypub_request; + +if ( is_activitypub_request() || is_feed() ) { + return ''; +} + /** * Render callback for the Extra Fields block. * From b82e809ff1b35cfe4d91cacbc181eb836a46d21a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 11 Nov 2025 14:52:49 +0100 Subject: [PATCH 12/17] Add paragraph margin styles to extra field descriptions Updated SCSS and built CSS files to style

elements within .activitypub-extra-field dd, ensuring proper spacing and removing bottom margin from the last paragraph. This improves readability and consistency for extra field content. --- build/extra-fields/style-index-rtl.css | 2 +- build/extra-fields/style-index.css | 2 +- src/extra-fields/style.scss | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build/extra-fields/style-index-rtl.css b/build/extra-fields/style-index-rtl.css index 6fb491f57..237741f69 100644 --- a/build/extra-fields/style-index-rtl.css +++ b/build/extra-fields/style-index-rtl.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/build/extra-fields/style-index.css b/build/extra-fields/style-index.css index 339d33b64..e5efe73aa 100644 --- a/build/extra-fields/style-index.css +++ b/build/extra-fields/style-index.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/src/extra-fields/style.scss b/src/extra-fields/style.scss index 34f4649ce..9ae8d050a 100644 --- a/src/extra-fields/style.scss +++ b/src/extra-fields/style.scss @@ -40,6 +40,15 @@ margin-bottom: 0; color: inherit; + p { + margin-top: 0; + margin-bottom: 0.5em; + + &:last-child { + margin-bottom: 0; + } + } + a { color: inherit; text-decoration: underline; From ea57a71e382031b7370f0ec8056efdd8907e259e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 13 Nov 2025 18:18:15 +0100 Subject: [PATCH 13/17] Rename block and classes to 'Fediverse Extra Fields' Updated block title, description, and UI labels from 'Fediverse Profile Fields' to 'Fediverse Extra Fields' for clarity. CSS class names and references in JS were also changed to match the new naming convention. --- .github/changelog/2439-from-description | 2 +- assets/css/activitypub-welcome.css | 6 ++--- build/extra-fields/block.json | 4 +-- build/extra-fields/index.asset.php | 2 +- build/extra-fields/index.js | 2 +- src/extra-fields/block.json | 4 +-- src/extra-fields/edit.js | 34 ++++++++++++------------- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/changelog/2439-from-description b/.github/changelog/2439-from-description index d82f6f7fb..8f0f85296 100644 --- a/.github/changelog/2439-from-description +++ b/.github/changelog/2439-from-description @@ -1,4 +1,4 @@ Significance: minor Type: added -Added a new Fediverse Profile Fields block to display ActivityPub profile details, featuring default, compact, and card layouts with flexible user selection options. +Added a new Fediverse Extra Fields block to display ActivityPub extra fields, featuring default, compact, and card layouts with flexible user selection options. diff --git a/assets/css/activitypub-welcome.css b/assets/css/activitypub-welcome.css index 182d8e19d..3faf3ffbf 100644 --- a/assets/css/activitypub-welcome.css +++ b/assets/css/activitypub-welcome.css @@ -223,11 +223,11 @@ padding: 15px; } -.profile-field { +.extra-field { margin-bottom: 15px; } -.profile-field label { +.extra-field label { display: block; margin-bottom: 5px; font-weight: 500; @@ -235,7 +235,7 @@ color: #646970; } -.profile-field input { +.extra-field input { width: 100%; padding: 8px; font-size: 13px; diff --git a/build/extra-fields/block.json b/build/extra-fields/block.json index f5d48dceb..6e73c1557 100644 --- a/build/extra-fields/block.json +++ b/build/extra-fields/block.json @@ -3,9 +3,9 @@ "name": "activitypub/extra-fields", "apiVersion": 3, "version": "1.0.0", - "title": "Fediverse Profile Fields", + "title": "Fediverse Extra Fields", "category": "widgets", - "description": "Display extra profile fields from ActivityPub user profiles", + "description": "Display extra fields from ActivityPub user profiles", "textdomain": "activitypub", "icon": "list-view", "supports": { diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php index c63f75989..0a3afb210 100644 --- a/build/extra-fields/index.asset.php +++ b/build/extra-fields/index.asset.php @@ -1 +1 @@ - array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '6fa49668f44308452c4d'); + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '5cf46c292b43f73153ae'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js index 60570ae18..0fa24a5e5 100644 --- a/build/extra-fields/index.js +++ b/build/extra-fields/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={489:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:p,postType:h}=null!=i?i:{},[v,b]=(0,o.useState)([]),[y,f]=(0,o.useState)(!1),[w,g]=(0,o.useState)(null),x=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),m=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(p&&h&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",h,p))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",h,p))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[p,h]),_=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,u]);const p=n||(u&&r?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0}),j="blog"===r?0:"inherit"===r?m||null:r;(0,o.useEffect)(()=>{null!==j?(f(!0),g(null),d()({path:`/activitypub/1.0/actors/${j}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);b(t),f(!1)}).catch(e=>{g(e.message),f(!1)})):b([])},[j]);const P=c>0?v.slice(0,c):v,k=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),F=(0,u.jsx)(l.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:_,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||m?y?(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):w?(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",w]})})}):0===P.length?(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:P.map((e,t)=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:k,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsxs)(u.Fragment,{children:[F,(0,u.jsx)("div",{...x,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Profile Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:function(){return null}})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(489));s=r.O(s)})(); \ No newline at end of file +(()=>{"use strict";var e,t={489:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:p,postType:h}=null!=i?i:{},[v,b]=(0,o.useState)([]),[y,x]=(0,o.useState)(!1),[f,w]=(0,o.useState)(null),g=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(p&&h&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",h,p))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",h,p))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[p,h]),m="blog"===r?0:"inherit"===r?g||null:r,_=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),j=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,u]);const p=n||(u&&r?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0});(0,o.useEffect)(()=>{null!==m?(x(!0),w(null),d()({path:`/activitypub/1.0/actors/${m}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);b(t),x(!1)}).catch(e=>{w(e.message),x(!1)})):b([])},[m]);const k=c>0?v.slice(0,c):v,F=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),U=(0,u.jsx)(l.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:j,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||g?y?(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):f?(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",f]})})}):0===k.length?(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:k.map(e=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:F,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:function(){return null}})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(489));s=r.O(s)})(); \ No newline at end of file diff --git a/src/extra-fields/block.json b/src/extra-fields/block.json index 7175f48d7..7d348fc0b 100644 --- a/src/extra-fields/block.json +++ b/src/extra-fields/block.json @@ -3,9 +3,9 @@ "name": "activitypub/extra-fields", "apiVersion": 3, "version": "1.0.0", - "title": "Fediverse Profile Fields", + "title": "Fediverse Extra Fields", "category": "widgets", - "description": "Display extra profile fields from ActivityPub user profiles", + "description": "Display extra fields from ActivityPub user profiles", "textdomain": "activitypub", "icon": "list-view", "supports": { diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index 10d3f1e03..05ccba860 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -16,10 +16,10 @@ import { useUserOptions } from '../shared/use-user-options'; /** * Editor component for Extra Fields block. * - * @param {Object} props Component props. - * @param {Object} props.attributes Block attributes. + * @param {Object} props Component props. + * @param {Object} props.attributes Block attributes. * @param {Function} props.setAttributes Function to set attributes. - * @param {Object} props.context Block context. + * @param {Object} props.context Block context. * @return {Element} Component element. */ export default function Edit( { attributes, setAttributes, context } ) { @@ -29,10 +29,6 @@ export default function Edit( { attributes, setAttributes, context } ) { const [ isLoading, setIsLoading ] = useState( false ); const [ error, setError ] = useState( null ); - const blockProps = useBlockProps( { - className: 'activitypub-extra-fields-block-wrapper', - } ); - // Get author ID from context or current post depending on editor. const authorId = useSelect( ( select ) => { @@ -61,11 +57,6 @@ export default function Edit( { attributes, setAttributes, context } ) { [ contextPostId, contextPostType ] ); - // Get user options for dropdown - const userOptions = useUserOptions( { - withInherit: true, - } ); - // Determine which user ID to fetch const getUserId = () => { if ( selectedUser === 'blog' ) { @@ -84,6 +75,15 @@ export default function Edit( { attributes, setAttributes, context } ) { const userId = getUserId(); + const blockProps = useBlockProps( { + className: 'activitypub-extra-fields-block-wrapper', + } ); + + // Get user options for dropdown + const userOptions = useUserOptions( { + withInherit: true, + } ); + // Fetch extra fields useEffect( () => { if ( userId === null ) { @@ -169,7 +169,7 @@ export default function Edit( { attributes, setAttributes, context } ) { <> { settingsPanel }

- +

{ __( 'This block will display extra fields based on the post author when published.', @@ -186,7 +186,7 @@ export default function Edit( { attributes, setAttributes, context } ) { if ( isLoading ) { return (

- +
@@ -197,7 +197,7 @@ export default function Edit( { attributes, setAttributes, context } ) { if ( error ) { return (
- +

{ __( 'Error loading extra fields:', 'activitypub' ) } { error }

@@ -212,7 +212,7 @@ export default function Edit( { attributes, setAttributes, context } ) { <> { settingsPanel }
- +

{ __( 'No extra fields found. Add fields in your profile settings.', 'activitypub' ) }

@@ -225,7 +225,7 @@ export default function Edit( { attributes, setAttributes, context } ) { { settingsPanel }
- { displayFields.map( ( field, index ) => ( + { displayFields.map( ( field ) => (
Date: Thu, 13 Nov 2025 18:20:27 +0100 Subject: [PATCH 14/17] Rename block styles and update styling for extra fields Changed the default block style from 'default' to 'compact', renamed the previous 'compact' style to 'stacked', and updated related CSS/SCSS to support the new naming and layout. This improves clarity and consistency in style options for the Fediverse Extra Fields block. --- .github/changelog/2439-from-description | 2 +- build/extra-fields/block.json | 8 ++++---- build/extra-fields/style-index-rtl.css | 2 +- build/extra-fields/style-index.css | 2 +- src/extra-fields/block.json | 8 ++++---- src/extra-fields/style.scss | 10 ++++++++-- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/changelog/2439-from-description b/.github/changelog/2439-from-description index 8f0f85296..67236440c 100644 --- a/.github/changelog/2439-from-description +++ b/.github/changelog/2439-from-description @@ -1,4 +1,4 @@ Significance: minor Type: added -Added a new Fediverse Extra Fields block to display ActivityPub extra fields, featuring default, compact, and card layouts with flexible user selection options. +Added a new Fediverse Extra Fields block to display ActivityPub extra fields, featuring compact, stacked, and card layouts with flexible user selection options. diff --git a/build/extra-fields/block.json b/build/extra-fields/block.json index 6e73c1557..81c721730 100644 --- a/build/extra-fields/block.json +++ b/build/extra-fields/block.json @@ -39,13 +39,13 @@ }, "styles": [ { - "name": "default", - "label": "Default", + "name": "compact", + "label": "Compact", "isDefault": true }, { - "name": "compact", - "label": "Compact" + "name": "stacked", + "label": "Stacked" }, { "name": "cards", diff --git a/build/extra-fields/style-index-rtl.css b/build/extra-fields/style-index-rtl.css index 237741f69..fbb6e0fb8 100644 --- a/build/extra-fields/style-index-rtl.css +++ b/build/extra-fields/style-index-rtl.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/build/extra-fields/style-index.css b/build/extra-fields/style-index.css index e5efe73aa..3b2a83fd9 100644 --- a/build/extra-fields/style-index.css +++ b/build/extra-fields/style-index.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} diff --git a/src/extra-fields/block.json b/src/extra-fields/block.json index 7d348fc0b..fec400f06 100644 --- a/src/extra-fields/block.json +++ b/src/extra-fields/block.json @@ -36,13 +36,13 @@ }, "styles": [ { - "name": "default", - "label": "Default", + "name": "compact", + "label": "Compact", "isDefault": true }, { - "name": "compact", - "label": "Compact" + "name": "stacked", + "label": "Stacked" }, { "name": "cards", diff --git a/src/extra-fields/style.scss b/src/extra-fields/style.scss index 9ae8d050a..77952101e 100644 --- a/src/extra-fields/style.scss +++ b/src/extra-fields/style.scss @@ -3,9 +3,11 @@ */ .activitypub-extra-fields-block-wrapper { - // Add padding to content when background or border is set (default and compact styles) + // Add padding to content when background or border is set (stacked and compact styles) &.has-background .activitypub-extra-fields, - &.has-border .activitypub-extra-fields { + &.has-border .activitypub-extra-fields, + &.is-style-stacked.has-background .activitypub-extra-fields, + &.is-style-stacked.has-border .activitypub-extra-fields { padding: 1rem; } @@ -22,6 +24,10 @@ list-style: none; } +/** + * Stacked Style (also default fallback) + * Displays fields in a stacked format with label on top and value below. + */ .activitypub-extra-field { margin-bottom: 1em; From 2f3054b89ded24b60f5db353695581d4d90bf4db Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 14 Nov 2025 07:54:19 +0100 Subject: [PATCH 15/17] Refactor extra fields block styles and remove save.js Consolidates block save logic by removing the unused save.js file and updating registration to use an inline save function returning null. Refactors CSS and SCSS for the extra fields block to make the default style compact (table layout), and adjusts stacked and card styles for consistency. Adds support for a configurable namespace in API requests. --- build/extra-fields/index.asset.php | 2 +- build/extra-fields/index.js | 2 +- build/extra-fields/style-index-rtl.css | 2 +- build/extra-fields/style-index.css | 2 +- src/extra-fields/edit.js | 14 ++-- src/extra-fields/index.js | 5 +- src/extra-fields/save.js | 10 --- src/extra-fields/style.scss | 88 ++++++++++++++++++-------- 8 files changed, 77 insertions(+), 48 deletions(-) delete mode 100644 src/extra-fields/save.js diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php index 0a3afb210..a3392fb57 100644 --- a/build/extra-fields/index.asset.php +++ b/build/extra-fields/index.asset.php @@ -1 +1 @@ - array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '5cf46c292b43f73153ae'); + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'cabfd88d050b1444105c'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js index 0fa24a5e5..bc3a1a6bb 100644 --- a/build/extra-fields/index.js +++ b/build/extra-fields/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={489:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);const u=window.ReactJSXRuntime,p=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(p.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:p,postType:h}=null!=i?i:{},[v,b]=(0,o.useState)([]),[y,x]=(0,o.useState)(!1),[f,w]=(0,o.useState)(null),g=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(p&&h&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",h,p))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",h,p))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[p,h]),m="blog"===r?0:"inherit"===r?g||null:r,_=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),j=function({withInherit:e=!1}){const{enabled:t,namespace:i}=window._activityPubOptions||{},[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),u=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!u||d()({path:`/${i}/actors/${u.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,u]);const p=n||(u&&r?[{id:u.id,name:u.name}]:[]);return(0,o.useMemo)(()=>{if(!p.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),p.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[p])}({withInherit:!0});(0,o.useEffect)(()=>{null!==m?(x(!0),w(null),d()({path:`/activitypub/1.0/actors/${m}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);b(t),x(!1)}).catch(e=>{w(e.message),x(!1)})):b([])},[m]);const k=c>0?v.slice(0,c):v,F=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),U=(0,u.jsx)(l.InspectorControls,{children:(0,u.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,u.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:j,onChange:e=>t({selectedUser:e})}),(0,u.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||g?y?(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)(n.Spinner,{})})}):f?(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",f]})})}):0===k.length?(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)("dl",{className:"activitypub-extra-fields",children:k.map(e=>(0,u.jsxs)("div",{className:"activitypub-extra-field",style:F,children:[(0,u.jsx)("dt",{children:e.name}),(0,u.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,u.jsxs)(u.Fragment,{children:[U,(0,u.jsx)("div",{..._,children:(0,u.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,u.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:function(){return null}})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(489));s=r.O(s)})(); \ No newline at end of file +(()=>{"use strict";var e,t={157:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);function u(){return window._activityPubOptions||{}}const p=window.ReactJSXRuntime,h=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(h.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:h,postType:v}=null!=i?i:{},[b,y]=(0,o.useState)([]),[x,f]=(0,o.useState)(!1),[w,g]=(0,o.useState)(null),m=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(h&&v&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",v,h))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",v,h))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[h,v]),_="blog"===r?0:"inherit"===r?m||null:r,{namespace:j="activitypub/1.0"}=u(),k=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),F=function({withInherit:e=!1}){const{enabled:t,namespace:i}=u(),[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),p=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!p||d()({path:`/${i}/actors/${p.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,p]);const h=n||(p&&r?[{id:p.id,name:p.name}]:[]);return(0,o.useMemo)(()=>{if(!h.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),h.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[h])}({withInherit:!0});(0,o.useEffect)(()=>{null!==_?(f(!0),g(null),d()({path:`/${j}/actors/${_}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);y(t),f(!1)}).catch(e=>{g(e.message),f(!1)})):y([])},[_,j]);const U=c>0?b.slice(0,c):b,O=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),C=(0,p.jsx)(l.InspectorControls,{children:(0,p.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,p.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:F,onChange:e=>t({selectedUser:e})}),(0,p.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||m?x?(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)(n.Spinner,{})})}):w?(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",w]})})}):0===U.length?(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)("dl",{className:"activitypub-extra-fields",children:U.map(e=>(0,p.jsxs)("div",{className:"activitypub-extra-field",style:O,children:[(0,p.jsx)("dt",{children:e.name}),(0,p.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:()=>null})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(157));s=r.O(s)})(); \ No newline at end of file diff --git a/build/extra-fields/style-index-rtl.css b/build/extra-fields/style-index-rtl.css index fbb6e0fb8..4f1e363b3 100644 --- a/build/extra-fields/style-index-rtl.css +++ b/build/extra-fields/style-index-rtl.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-right:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{display:table;list-style:none;margin:0;padding:0;table-layout:fixed}.activitypub-extra-field{display:table-row;margin-bottom:0}.activitypub-extra-field dt{color:inherit;display:table-cell;font-weight:600;margin-bottom:0;padding-bottom:.5em;padding-left:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.activitypub-extra-field dt:after{content:":"}.activitypub-extra-field dd{color:inherit;display:table-cell;margin-bottom:0;margin-right:0;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-stacked .activitypub-extra-fields{display:block;table-layout:auto}.is-style-stacked .activitypub-extra-field{display:block;margin-bottom:1em}.is-style-stacked .activitypub-extra-field:last-child{margin-bottom:0}.is-style-stacked .activitypub-extra-field dt{display:block;margin-bottom:.25em;padding-bottom:0;padding-left:0;text-overflow:clip;white-space:normal}.is-style-stacked .activitypub-extra-field dt:after{content:none}.is-style-stacked .activitypub-extra-field dd{display:block;padding-bottom:0}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-fields{display:block;table-layout:auto}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);display:block;margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;display:block;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;padding-bottom:0;padding-left:0;text-transform:uppercase;white-space:normal}.is-style-cards .activitypub-extra-field dt:after{content:none}.is-style-cards .activitypub-extra-field dd{display:block;font-size:1em;padding-bottom:0} diff --git a/build/extra-fields/style-index.css b/build/extra-fields/style-index.css index 3b2a83fd9..2c8cd64fa 100644 --- a/build/extra-fields/style-index.css +++ b/build/extra-fields/style-index.css @@ -1 +1 @@ -.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{list-style:none;margin:0;padding:0}.activitypub-extra-field{margin-bottom:1em}.activitypub-extra-field:last-child{margin-bottom:0}.activitypub-extra-field dt{color:inherit;font-weight:600;margin-bottom:.25em}.activitypub-extra-field dd{color:inherit;margin-bottom:0;margin-left:0}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-compact .activitypub-extra-fields{display:table;table-layout:fixed}.is-style-compact .activitypub-extra-field{display:table-row;margin-bottom:0}.is-style-compact .activitypub-extra-field dt{display:table-cell;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.is-style-compact .activitypub-extra-field dt:after{content:":"}.is-style-compact .activitypub-extra-field dd{display:table-cell;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;text-transform:uppercase}.is-style-cards .activitypub-extra-field dd{font-size:1em} +.activitypub-extra-fields-block-wrapper.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.has-border .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-stacked.has-border .activitypub-extra-fields{padding:1rem}.activitypub-extra-fields-block-wrapper.is-style-cards.has-background .activitypub-extra-fields,.activitypub-extra-fields-block-wrapper.is-style-cards.has-border .activitypub-extra-fields{padding:0}.activitypub-extra-fields{display:table;list-style:none;margin:0;padding:0;table-layout:fixed}.activitypub-extra-field{display:table-row;margin-bottom:0}.activitypub-extra-field dt{color:inherit;display:table-cell;font-weight:600;margin-bottom:0;padding-bottom:.5em;padding-right:10px;text-overflow:ellipsis;vertical-align:baseline;white-space:nowrap}.activitypub-extra-field dt:after{content:":"}.activitypub-extra-field dd{color:inherit;display:table-cell;margin-bottom:0;margin-left:0;padding-bottom:.5em;vertical-align:baseline;word-break:break-word}.activitypub-extra-field dd p{margin-bottom:.5em;margin-top:0}.activitypub-extra-field dd p:last-child{margin-bottom:0}.activitypub-extra-field dd a{color:inherit;text-decoration:underline}.activitypub-extra-field dd a:hover{text-decoration:none}.is-style-stacked .activitypub-extra-fields{display:block;table-layout:auto}.is-style-stacked .activitypub-extra-field{display:block;margin-bottom:1em}.is-style-stacked .activitypub-extra-field:last-child{margin-bottom:0}.is-style-stacked .activitypub-extra-field dt{display:block;margin-bottom:.25em;padding-bottom:0;padding-right:0;text-overflow:clip;white-space:normal}.is-style-stacked .activitypub-extra-field dt:after{content:none}.is-style-stacked .activitypub-extra-field dd{display:block;padding-bottom:0}.is-style-cards.has-background{background:transparent!important}.is-style-cards.has-background .activitypub-extra-fields{padding:1rem}.is-style-cards .activitypub-extra-fields{display:block;table-layout:auto}.is-style-cards .activitypub-extra-field{background:var(--wp--preset--color--base,#fff);border:1px solid var(--wp--preset--color--contrast-2,#ddd);border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.05);display:block;margin-bottom:1em;padding:1em}.is-style-cards .activitypub-extra-field:last-child{margin-bottom:0}.is-style-cards .activitypub-extra-field dt{color:currentColor;display:block;font-size:.9em;letter-spacing:.5px;margin-bottom:.5em;padding-bottom:0;padding-right:0;text-transform:uppercase;white-space:normal}.is-style-cards .activitypub-extra-field dt:after{content:none}.is-style-cards .activitypub-extra-field dd{display:block;font-size:1em;padding-bottom:0} diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index 05ccba860..4004b7977 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -11,15 +11,16 @@ import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies */ +import { useOptions } from '../shared/use-options'; import { useUserOptions } from '../shared/use-user-options'; /** * Editor component for Extra Fields block. * - * @param {Object} props Component props. - * @param {Object} props.attributes Block attributes. + * @param {Object} props Component props. + * @param {Object} props.attributes Block attributes. * @param {Function} props.setAttributes Function to set attributes. - * @param {Object} props.context Block context. + * @param {Object} props.context Block context. * @return {Element} Component element. */ export default function Edit( { attributes, setAttributes, context } ) { @@ -75,6 +76,9 @@ export default function Edit( { attributes, setAttributes, context } ) { const userId = getUserId(); + // Get ActivityPub options including namespace + const { namespace = 'activitypub/1.0' } = useOptions(); + const blockProps = useBlockProps( { className: 'activitypub-extra-fields-block-wrapper', } ); @@ -95,7 +99,7 @@ export default function Edit( { attributes, setAttributes, context } ) { setError( null ); apiFetch( { - path: `/activitypub/1.0/actors/${ userId }`, + path: `/${ namespace }/actors/${ userId }`, headers: { Accept: 'application/activity+json' }, } ) .then( ( actor ) => { @@ -110,7 +114,7 @@ export default function Edit( { attributes, setAttributes, context } ) { setError( err.message ); setIsLoading( false ); } ); - }, [ userId ] ); + }, [ userId, namespace ] ); // Apply max fields limit for preview const displayFields = maxFields > 0 ? fields.slice( 0, maxFields ) : fields; diff --git a/src/extra-fields/index.js b/src/extra-fields/index.js index 4e47b43e5..8823c1672 100644 --- a/src/extra-fields/index.js +++ b/src/extra-fields/index.js @@ -7,14 +7,15 @@ import { registerBlockType } from '@wordpress/blocks'; * Internal dependencies */ import edit from './edit'; -import save from './save'; import metadata from './block.json'; import './style.scss'; /** * Register the Extra Fields block. + * + * This block uses server-side rendering, so the save function returns null. */ registerBlockType( metadata.name, { edit, - save, + save: () => null, } ); diff --git a/src/extra-fields/save.js b/src/extra-fields/save.js deleted file mode 100644 index 414590768..000000000 --- a/src/extra-fields/save.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Save function for Extra Fields block. - * - * This block uses server-side rendering, so the save function returns null. - * - * @return {null} No save content. - */ -export default function save() { - return null; -} diff --git a/src/extra-fields/style.scss b/src/extra-fields/style.scss index 77952101e..b20081632 100644 --- a/src/extra-fields/style.scss +++ b/src/extra-fields/style.scss @@ -25,23 +25,40 @@ } /** - * Stacked Style (also default fallback) - * Displays fields in a stacked format with label on top and value below. + * Default Compact Style + * Displays fields in a table-like format with aligned labels and values. + * This is the default style, so it applies without a style class. */ -.activitypub-extra-field { - margin-bottom: 1em; +.activitypub-extra-fields { + display: table; + table-layout: fixed; +} - &:last-child { - margin-bottom: 0; - } +.activitypub-extra-field { + display: table-row; + margin-bottom: 0; dt { + display: table-cell; + padding-bottom: 0.5em; + margin-bottom: 0; + padding-right: 10px; + white-space: nowrap; + vertical-align: baseline; + text-overflow: ellipsis; font-weight: 600; - margin-bottom: 0.25em; color: inherit; + + &::after { + content: ':'; + } } dd { + display: table-cell; + padding-bottom: 0.5em; + vertical-align: baseline; + word-break: break-word; margin-left: 0; margin-bottom: 0; color: inherit; @@ -67,38 +84,39 @@ } /** - * Compact Style - * Displays fields in a table-like format with aligned labels and values. + * Stacked Style + * Displays fields in a stacked format with label on top and value below. */ -.is-style-compact { +.is-style-stacked { .activitypub-extra-fields { - display: table; - table-layout: fixed; + display: block; + table-layout: auto; } .activitypub-extra-field { - display: table-row; - margin-bottom: 0; + display: block; + margin-bottom: 1em; - dt { - display: table-cell; - padding-bottom: 0.5em; + &:last-child { margin-bottom: 0; - padding-right: 10px; - white-space: nowrap; - vertical-align: baseline; - text-overflow: ellipsis; + } + + dt { + display: block; + padding-bottom: 0; + padding-right: 0; + margin-bottom: 0.25em; + white-space: normal; + text-overflow: clip; &::after { - content: ':'; + content: none; } } dd { - display: table-cell; - padding-bottom: 0.5em; - vertical-align: baseline; - word-break: break-word; + display: block; + padding-bottom: 0; } } } @@ -117,7 +135,13 @@ } } + .activitypub-extra-fields { + display: block; + table-layout: auto; + } + .activitypub-extra-field { + display: block; border: 1px solid var(--wp--preset--color--contrast-2, #ddd); border-radius: 8px; padding: 1em; @@ -130,15 +154,25 @@ } dt { + display: block; font-size: 0.9em; text-transform: uppercase; letter-spacing: 0.5px; color: currentColor; margin-bottom: 0.5em; + padding-bottom: 0; + padding-right: 0; + white-space: normal; + + &::after { + content: none; + } } dd { + display: block; font-size: 1em; + padding-bottom: 0; } } } From 9bc1315fd5214b32db06663f4278e1041edd8084 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 14 Nov 2025 08:50:21 +0100 Subject: [PATCH 16/17] Add profile settings link to extra fields block The extra fields block now displays a link to the appropriate profile settings page when no extra fields are found, based on user type. Error messages are improved with sprintf for translation, and the block UI is updated to remove unused icons. ActivityPub options now include profile URLs for both user and blog types. --- build/extra-fields/index.asset.php | 2 +- build/extra-fields/index.js | 3 ++- includes/class-blocks.php | 4 +++ src/extra-fields/edit.js | 39 ++++++++++++++++++++++-------- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/build/extra-fields/index.asset.php b/build/extra-fields/index.asset.php index a3392fb57..a2fa23f68 100644 --- a/build/extra-fields/index.asset.php +++ b/build/extra-fields/index.asset.php @@ -1 +1 @@ - array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'cabfd88d050b1444105c'); + array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '6f7cc2955f584ad618ba'); diff --git a/build/extra-fields/index.js b/build/extra-fields/index.js index bc3a1a6bb..c34ddc779 100644 --- a/build/extra-fields/index.js +++ b/build/extra-fields/index.js @@ -1 +1,2 @@ -(()=>{"use strict";var e,t={157:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);function u(){return window._activityPubOptions||{}}const p=window.ReactJSXRuntime,h=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(h.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:h,postType:v}=null!=i?i:{},[b,y]=(0,o.useState)([]),[x,f]=(0,o.useState)(!1),[w,g]=(0,o.useState)(null),m=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(h&&v&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",v,h))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",v,h))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[h,v]),_="blog"===r?0:"inherit"===r?m||null:r,{namespace:j="activitypub/1.0"}=u(),k=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),F=function({withInherit:e=!1}){const{enabled:t,namespace:i}=u(),[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),p=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!p||d()({path:`/${i}/actors/${p.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,p]);const h=n||(p&&r?[{id:p.id,name:p.name}]:[]);return(0,o.useMemo)(()=>{if(!h.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),h.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[h])}({withInherit:!0});(0,o.useEffect)(()=>{null!==_?(f(!0),g(null),d()({path:`/${j}/actors/${_}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);y(t),f(!1)}).catch(e=>{g(e.message),f(!1)})):y([])},[_,j]);const U=c>0?b.slice(0,c):b,O=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),C=(0,p.jsx)(l.InspectorControls,{children:(0,p.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,p.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:F,onChange:e=>t({selectedUser:e})}),(0,p.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||m?x?(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)(n.Spinner,{})})}):w?(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsxs)("p",{children:[(0,s.__)("Error loading extra fields:","activitypub")," ",w]})})}):0===U.length?(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)("p",{children:(0,s.__)("No extra fields found. Add fields in your profile settings.","activitypub")})})})]}):(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)("dl",{className:"activitypub-extra-fields",children:U.map(e=>(0,p.jsxs)("div",{className:"activitypub-extra-field",style:O,children:[(0,p.jsx)("dt",{children:e.name}),(0,p.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,p.jsxs)(p.Fragment,{children:[C,(0,p.jsx)("div",{...k,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),icon:"list-view",children:(0,p.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:()=>null})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(157));s=r.O(s)})(); \ No newline at end of file +(()=>{"use strict";var e,t={157:(e,t,i)=>{const r=window.wp.blocks,s=window.wp.i18n,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.data,o=window.wp.element,c=window.wp.apiFetch;var d=i.n(c);function u(){return window._activityPubOptions||{}}const p=window.ReactJSXRuntime,h=JSON.parse('{"UU":"activitypub/extra-fields"}');(0,r.registerBlockType)(h.UU,{edit:function({attributes:e,setAttributes:t,context:i}){const{selectedUser:r,maxFields:c}=e,{postId:h,postType:v}=null!=i?i:{},[b,y]=(0,o.useState)([]),[f,x]=(0,o.useState)(!1),[g,w]=(0,o.useState)(null),m=(0,a.useSelect)(e=>{const t=e("core/editor"),i=e("core");if(h&&v&&i){var r,s;const e=null!==(r=i.getEditedEntityRecord?.("postType",v,h))&&void 0!==r?r:null;if(e?.author)return e.author;const t=null!==(s=i.getEntityRecord?.("postType",v,h))&&void 0!==s?s:null;if(t?.author)return t.author}return t&&t.getCurrentPostAttribute?t.getCurrentPostAttribute("author"):null},[h,v]),_="blog"===r?0:"inherit"===r?m||null:r,{namespace:j="activitypub/1.0",profileUrls:k={}}=u(),U="blog"===r?k.blog:k.user,F=(0,l.useBlockProps)({className:"activitypub-extra-fields-block-wrapper"}),C=function({withInherit:e=!1}){const{enabled:t,namespace:i}=u(),[r,l]=(0,o.useState)(!1),{fetchedUsers:n,isLoadingUsers:c}=(0,a.useSelect)(e=>{const{getUsers:i,getIsResolving:r}=e("core");return{fetchedUsers:t?.users?i({capabilities:"activitypub"}):null,isLoadingUsers:!!t?.users&&r("getUsers",[{capabilities:"activitypub"}])}},[]),p=(0,a.useSelect)(e=>n||c?null:e("core").getCurrentUser(),[n,c]);(0,o.useEffect)(()=>{n||c||!p||d()({path:`/${i}/actors/${p.id}`,method:"HEAD",headers:{Accept:"application/activity+json"},parse:!1}).then(()=>l(!0)).catch(()=>l(!1))},[n,c,p]);const h=n||(p&&r?[{id:p.id,name:p.name}]:[]);return(0,o.useMemo)(()=>{if(!h.length)return[];const i=[];return t?.blog&&n&&i.push({label:(0,s.__)("Blog","activitypub"),value:"blog"}),e&&t?.users&&n&&i.push({label:(0,s.__)("Dynamic User","activitypub"),value:"inherit"}),h.reduce((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e),i)},[h])}({withInherit:!0});(0,o.useEffect)(()=>{null!==_?(x(!0),w(null),d()({path:`/${j}/actors/${_}`,headers:{Accept:"application/activity+json"}}).then(e=>{const t=(e.attachment||[]).filter(e=>"PropertyValue"===e.type);y(t),x(!1)}).catch(e=>{w(e.message),x(!1)})):y([])},[_,j]);const O=c>0?b.slice(0,c):b,E=(()=>{const t=e.className?.includes("is-style-cards");if(!t)return{};const i=e.style||{},r=e.backgroundColor,s=i.color?.background;return r?{backgroundColor:`var(--wp--preset--color--${r})`}:s?{backgroundColor:s}:{}})(),S=(0,p.jsx)(l.InspectorControls,{children:(0,p.jsxs)(n.PanelBody,{title:(0,s.__)("Settings","activitypub"),initialOpen:!0,children:[(0,p.jsx)(n.SelectControl,{label:(0,s.__)("User","activitypub"),value:r,options:C,onChange:e=>t({selectedUser:e})}),(0,p.jsx)(n.RangeControl,{label:(0,s.__)("Maximum Fields","activitypub"),value:c,onChange:e=>t({maxFields:e}),min:0,max:20,help:(0,s.__)("Limit the number of fields displayed. 0 = show all.","activitypub")})]})});return"inherit"!==r||m?f?(0,p.jsx)("div",{...F,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),children:(0,p.jsx)(n.Spinner,{})})}):g?(0,p.jsx)("div",{...F,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),children:(0,p.jsx)("p",{children:(0,s.sprintf)(/* translators: %s: Error message */ /* translators: %s: Error message */ +(0,s.__)("Error loading extra fields: %s","activitypub"),g)})})}):0===O.length?(0,p.jsxs)(p.Fragment,{children:[S,(0,p.jsx)("div",{...F,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),children:(0,p.jsxs)("p",{children:[(0,s.__)("No extra fields found.","activitypub")," ",U&&(0,p.jsx)(n.Button,{variant:"link",onClick:()=>{window.location.href=U},children:(0,s.__)("Add fields in your profile settings","activitypub")})]})})})]}):(0,p.jsxs)(p.Fragment,{children:[S,(0,p.jsx)("div",{...F,children:(0,p.jsx)("dl",{className:"activitypub-extra-fields",children:O.map(e=>(0,p.jsxs)("div",{className:"activitypub-extra-field",style:E,children:[(0,p.jsx)("dt",{children:e.name}),(0,p.jsx)("dd",{dangerouslySetInnerHTML:{__html:e.value}})]},`${e.name}-${e.value}`))})})]}):(0,p.jsxs)(p.Fragment,{children:[S,(0,p.jsx)("div",{...F,children:(0,p.jsx)(n.Placeholder,{label:(0,s.__)("Fediverse Extra Fields","activitypub"),children:(0,p.jsx)("p",{children:(0,s.__)("This block will display extra fields based on the post author when published.","activitypub")})})})]})},save:()=>null})}},i={};function r(e){var s=i[e];if(void 0!==s)return s.exports;var l=i[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,i,s,l)=>{if(!i){var n=1/0;for(d=0;d=l)&&Object.keys(r.O).every(e=>r.O[e](i[o]))?i.splice(o--,1):(a=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[i,s,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={914:0,678:0};r.O.j=t=>0===e[t];var t=(t,i)=>{var s,l,[n,a,o]=i,c=0;if(n.some(t=>0!==e[t])){for(s in a)r.o(a,s)&&(r.m[s]=a[s]);if(o)var d=o(r)}for(t&&t(i);cr(157));s=r.O(s)})(); \ No newline at end of file diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 1615e9685..640867399 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -42,6 +42,10 @@ public static function enqueue_editor_assets() { 'blog' => ! is_user_type_disabled( 'blog' ), 'users' => ! is_user_type_disabled( 'user' ), ), + 'profileUrls' => array( + 'user' => \admin_url( 'profile.php#activitypub' ), + 'blog' => \admin_url( 'options-general.php?page=activitypub&tab=blog-profile' ), + ), ); wp_localize_script( 'wp-editor', '_activityPubOptions', $data ); diff --git a/src/extra-fields/edit.js b/src/extra-fields/edit.js index 4004b7977..dc009a5b5 100644 --- a/src/extra-fields/edit.js +++ b/src/extra-fields/edit.js @@ -1,9 +1,9 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; -import { PanelBody, SelectControl, RangeControl, Placeholder, Spinner } from '@wordpress/components'; +import { PanelBody, SelectControl, RangeControl, Placeholder, Spinner, Button } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useState, useEffect } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; @@ -76,8 +76,11 @@ export default function Edit( { attributes, setAttributes, context } ) { const userId = getUserId(); - // Get ActivityPub options including namespace - const { namespace = 'activitypub/1.0' } = useOptions(); + // Get ActivityPub options + const { namespace = 'activitypub/1.0', profileUrls = {} } = useOptions(); + + // Select profile settings URL based on user type + const profileUrl = selectedUser === 'blog' ? profileUrls.blog : profileUrls.user; const blockProps = useBlockProps( { className: 'activitypub-extra-fields-block-wrapper', @@ -173,7 +176,7 @@ export default function Edit( { attributes, setAttributes, context } ) { <> { settingsPanel }
- +

{ __( 'This block will display extra fields based on the post author when published.', @@ -190,7 +193,7 @@ export default function Edit( { attributes, setAttributes, context } ) { if ( isLoading ) { return (

- +
@@ -201,9 +204,13 @@ export default function Edit( { attributes, setAttributes, context } ) { if ( error ) { return (
- +

- { __( 'Error loading extra fields:', 'activitypub' ) } { error } + { sprintf( + /* translators: %s: Error message */ + __( 'Error loading extra fields: %s', 'activitypub' ), + error + ) }

@@ -216,8 +223,20 @@ export default function Edit( { attributes, setAttributes, context } ) { <> { settingsPanel }
- -

{ __( 'No extra fields found. Add fields in your profile settings.', 'activitypub' ) }

+ +

+ { __( 'No extra fields found.', 'activitypub' ) }{ ' ' } + { profileUrl && ( + + ) } +

From 05337ddc51a4c38a8612ab8b304d69a5482d4b92 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 14 Nov 2025 09:05:52 +0100 Subject: [PATCH 17/17] Update mocks and split empty fields message in tests Added mocks for sprintf, Button, and useOptions to improve test coverage. Split the empty fields message into two separate assertions for clarity. --- src/extra-fields/__tests__/edit.test.js | 28 +++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/extra-fields/__tests__/edit.test.js b/src/extra-fields/__tests__/edit.test.js index cd555b86e..d2a57315c 100644 --- a/src/extra-fields/__tests__/edit.test.js +++ b/src/extra-fields/__tests__/edit.test.js @@ -16,6 +16,13 @@ jest.mock( '@wordpress/api-fetch' ); jest.mock( '@wordpress/i18n', () => ( { __: ( text ) => text, + sprintf: ( format, ...args ) => { + let formatted = format; + args.forEach( ( arg, index ) => { + formatted = formatted.replace( /%s/, arg ); + } ); + return formatted; + }, } ) ); jest.mock( '@wordpress/block-editor', () => ( { @@ -60,6 +67,11 @@ jest.mock( '@wordpress/components', () => ( {
), Spinner: () =>
Loading...
, + Button: ( { children, onClick, variant } ) => ( + + ), } ) ); jest.mock( '../../shared/use-user-options', () => ( { @@ -70,6 +82,16 @@ jest.mock( '../../shared/use-user-options', () => ( { ] ), } ) ); +jest.mock( '../../shared/use-options', () => ( { + useOptions: jest.fn( () => ( { + namespace: 'activitypub/1.0', + profileUrls: { + user: '/wp-admin/profile.php#activitypub', + blog: '/wp-admin/options-general.php?page=activitypub&tab=blog-profile', + }, + } ) ), +} ) ); + // Suppress console warnings for testing const originalError = console.error; @@ -227,7 +249,8 @@ describe( 'Extra Fields Edit Component', () => { expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); } ); - expect( screen.getByText( 'No extra fields found. Add fields in your profile settings.' ) ).toBeTruthy(); + expect( screen.getByText( 'No extra fields found.' ) ).toBeTruthy(); + expect( screen.getByText( 'Add fields in your profile settings' ) ).toBeTruthy(); } ); test( 'limits displayed fields when maxFields is set', async () => { @@ -497,7 +520,8 @@ describe( 'Extra Fields Edit Component', () => { expect( screen.getByTestId( 'placeholder' ) ).toBeTruthy(); } ); - expect( screen.getByText( 'No extra fields found. Add fields in your profile settings.' ) ).toBeTruthy(); + expect( screen.getByText( 'No extra fields found.' ) ).toBeTruthy(); + expect( screen.getByText( 'Add fields in your profile settings' ) ).toBeTruthy(); } ); test( 'does not fetch when userId is null', () => {