diff --git a/wp-content/plugins/activitypub/activitypub.php b/wp-content/plugins/activitypub/activitypub.php index abab5f09..6c170a0c 100644 --- a/wp-content/plugins/activitypub/activitypub.php +++ b/wp-content/plugins/activitypub/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 1.3.0 + * Version: 2.0.1 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT @@ -29,10 +29,11 @@ require_once __DIR__ . '/includes/functions.php'; \defined( 'ACTIVITYPUB_SHOW_PLUGIN_RECOMMENDATIONS' ) || \define( 'ACTIVITYPUB_SHOW_PLUGIN_RECOMMENDATIONS', true ); \defined( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS' ) || \define( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS', 3 ); \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); -\defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); +\defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9\._-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "[ap_title]\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" ); \defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false ); \defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false ); +\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false ); \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); @@ -171,7 +172,7 @@ function plugin_settings_link( $actions ) { */ add_action( 'bp_include', - function() { + function () { require_once __DIR__ . '/integration/class-buddypress.php'; Integration\Buddypress::init(); }, diff --git a/wp-content/plugins/activitypub/assets/js/activitypub-admin.js b/wp-content/plugins/activitypub/assets/js/activitypub-admin.js index f6a75afe..37117cab 100644 --- a/wp-content/plugins/activitypub/assets/js/activitypub-admin.js +++ b/wp-content/plugins/activitypub/assets/js/activitypub-admin.js @@ -11,10 +11,11 @@ jQuery( function( $ ) { $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false ); } } ); - + $(document).on( 'wp-plugin-install-success', function( event, response ) { setTimeout( function() { $( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' ); }, 1200 ); - } ); + } ); + } ); diff --git a/wp-content/plugins/activitypub/build/follow-me/index.asset.php b/wp-content/plugins/activitypub/build/follow-me/index.asset.php index 760f4336..eb992eab 100644 --- a/wp-content/plugins/activitypub/build/follow-me/index.asset.php +++ b/wp-content/plugins/activitypub/build/follow-me/index.asset.php @@ -1 +1 @@ - array('wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '6aeec6336fd28aa836a7'); + array( 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '3ffce3edc6fed284bfbc' ); diff --git a/wp-content/plugins/activitypub/build/follow-me/index.js b/wp-content/plugins/activitypub/build/follow-me/index.js index 1320f15d..96605cdb 100644 --- a/wp-content/plugins/activitypub/build/follow-me/index.js +++ b/wp-content/plugins/activitypub/build/follow-me/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={843:(e,t,n)=>{const r=window.wp.blocks,o=window.wp.element,l=window.wp.primitives,a=(0,o.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,o.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"}));function c(){return c=Object.assign?Object.assign.bind():function(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:null;return n?`${e}${arguments.length>3&&void 0!==arguments[3]?arguments[3]:""} { ${t}: ${n}; }\n`:""}function E(e,t,n,r){return g(e,"background-color",t)+g(e,"color",n)+g(e,"background-color",r,":hover")+g(e,"background-color",r,":focus")}function k(e){let{selector:t,style:n,backgroundColor:r}=e;const l=function(e,t,n){const r=`${e} .components-button`,o=("string"==typeof(l=n)?h(l):l?.color?.background||null)||t?.color?.background;var l;return E(r,_(t?.elements?.link?.color?.text),o,_(t?.elements?.link?.[":hover"]?.color?.text))}(t,n,r);return(0,o.createElement)("style",null,l)}const{namespace:O}=window._activityPubOptions,x={avatar:"",resource:"@well@hello.dolly",name:(0,s.__)("Hello Dolly Fan Account","activitypub"),url:"#"};function C(e){if(!e)return x;const t={...x,...e};return t.avatar=t?.icon?.url,t}function S(e){let{profile:t,popupStyles:n,userId:r}=e;const{avatar:l,name:a,resource:c}=t;return(0,o.createElement)("div",{className:"activitypub-profile"},(0,o.createElement)("img",{className:"activitypub-profile__avatar",src:l,alt:a}),(0,o.createElement)("div",{className:"activitypub-profile__content"},(0,o.createElement)("div",{className:"activitypub-profile__name"},a),(0,o.createElement)("div",{className:"activitypub-profile__handle",title:c},c)),(0,o.createElement)(N,{profile:t,popupStyles:n,userId:r}))}function N(e){let{profile:t,popupStyles:n,userId:r}=e;const[l,a]=(0,o.useState)(!1),c=(0,s.sprintf)((0,s.__)("Follow %s","activitypub"),t?.name);return(0,o.createElement)(o.Fragment,null,(0,o.createElement)(u.Button,{className:"activitypub-profile__follow",onClick:()=>a(!0)},(0,s.__)("Follow","activitypub")),l&&(0,o.createElement)(u.Modal,{className:"activitypub-profile__confirm",onRequestClose:()=>a(!1),title:c},(0,o.createElement)(P,{profile:t,userId:r}),(0,o.createElement)("style",null,n)))}function $(e){try{return new URL(e),!0}catch(e){return!1}}function P(e){let{profile:t,userId:n}=e;const{resource:r}=t,l=(0,s.__)("Follow","activitypub"),a=(0,s.__)("Loading...","activitypub"),c=(0,s.__)("Opening...","activitypub"),i=(0,s.__)("Error","activitypub"),p=(0,s.__)("Invalid","activitypub"),[m,v]=(0,o.useState)(l),[h,_]=(0,o.useState)(f),g=(0,y.useCopyToClipboard)(r,(()=>{_(w),setTimeout((()=>_(f)),1e3)})),[E,k]=(0,o.useState)(""),x=(0,o.useCallback)((()=>{let e;if(!$(E)&&!function(e){const t=e.replace(/^@/,"").split("@");return 2===t.length&&$(`https://${t[1]}`)}(E))return v(p),e=setTimeout((()=>v(l)),2e3),()=>clearTimeout(e);const t=`/${O}/users/${n}/remote-follow?resource=${E}`;v(a),d()({path:t}).then((e=>{let{url:t}=e;v(c),setTimeout((()=>{window.open(t,"_blank"),v(l)}),200)})).catch((()=>{v(i),setTimeout((()=>v(l)),2e3)}))}),[E]);return(0,o.createElement)("div",{className:"activitypub-follow-me__dialog"},(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,s.__)("My Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,s.__)("Copy and paste my profile into the search field of your favorite fediverse app or server.","activitypub")),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:r,readOnly:!0}),(0,o.createElement)(u.Button,{ref:g},(0,o.createElement)(b,{icon:h}),(0,s.__)("Copy","activitypub")))),(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,s.__)("Your Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,o.createInterpolateElement)((0,s.__)("Or, if you know your own profile, we can start things that way! (eg https://example.com/yourusername or yourusername@example.com)","activitypub"),{code:(0,o.createElement)("code",null)})),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:E,onKeyDown:e=>{"Enter"===e?.code&&x()},onChange:e=>k(e.target.value)}),(0,o.createElement)(u.Button,{onClick:x},m))))}function z(e){let{selectedUser:t,style:n,backgroundColor:r,id:l,useId:a=!1,profileData:c=!1}=e;const[i,s]=(0,o.useState)(C()),u="site"===t?0:t,p=function(e){return E(".apfmd__button-group .components-button",_(e?.elements?.link?.color?.text)||"#111","#fff",_(e?.elements?.link?.[":hover"]?.color?.text)||"#333")}(n),m=a?{id:l}:{};function v(e){s(C(e))}return(0,o.useEffect)((()=>{if(c)return v(c);(function(e){const t={headers:{Accept:"application/activity+json"},path:`/${O}/users/${e}`};return d()(t)})(u).then(v)}),[u,c]),(0,o.createElement)("div",m,(0,o.createElement)(k,{selector:`#${l}`,style:n,backgroundColor:r}),(0,o.createElement)(S,{profile:i,userId:u,popupStyles:p}))}(0,r.registerBlockType)("activitypub/follow-me",{edit:function(e){let{attributes:t,setAttributes:n}=e;const r=(0,i.useBlockProps)({className:"activitypub-follow-me-block-wrapper"}),l=function(){const e=m?.users?(0,p.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,o.useMemo)((()=>{if(!e)return[];const t=m?.site?[{label:(0,s.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),{selectedUser:a}=t;return(0,o.useEffect)((()=>{l.length&&(l.find((e=>{let{value:t}=e;return t===a}))||n({selectedUser:l[0].value}))}),[a,l]),(0,o.createElement)("div",r,l.length>1&&(0,o.createElement)(i.InspectorControls,{key:"setting"},(0,o.createElement)(u.PanelBody,{title:(0,s.__)("Followers Options","activitypub")},(0,o.createElement)(u.SelectControl,{label:(0,s.__)("Select User","activitypub"),value:t.selectedUser,options:l,onChange:e=>n({selectedUser:e})}))),(0,o.createElement)(z,c({},t,{id:r.id})))},save:()=>null,icon:a})}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var l=n[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,n,o,l)=>{if(!n){var a=1/0;for(u=0;u=l)&&Object.keys(r.O).every((e=>r.O[e](n[i])))?n.splice(i--,1):(c=!1,l0&&e[u-1][2]>l;u--)e[u]=e[u-1];e[u]=[n,o,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 n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={127:0,740:0};r.O.j=t=>0===e[t];var t=(t,n)=>{var o,l,[a,c,i]=n,s=0;if(a.some((t=>0!==e[t]))){for(o in c)r.o(c,o)&&(r.m[o]=c[o]);if(i)var u=i(r)}for(t&&t(n);sr(843)));o=r.O(o)})(); \ No newline at end of file +(()=>{"use strict";var e,t={454:(e,t,n)=>{const r=window.wp.blocks,o=window.wp.element,l=window.wp.primitives,a=(0,o.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,o.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),c=window.wp.blockEditor,i=window.wp.i18n,s=window.wp.components,u=window.wp.data,p=window._activityPubOptions?.enabled,m=window.wp.apiFetch;var d=n.n(m);const v=(0,o.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,o.createElement)(l.Path,{d:"M20.2 8v11c0 .7-.6 1.2-1.2 1.2H6v1.5h13c1.5 0 2.7-1.2 2.7-2.8V8zM18 16.4V4.6c0-.9-.7-1.6-1.6-1.6H4.6C3.7 3 3 3.7 3 4.6v11.8c0 .9.7 1.6 1.6 1.6h11.8c.9 0 1.6-.7 1.6-1.6zm-13.5 0V4.6c0-.1.1-.1.1-.1h11.8c.1 0 .1.1.1.1v11.8c0 .1-.1.1-.1.1H4.6l-.1-.1z"})),f=(0,o.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,o.createElement)(l.Path,{d:"M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"})),w=function({icon:e,size:t=24,...n}){return(0,o.cloneElement)(e,{width:t,height:t,...n})},b=window.wp.compose;function y(e){return`var(--wp--preset--color--${e})`}function _(e){if("string"!=typeof e)return null;if(e.match(/^#/))return e.substring(0,7);const[,,t]=e.split("|");return y(t)}function h(e,t,n=null,r=""){return n?`${e}${r} { ${t}: ${n}; }\n`:""}function E(e,t,n,r){return h(e,"background-color",t)+h(e,"color",n)+h(e,"background-color",r,":hover")+h(e,"background-color",r,":focus")}function g({selector:e,style:t,backgroundColor:n}){const r=function(e,t,n){const r=`${e} .components-button`,o=("string"==typeof(l=n)?y(l):l?.color?.background||null)||t?.color?.background;var l;return E(r,_(t?.elements?.link?.color?.text),o,_(t?.elements?.link?.[":hover"]?.color?.text))}(e,t,n);return(0,o.createElement)("style",null,r)}const{namespace:k}=window._activityPubOptions,x={avatar:"",webfinger:"@well@hello.dolly",name:(0,i.__)("Hello Dolly Fan Account","activitypub"),url:"#"};function C(e){if(!e)return x;const t={...x,...e};return t.avatar=t?.icon?.url,t}function O({profile:e,popupStyles:t,userId:n}){const{avatar:r,name:l,webfinger:a}=e;return(0,o.createElement)("div",{className:"activitypub-profile"},(0,o.createElement)("img",{className:"activitypub-profile__avatar",src:r,alt:l}),(0,o.createElement)("div",{className:"activitypub-profile__content"},(0,o.createElement)("div",{className:"activitypub-profile__name"},l),(0,o.createElement)("div",{className:"activitypub-profile__handle",title:a},a)),(0,o.createElement)(S,{profile:e,popupStyles:t,userId:n}))}function S({profile:e,popupStyles:t,userId:n}){const[r,l]=(0,o.useState)(!1),a=(0,i.sprintf)((0,i.__)("Follow %s","activitypub"),e?.name);return(0,o.createElement)(o.Fragment,null,(0,o.createElement)(s.Button,{className:"activitypub-profile__follow",onClick:()=>l(!0)},(0,i.__)("Follow","activitypub")),r&&(0,o.createElement)(s.Modal,{className:"activitypub-profile__confirm",onRequestClose:()=>l(!1),title:a},(0,o.createElement)($,{profile:e,userId:n}),(0,o.createElement)("style",null,t)))}function N(e){try{return new URL(e),!0}catch(e){return!1}}function $({profile:e,userId:t}){const{webfinger:n}=e,r=(0,i.__)("Follow","activitypub"),l=(0,i.__)("Loading...","activitypub"),a=(0,i.__)("Opening...","activitypub"),c=(0,i.__)("Error","activitypub"),u=(0,i.__)("Invalid","activitypub"),[p,m]=(0,o.useState)(r),[y,_]=(0,o.useState)(v),h=(0,b.useCopyToClipboard)(n,(()=>{_(f),setTimeout((()=>_(v)),1e3)})),[E,g]=(0,o.useState)(""),x=(0,o.useCallback)((()=>{let e;if(!N(E)&&!function(e){const t=e.replace(/^@/,"").split("@");return 2===t.length&&N(`https://${t[1]}`)}(E))return m(u),e=setTimeout((()=>m(r)),2e3),()=>clearTimeout(e);const n=`/${k}/users/${t}/remote-follow?resource=${E}`;m(l),d()({path:n}).then((({url:e})=>{m(a),setTimeout((()=>{window.open(e,"_blank"),m(r)}),200)})).catch((()=>{m(c),setTimeout((()=>m(r)),2e3)}))}),[E]);return(0,o.createElement)("div",{className:"activitypub-follow-me__dialog"},(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,i.__)("My Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,i.__)("Copy and paste my profile into the search field of your favorite fediverse app or server.","activitypub")),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:n,readOnly:!0}),(0,o.createElement)(s.Button,{ref:h},(0,o.createElement)(w,{icon:y}),(0,i.__)("Copy","activitypub")))),(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,i.__)("Your Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,o.createInterpolateElement)((0,i.__)("Or, if you know your own profile, we can start things that way! (eg https://example.com/yourusername or yourusername@example.com)","activitypub"),{code:(0,o.createElement)("code",null)})),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:E,onKeyDown:e=>{"Enter"===e?.code&&x()},onChange:e=>g(e.target.value)}),(0,o.createElement)(s.Button,{onClick:x},p))))}function z({selectedUser:e,style:t,backgroundColor:n,id:r,useId:l=!1,profileData:a=!1}){const[c,i]=(0,o.useState)(C()),s="site"===e?0:e,u=function(e){return E(".apfmd__button-group .components-button",_(e?.elements?.link?.color?.text)||"#111","#fff",_(e?.elements?.link?.[":hover"]?.color?.text)||"#333")}(t),p=l?{id:r}:{};function m(e){i(C(e))}return(0,o.useEffect)((()=>{if(a)return m(a);(function(e){const t={headers:{Accept:"application/activity+json"},path:`/${k}/users/${e}`};return d()(t)})(s).then(m)}),[s,a]),(0,o.createElement)("div",{...p},(0,o.createElement)(g,{selector:`#${r}`,style:t,backgroundColor:n}),(0,o.createElement)(O,{profile:c,userId:s,popupStyles:u}))}(0,r.registerBlockType)("activitypub/follow-me",{edit:function({attributes:e,setAttributes:t}){const n=(0,c.useBlockProps)({className:"activitypub-follow-me-block-wrapper"}),r=function(){const e=p?.users?(0,u.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,o.useMemo)((()=>{if(!e)return[];const t=p?.site?[{label:(0,i.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),{selectedUser:l}=e;return(0,o.useEffect)((()=>{r.length&&(r.find((({value:e})=>e===l))||t({selectedUser:r[0].value}))}),[l,r]),(0,o.createElement)("div",{...n},r.length>1&&(0,o.createElement)(c.InspectorControls,{key:"setting"},(0,o.createElement)(s.PanelBody,{title:(0,i.__)("Followers Options","activitypub")},(0,o.createElement)(s.SelectControl,{label:(0,i.__)("Select User","activitypub"),value:e.selectedUser,options:r,onChange:e=>t({selectedUser:e})}))),(0,o.createElement)(z,{...e,id:n.id}))},save:()=>null,icon:a})}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var l=n[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,n,o,l)=>{if(!n){var a=1/0;for(u=0;u=l)&&Object.keys(r.O).every((e=>r.O[e](n[i])))?n.splice(i--,1):(c=!1,l0&&e[u-1][2]>l;u--)e[u]=e[u-1];e[u]=[n,o,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 n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={127:0,740:0};r.O.j=t=>0===e[t];var t=(t,n)=>{var o,l,a=n[0],c=n[1],i=n[2],s=0;if(a.some((t=>0!==e[t]))){for(o in c)r.o(c,o)&&(r.m[o]=c[o]);if(i)var u=i(r)}for(t&&t(n);sr(454)));o=r.O(o)})(); \ No newline at end of file diff --git a/wp-content/plugins/activitypub/build/follow-me/view.asset.php b/wp-content/plugins/activitypub/build/follow-me/view.asset.php index 76495efb..adf0ea1b 100644 --- a/wp-content/plugins/activitypub/build/follow-me/view.asset.php +++ b/wp-content/plugins/activitypub/build/follow-me/view.asset.php @@ -1 +1 @@ - array('wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '5b48281e37700a970a66'); + array( 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '40f3434fe6f953826373' ); diff --git a/wp-content/plugins/activitypub/build/follow-me/view.js b/wp-content/plugins/activitypub/build/follow-me/view.js index 62d6d05a..fd4e421c 100644 --- a/wp-content/plugins/activitypub/build/follow-me/view.js +++ b/wp-content/plugins/activitypub/build/follow-me/view.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={810:(e,t,r)=>{function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:null;return r?`${e}${arguments.length>3&&void 0!==arguments[3]?arguments[3]:""} { ${t}: ${r}; }\n`:""}function _(e,t,r,n){return w(e,"background-color",t)+w(e,"color",r)+w(e,"background-color",n,":hover")+w(e,"background-color",n,":focus")}function h(e){let{selector:t,style:r,backgroundColor:n}=e;const l=function(e,t,r){const n=`${e} .components-button`,o=("string"==typeof(l=r)?y(l):l?.color?.background||null)||t?.color?.background;var l;return _(n,b(t?.elements?.link?.color?.text),o,b(t?.elements?.link?.[":hover"]?.color?.text))}(t,r,n);return(0,o.createElement)("style",null,l)}const{namespace:E}=window._activityPubOptions,g={avatar:"",resource:"@well@hello.dolly",name:(0,s.__)("Hello Dolly Fan Account","activitypub"),url:"#"};function k(e){if(!e)return g;const t={...g,...e};return t.avatar=t?.icon?.url,t}function O(e){let{profile:t,popupStyles:r,userId:n}=e;const{avatar:l,name:a,resource:c}=t;return(0,o.createElement)("div",{className:"activitypub-profile"},(0,o.createElement)("img",{className:"activitypub-profile__avatar",src:l,alt:a}),(0,o.createElement)("div",{className:"activitypub-profile__content"},(0,o.createElement)("div",{className:"activitypub-profile__name"},a),(0,o.createElement)("div",{className:"activitypub-profile__handle",title:c},c)),(0,o.createElement)(x,{profile:t,popupStyles:r,userId:n}))}function x(e){let{profile:t,popupStyles:r,userId:n}=e;const[l,a]=(0,o.useState)(!1),c=(0,s.sprintf)((0,s.__)("Follow %s","activitypub"),t?.name);return(0,o.createElement)(o.Fragment,null,(0,o.createElement)(u.Button,{className:"activitypub-profile__follow",onClick:()=>a(!0)},(0,s.__)("Follow","activitypub")),l&&(0,o.createElement)(u.Modal,{className:"activitypub-profile__confirm",onRequestClose:()=>a(!1),title:c},(0,o.createElement)(N,{profile:t,userId:n}),(0,o.createElement)("style",null,r)))}function C(e){try{return new URL(e),!0}catch(e){return!1}}function N(e){let{profile:t,userId:r}=e;const{resource:n}=t,l=(0,s.__)("Follow","activitypub"),a=(0,s.__)("Loading...","activitypub"),c=(0,s.__)("Opening...","activitypub"),p=(0,s.__)("Error","activitypub"),y=(0,s.__)("Invalid","activitypub"),[b,w]=(0,o.useState)(l),[_,h]=(0,o.useState)(m),g=(0,f.useCopyToClipboard)(n,(()=>{h(d),setTimeout((()=>h(m)),1e3)})),[k,O]=(0,o.useState)(""),x=(0,o.useCallback)((()=>{let e;if(!C(k)&&!function(e){const t=e.replace(/^@/,"").split("@");return 2===t.length&&C(`https://${t[1]}`)}(k))return w(y),e=setTimeout((()=>w(l)),2e3),()=>clearTimeout(e);const t=`/${E}/users/${r}/remote-follow?resource=${k}`;w(a),i()({path:t}).then((e=>{let{url:t}=e;w(c),setTimeout((()=>{window.open(t,"_blank"),w(l)}),200)})).catch((()=>{w(p),setTimeout((()=>w(l)),2e3)}))}),[k]);return(0,o.createElement)("div",{className:"activitypub-follow-me__dialog"},(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,s.__)("My Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,s.__)("Copy and paste my profile into the search field of your favorite fediverse app or server.","activitypub")),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:n,readOnly:!0}),(0,o.createElement)(u.Button,{ref:g},(0,o.createElement)(v,{icon:_}),(0,s.__)("Copy","activitypub")))),(0,o.createElement)("div",{className:"apmfd__section"},(0,o.createElement)("h4",null,(0,s.__)("Your Profile","activitypub")),(0,o.createElement)("div",{className:"apfmd-description"},(0,o.createInterpolateElement)((0,s.__)("Or, if you know your own profile, we can start things that way! (eg https://example.com/yourusername or yourusername@example.com)","activitypub"),{code:(0,o.createElement)("code",null)})),(0,o.createElement)("div",{className:"apfmd__button-group"},(0,o.createElement)("input",{type:"text",value:k,onKeyDown:e=>{"Enter"===e?.code&&x()},onChange:e=>O(e.target.value)}),(0,o.createElement)(u.Button,{onClick:x},b))))}function S(e){let{selectedUser:t,style:r,backgroundColor:n,id:l,useId:a=!1,profileData:c=!1}=e;const[u,s]=(0,o.useState)(k()),p="site"===t?0:t,m=function(e){return _(".apfmd__button-group .components-button",b(e?.elements?.link?.color?.text)||"#111","#fff",b(e?.elements?.link?.[":hover"]?.color?.text)||"#333")}(r),d=a?{id:l}:{};function v(e){s(k(e))}return(0,o.useEffect)((()=>{if(c)return v(c);(function(e){const t={headers:{Accept:"application/activity+json"},path:`/${E}/users/${e}`};return i()(t)})(p).then(v)}),[p,c]),(0,o.createElement)("div",d,(0,o.createElement)(h,{selector:`#${l}`,style:r,backgroundColor:n}),(0,o.createElement)(O,{profile:u,userId:p,popupStyles:m}))}let $=1;a()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follow-me-block-wrapper"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,o.render)((0,o.createElement)(S,n({},t,{id:"activitypub-follow-me-block-"+$++,useId:!0})),e)}))}))}},r={};function n(e){var o=r[e];if(void 0!==o)return o.exports;var l=r[e]={exports:{}};return t[e](l,l.exports,n),l.exports}n.m=t,e=[],n.O=(t,r,o,l)=>{if(!r){var a=1/0;for(s=0;s=l)&&Object.keys(n.O).every((e=>n.O[e](r[i])))?r.splice(i--,1):(c=!1,l0&&e[s-1][2]>l;s--)e[s]=e[s-1];e[s]=[r,o,l]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={529:0,740:0};n.O.j=t=>0===e[t];var t=(t,r)=>{var o,l,[a,c,i]=r,u=0;if(a.some((t=>0!==e[t]))){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);if(i)var s=i(n)}for(t&&t(r);un(810)));o=n.O(o)})(); \ No newline at end of file +(()=>{"use strict";var e,t={703:(e,t,r)=>{const n=window.wp.element,o=window.wp.domReady;var a=r.n(o);const l=window.wp.apiFetch;var c=r.n(l);const i=window.wp.components,u=window.wp.i18n,s=window.wp.primitives,p=(0,n.createElement)(s.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(s.Path,{d:"M20.2 8v11c0 .7-.6 1.2-1.2 1.2H6v1.5h13c1.5 0 2.7-1.2 2.7-2.8V8zM18 16.4V4.6c0-.9-.7-1.6-1.6-1.6H4.6C3.7 3 3 3.7 3 4.6v11.8c0 .9.7 1.6 1.6 1.6h11.8c.9 0 1.6-.7 1.6-1.6zm-13.5 0V4.6c0-.1.1-.1.1-.1h11.8c.1 0 .1.1.1.1v11.8c0 .1-.1.1-.1.1H4.6l-.1-.1z"})),m=(0,n.createElement)(s.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(s.Path,{d:"M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"})),d=function({icon:e,size:t=24,...r}){return(0,n.cloneElement)(e,{width:t,height:t,...r})},f=window.wp.compose;function v(e){return`var(--wp--preset--color--${e})`}function y(e){if("string"!=typeof e)return null;if(e.match(/^#/))return e.substring(0,7);const[,,t]=e.split("|");return v(t)}function w(e,t,r=null,n=""){return r?`${e}${n} { ${t}: ${r}; }\n`:""}function b(e,t,r,n){return w(e,"background-color",t)+w(e,"color",r)+w(e,"background-color",n,":hover")+w(e,"background-color",n,":focus")}function _({selector:e,style:t,backgroundColor:r}){const o=function(e,t,r){const n=`${e} .components-button`,o=("string"==typeof(a=r)?v(a):a?.color?.background||null)||t?.color?.background;var a;return b(n,y(t?.elements?.link?.color?.text),o,y(t?.elements?.link?.[":hover"]?.color?.text))}(e,t,r);return(0,n.createElement)("style",null,o)}const{namespace:h}=window._activityPubOptions,E={avatar:"",webfinger:"@well@hello.dolly",name:(0,u.__)("Hello Dolly Fan Account","activitypub"),url:"#"};function g(e){if(!e)return E;const t={...E,...e};return t.avatar=t?.icon?.url,t}function k({profile:e,popupStyles:t,userId:r}){const{avatar:o,name:a,webfinger:l}=e;return(0,n.createElement)("div",{className:"activitypub-profile"},(0,n.createElement)("img",{className:"activitypub-profile__avatar",src:o,alt:a}),(0,n.createElement)("div",{className:"activitypub-profile__content"},(0,n.createElement)("div",{className:"activitypub-profile__name"},a),(0,n.createElement)("div",{className:"activitypub-profile__handle",title:l},l)),(0,n.createElement)(x,{profile:e,popupStyles:t,userId:r}))}function x({profile:e,popupStyles:t,userId:r}){const[o,a]=(0,n.useState)(!1),l=(0,u.sprintf)((0,u.__)("Follow %s","activitypub"),e?.name);return(0,n.createElement)(n.Fragment,null,(0,n.createElement)(i.Button,{className:"activitypub-profile__follow",onClick:()=>a(!0)},(0,u.__)("Follow","activitypub")),o&&(0,n.createElement)(i.Modal,{className:"activitypub-profile__confirm",onRequestClose:()=>a(!1),title:l},(0,n.createElement)(C,{profile:e,userId:r}),(0,n.createElement)("style",null,t)))}function O(e){try{return new URL(e),!0}catch(e){return!1}}function C({profile:e,userId:t}){const{webfinger:r}=e,o=(0,u.__)("Follow","activitypub"),a=(0,u.__)("Loading...","activitypub"),l=(0,u.__)("Opening...","activitypub"),s=(0,u.__)("Error","activitypub"),v=(0,u.__)("Invalid","activitypub"),[y,w]=(0,n.useState)(o),[b,_]=(0,n.useState)(p),E=(0,f.useCopyToClipboard)(r,(()=>{_(m),setTimeout((()=>_(p)),1e3)})),[g,k]=(0,n.useState)(""),x=(0,n.useCallback)((()=>{let e;if(!O(g)&&!function(e){const t=e.replace(/^@/,"").split("@");return 2===t.length&&O(`https://${t[1]}`)}(g))return w(v),e=setTimeout((()=>w(o)),2e3),()=>clearTimeout(e);const r=`/${h}/users/${t}/remote-follow?resource=${g}`;w(a),c()({path:r}).then((({url:e})=>{w(l),setTimeout((()=>{window.open(e,"_blank"),w(o)}),200)})).catch((()=>{w(s),setTimeout((()=>w(o)),2e3)}))}),[g]);return(0,n.createElement)("div",{className:"activitypub-follow-me__dialog"},(0,n.createElement)("div",{className:"apmfd__section"},(0,n.createElement)("h4",null,(0,u.__)("My Profile","activitypub")),(0,n.createElement)("div",{className:"apfmd-description"},(0,u.__)("Copy and paste my profile into the search field of your favorite fediverse app or server.","activitypub")),(0,n.createElement)("div",{className:"apfmd__button-group"},(0,n.createElement)("input",{type:"text",value:r,readOnly:!0}),(0,n.createElement)(i.Button,{ref:E},(0,n.createElement)(d,{icon:b}),(0,u.__)("Copy","activitypub")))),(0,n.createElement)("div",{className:"apmfd__section"},(0,n.createElement)("h4",null,(0,u.__)("Your Profile","activitypub")),(0,n.createElement)("div",{className:"apfmd-description"},(0,n.createInterpolateElement)((0,u.__)("Or, if you know your own profile, we can start things that way! (eg https://example.com/yourusername or yourusername@example.com)","activitypub"),{code:(0,n.createElement)("code",null)})),(0,n.createElement)("div",{className:"apfmd__button-group"},(0,n.createElement)("input",{type:"text",value:g,onKeyDown:e=>{"Enter"===e?.code&&x()},onChange:e=>k(e.target.value)}),(0,n.createElement)(i.Button,{onClick:x},y))))}function N({selectedUser:e,style:t,backgroundColor:r,id:o,useId:a=!1,profileData:l=!1}){const[i,u]=(0,n.useState)(g()),s="site"===e?0:e,p=function(e){return b(".apfmd__button-group .components-button",y(e?.elements?.link?.color?.text)||"#111","#fff",y(e?.elements?.link?.[":hover"]?.color?.text)||"#333")}(t),m=a?{id:o}:{};function d(e){u(g(e))}return(0,n.useEffect)((()=>{if(l)return d(l);(function(e){const t={headers:{Accept:"application/activity+json"},path:`/${h}/users/${e}`};return c()(t)})(s).then(d)}),[s,l]),(0,n.createElement)("div",{...m},(0,n.createElement)(_,{selector:`#${o}`,style:t,backgroundColor:r}),(0,n.createElement)(k,{profile:i,userId:s,popupStyles:p}))}let S=1;a()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follow-me-block-wrapper"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,n.render)((0,n.createElement)(N,{...t,id:"activitypub-follow-me-block-"+S++,useId:!0}),e)}))}))}},r={};function n(e){var o=r[e];if(void 0!==o)return o.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,e=[],n.O=(t,r,o,a)=>{if(!r){var l=1/0;for(s=0;s=a)&&Object.keys(n.O).every((e=>n.O[e](r[i])))?r.splice(i--,1):(c=!1,a0&&e[s-1][2]>a;s--)e[s]=e[s-1];e[s]=[r,o,a]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={529:0,740:0};n.O.j=t=>0===e[t];var t=(t,r)=>{var o,a,l=r[0],c=r[1],i=r[2],u=0;if(l.some((t=>0!==e[t]))){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);if(i)var s=i(n)}for(t&&t(r);un(703)));o=n.O(o)})(); \ No newline at end of file diff --git a/wp-content/plugins/activitypub/build/followers/index.asset.php b/wp-content/plugins/activitypub/build/followers/index.asset.php index 324ac5ed..b4dda834 100644 --- a/wp-content/plugins/activitypub/build/followers/index.asset.php +++ b/wp-content/plugins/activitypub/build/followers/index.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '59d9702e06860a6d13e4'); + array( 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url' ), 'version' => 'c338a0364a63e21934ae' ); diff --git a/wp-content/plugins/activitypub/build/followers/index.js b/wp-content/plugins/activitypub/build/followers/index.js index bcbcf3f1..4871225b 100644 --- a/wp-content/plugins/activitypub/build/followers/index.js +++ b/wp-content/plugins/activitypub/build/followers/index.js @@ -1,3 +1,3 @@ -(()=>{var e={184:(e,t)=>{var a;!function(){"use strict";var n={}.hasOwnProperty;function l(){for(var e=[],t=0;t{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const e=window.wp.blocks,t=window.wp.element,n=window.wp.primitives,l=(0,t.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,t.createElement)(n.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"}));function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e.preventDefault(),!a&&r(l)}},n)}const d={outlined:"outlined",minimal:"minimal"};function f(e){let{compact:a,nextLabel:n,page:l,pageClick:r,perPage:o,prevLabel:i,total:c,variant:s=d.outlined}=e;const p=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,n)=>e>=1&&e<=t&&n.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(l,Math.ceil(c/o)),u=b()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${s}`,{"is-compact":a});return(0,t.createElement)("nav",{className:u},i&&(0,t.createElement)(w,{key:"prev",page:l-1,pageClick:r,active:1===l,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!a&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},p.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:r,active:e===l,className:"page-numbers"},e)))),n&&(0,t.createElement)(w,{key:"next",page:l+1,pageClick:r,active:l===Math.ceil(c/o),"aria-label":n,className:"wp-block-query-pagination-next block-editor-block-list__block"},n))}const{namespace:g}=window._activityPubOptions;function y(e){let{selectedUser:a,per_page:n,order:l,title:o,page:i,setPage:p,className:m="",followLinks:b=!0,followerData:w=!1}=e;const d="site"===a?0:a,[y,k]=(0,s.useState)([]),[E,_]=(0,s.useState)(0),[x,C]=(0,s.useState)(0),[S,O]=function(){const[e,t]=(0,s.useState)(1);return[e,t]}(),N=i||S,P=p||O,L=(0,t.createInterpolateElement)(/* translators: arrow for previous followers link */ -(0,c.__)(" Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),j=(0,t.createInterpolateElement)(/* translators: arrow for next followers link */ -(0,c.__)("More ","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),M=(e,t)=>{k(e),C(t),_(Math.ceil(t/n))};return(0,s.useEffect)((()=>{if(w&&1===N)return M(w.followers,w.total);const e=function(e,t,a,n){const l=`/${g}/users/${e}/followers`,r={per_page:t,order:a,page:n,context:"full"};return(0,v.addQueryArgs)(l,r)}(d,n,l,N);u()({path:e}).then((e=>M(e.orderedItems,e.totalItems))).catch((()=>{}))}),[d,n,l,N,w]),(0,t.createElement)("div",{className:"activitypub-follower-block "+m},(0,t.createElement)("h3",null,o),(0,t.createElement)("ul",null,y&&y.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(h,r({},e,{followLinks:b})))))),E>1&&(0,t.createElement)(f,{page:N,perPage:n,total:x,pageClick:P,nextLabel:j,prevLabel:L,compact:"is-style-compact"===m}))}function h(e){let{name:a,icon:n,url:l,preferredUsername:i,followLinks:c=!0}=e;const s=`@${i}`,p={};return c||(p.onClick=e=>e.preventDefault()),(0,t.createElement)(o.ExternalLink,r({className:"activitypub-link",href:l,title:s},p),(0,t.createElement)("img",{width:"40",height:"40",src:n.url,class:"avatar activitypub-avatar",alt:a}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},a),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},s)))}const k=window.wp.data,E=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function(e){let{attributes:a,setAttributes:n}=e;const{order:l,per_page:s,selectedUser:p,title:u}=a,v=(0,i.useBlockProps)(),[m,b]=(0,t.useState)(1),w=[{label:(0,c.__)("New to old","activitypub"),value:"desc"},{label:(0,c.__)("Old to new","activitypub"),value:"asc"}],d=function(){const e=E?.users?(0,k.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,t.useMemo)((()=>{if(!e)return[];const t=E?.site?[{label:(0,c.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),f=e=>t=>{b(1),n({[e]:t})};return(0,t.useEffect)((()=>{d.length&&(d.find((e=>{let{value:t}=e;return t===p}))||n({selectedUser:d[0].value}))}),[p,d]),(0,t.createElement)("div",v,(0,t.createElement)(i.InspectorControls,{key:"setting"},(0,t.createElement)(o.PanelBody,{title:(0,c.__)("Followers Options","activitypub")},(0,t.createElement)(o.TextControl,{label:(0,c.__)("Title","activitypub"),help:(0,c.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:u,onChange:e=>n({title:e})}),d.length>1&&(0,t.createElement)(o.SelectControl,{label:(0,c.__)("Select User","activitypub"),value:p,options:d,onChange:f("selectedUser")}),(0,t.createElement)(o.SelectControl,{label:(0,c.__)("Sort","activitypub"),value:l,options:w,onChange:f("order")}),(0,t.createElement)(o.RangeControl,{label:(0,c.__)("Number of Followers","activitypub"),value:s,onChange:f("per_page"),min:1,max:10}))),(0,t.createElement)(y,r({},a,{page:m,setPage:b,followLinks:!1})))},save:()=>null,icon:l})})()})(); \ No newline at end of file +(()=>{var e={184:(e,t)=>{var a;!function(){"use strict";var l={}.hasOwnProperty;function n(){for(var e=[],t=0;t{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var l in t)a.o(t,l)&&!a.o(e,l)&&Object.defineProperty(e,l,{enumerable:!0,get:t[l]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const e=window.wp.blocks,t=window.wp.element,l=window.wp.primitives,n=(0,t.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,t.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),r=window.wp.components,o=window.wp.blockEditor,i=window.wp.i18n,c=window.React,s=window.wp.apiFetch;var p=a.n(s);const u=window.wp.url;var v=a(184),m=a.n(v);function w({active:e,children:a,page:l,pageClick:n,className:r}){const o=m()("wp-block activitypub-pager",r,{current:e});return(0,t.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&n(l)}},a)}const b={outlined:"outlined",minimal:"minimal"};function d({compact:e,nextLabel:a,page:l,pageClick:n,perPage:r,prevLabel:o,total:i,variant:c=b.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,l)=>e>=1&&e<=t&&l.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(l,Math.ceil(i/r)),p=m()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,t.createElement)("nav",{className:p},o&&(0,t.createElement)(w,{key:"prev",page:l-1,pageClick:n,active:1===l,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:n,active:e===l,className:"page-numbers"},e)))),a&&(0,t.createElement)(w,{key:"next",page:l+1,pageClick:n,active:l===Math.ceil(i/r),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const{namespace:g}=window._activityPubOptions;function f({selectedUser:e,per_page:a,order:l,title:n,page:r,setPage:o,className:s="",followLinks:v=!0,followerData:m=!1}){const w="site"===e?0:e,[b,f]=(0,c.useState)([]),[h,k]=(0,c.useState)(0),[E,_]=(0,c.useState)(0),[x,C]=function(){const[e,t]=(0,c.useState)(1);return[e,t]}(),S=r||x,N=o||C,P=(0,t.createInterpolateElement)(/* translators: arrow for previous followers link */ +(0,i.__)(" Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,t.createInterpolateElement)(/* translators: arrow for next followers link */ +(0,i.__)("More ","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),O=(e,t)=>{f(e),_(t),k(Math.ceil(t/a))};return(0,c.useEffect)((()=>{if(m&&1===S)return O(m.followers,m.total);const e=function(e,t,a,l){const n=`/${g}/users/${e}/followers`,r={per_page:t,order:a,page:l,context:"full"};return(0,u.addQueryArgs)(n,r)}(w,a,l,S);p()({path:e}).then((e=>O(e.orderedItems,e.totalItems))).catch((()=>{}))}),[w,a,l,S,m]),(0,t.createElement)("div",{className:"activitypub-follower-block "+s},(0,t.createElement)("h3",null,n),(0,t.createElement)("ul",null,b&&b.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(y,{...e,followLinks:v}))))),h>1&&(0,t.createElement)(d,{page:S,perPage:a,total:E,pageClick:N,nextLabel:L,prevLabel:P,compact:"is-style-compact"===s}))}function y({name:e,icon:a,url:l,preferredUsername:n,followLinks:o=!0}){const i=`@${n}`,c={};return o||(c.onClick=e=>e.preventDefault()),(0,t.createElement)(r.ExternalLink,{className:"activitypub-link",href:l,title:i,...c},(0,t.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:e}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},e),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},i)))}const h=window.wp.data,k=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function({attributes:e,setAttributes:a}){const{order:l,per_page:n,selectedUser:c,title:s}=e,p=(0,o.useBlockProps)(),[u,v]=(0,t.useState)(1),m=[{label:(0,i.__)("New to old","activitypub"),value:"desc"},{label:(0,i.__)("Old to new","activitypub"),value:"asc"}],w=function(){const e=k?.users?(0,h.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,t.useMemo)((()=>{if(!e)return[];const t=k?.site?[{label:(0,i.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),b=e=>t=>{v(1),a({[e]:t})};return(0,t.useEffect)((()=>{w.length&&(w.find((({value:e})=>e===c))||a({selectedUser:w[0].value}))}),[c,w]),(0,t.createElement)("div",{...p},(0,t.createElement)(o.InspectorControls,{key:"setting"},(0,t.createElement)(r.PanelBody,{title:(0,i.__)("Followers Options","activitypub")},(0,t.createElement)(r.TextControl,{label:(0,i.__)("Title","activitypub"),help:(0,i.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:s,onChange:e=>a({title:e})}),w.length>1&&(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Select User","activitypub"),value:c,options:w,onChange:b("selectedUser")}),(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Sort","activitypub"),value:l,options:m,onChange:b("order")}),(0,t.createElement)(r.RangeControl,{label:(0,i.__)("Number of Followers","activitypub"),value:n,onChange:b("per_page"),min:1,max:10}))),(0,t.createElement)(f,{...e,page:u,setPage:v,followLinks:!1}))},save:()=>null,icon:n})})()})(); \ No newline at end of file diff --git a/wp-content/plugins/activitypub/build/followers/view.asset.php b/wp-content/plugins/activitypub/build/followers/view.asset.php index ba6c3228..50def18a 100644 --- a/wp-content/plugins/activitypub/build/followers/view.asset.php +++ b/wp-content/plugins/activitypub/build/followers/view.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '04e51e7562fe28b0b2c3'); + array( 'react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url' ), 'version' => 'ed5a13e66f8b10323435' ); diff --git a/wp-content/plugins/activitypub/build/followers/view.js b/wp-content/plugins/activitypub/build/followers/view.js index 7c66f054..6c42c39f 100644 --- a/wp-content/plugins/activitypub/build/followers/view.js +++ b/wp-content/plugins/activitypub/build/followers/view.js @@ -1,3 +1,3 @@ -(()=>{var e,t={189:(e,t,a)=>{"use strict";const r=window.wp.element;function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e.preventDefault(),!t&&l(n)}},a)}const v={outlined:"outlined",minimal:"minimal"};function f(e){let{compact:t,nextLabel:a,page:n,pageClick:l,perPage:o,prevLabel:i,total:c,variant:s=v.outlined}=e;const p=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(n,Math.ceil(c/o)),f=u()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${s}`,{"is-compact":t});return(0,r.createElement)("nav",{className:f},i&&(0,r.createElement)(m,{key:"prev",page:n-1,pageClick:l,active:1===n,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!t&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},p.map((e=>(0,r.createElement)(m,{key:e,page:e,pageClick:l,active:e===n,className:"page-numbers"},e)))),a&&(0,r.createElement)(m,{key:"next",page:n+1,pageClick:l,active:n===Math.ceil(c/o),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const b=window.wp.components,{namespace:d}=window._activityPubOptions;function w(e){let{selectedUser:t,per_page:a,order:o,title:p,page:u,setPage:m,className:v="",followLinks:b=!0,followerData:w=!1}=e;const y="site"===t?0:t,[k,h]=(0,l.useState)([]),[E,O]=(0,l.useState)(0),[x,_]=(0,l.useState)(0),[N,j]=function(){const[e,t]=(0,l.useState)(1);return[e,t]}(),S=u||N,C=m||j,L=(0,r.createInterpolateElement)(/* translators: arrow for previous followers link */ -(0,s.__)(" Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),q=(0,r.createInterpolateElement)(/* translators: arrow for next followers link */ -(0,s.__)("More ","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),P=(e,t)=>{h(e),_(t),O(Math.ceil(t/a))};return(0,l.useEffect)((()=>{if(w&&1===S)return P(w.followers,w.total);const e=function(e,t,a,r){const n=`/${d}/users/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,c.addQueryArgs)(n,l)}(y,a,o,S);i()({path:e}).then((e=>P(e.orderedItems,e.totalItems))).catch((()=>{}))}),[y,a,o,S,w]),(0,r.createElement)("div",{className:"activitypub-follower-block "+v},(0,r.createElement)("h3",null,p),(0,r.createElement)("ul",null,k&&k.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(g,n({},e,{followLinks:b})))))),E>1&&(0,r.createElement)(f,{page:S,perPage:a,total:x,pageClick:C,nextLabel:q,prevLabel:L,compact:"is-style-compact"===v}))}function g(e){let{name:t,icon:a,url:l,preferredUsername:o,followLinks:i=!0}=e;const c=`@${o}`,s={};return i||(s.onClick=e=>e.preventDefault()),(0,r.createElement)(b.ExternalLink,n({className:"activitypub-link",href:l,title:c},s),(0,r.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:t}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},t),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},c)))}const y=window.wp.domReady;a.n(y)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,r.render)((0,r.createElement)(w,t),e)}))}))},184:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e=[],t=0;t{if(!a){var o=1/0;for(p=0;p=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(i=!1,l0&&e[p-1][2]>l;p--)e[p]=e[p-1];e[p]=[a,n,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 a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={638:0,962:0};r.O.j=t=>0===e[t];var t=(t,a)=>{var n,l,[o,i,c]=a,s=0;if(o.some((t=>0!==e[t]))){for(n in i)r.o(i,n)&&(r.m[n]=i[n]);if(c)var p=c(r)}for(t&&t(a);sr(189)));n=r.O(n)})(); \ No newline at end of file +(()=>{var e,t={142:(e,t,a)=>{"use strict";const r=window.wp.element,n=window.React,l=window.wp.apiFetch;var i=a.n(l);const o=window.wp.url,c=window.wp.i18n;var s=a(184),p=a.n(s);function u({active:e,children:t,page:a,pageClick:n,className:l}){const i=p()("wp-block activitypub-pager",l,{current:e});return(0,r.createElement)("a",{className:i,onClick:t=>{t.preventDefault(),!e&&n(a)}},t)}const m={outlined:"outlined",minimal:"minimal"};function f({compact:e,nextLabel:t,page:a,pageClick:n,perPage:l,prevLabel:i,total:o,variant:c=m.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(a,Math.ceil(o/l)),f=p()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,r.createElement)("nav",{className:f},i&&(0,r.createElement)(u,{key:"prev",page:a-1,pageClick:n,active:1===a,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!e&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,r.createElement)(u,{key:e,page:e,pageClick:n,active:e===a,className:"page-numbers"},e)))),t&&(0,r.createElement)(u,{key:"next",page:a+1,pageClick:n,active:a===Math.ceil(o/l),"aria-label":t,className:"wp-block-query-pagination-next block-editor-block-list__block"},t))}const v=window.wp.components,{namespace:d}=window._activityPubOptions;function b({selectedUser:e,per_page:t,order:a,title:l,page:s,setPage:p,className:u="",followLinks:m=!0,followerData:v=!1}){const b="site"===e?0:e,[g,k]=(0,n.useState)([]),[y,h]=(0,n.useState)(0),[E,x]=(0,n.useState)(0),[_,O]=function(){const[e,t]=(0,n.useState)(1);return[e,t]}(),N=s||_,S=p||O,C=(0,r.createInterpolateElement)(/* translators: arrow for previous followers link */ +(0,c.__)(" Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,r.createInterpolateElement)(/* translators: arrow for next followers link */ +(0,c.__)("More ","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),j=(e,a)=>{k(e),x(a),h(Math.ceil(a/t))};return(0,n.useEffect)((()=>{if(v&&1===N)return j(v.followers,v.total);const e=function(e,t,a,r){const n=`/${d}/users/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,o.addQueryArgs)(n,l)}(b,t,a,N);i()({path:e}).then((e=>j(e.orderedItems,e.totalItems))).catch((()=>{}))}),[b,t,a,N,v]),(0,r.createElement)("div",{className:"activitypub-follower-block "+u},(0,r.createElement)("h3",null,l),(0,r.createElement)("ul",null,g&&g.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(w,{...e,followLinks:m}))))),y>1&&(0,r.createElement)(f,{page:N,perPage:t,total:E,pageClick:S,nextLabel:L,prevLabel:C,compact:"is-style-compact"===u}))}function w({name:e,icon:t,url:a,preferredUsername:n,followLinks:l=!0}){const i=`@${n}`,o={};return l||(o.onClick=e=>e.preventDefault()),(0,r.createElement)(v.ExternalLink,{className:"activitypub-link",href:a,title:i,...o},(0,r.createElement)("img",{width:"40",height:"40",src:t.url,class:"avatar activitypub-avatar",alt:e}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},e),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},i)))}const g=window.wp.domReady;a.n(g)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,r.render)((0,r.createElement)(b,{...t}),e)}))}))},184:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e=[],t=0;t{if(!a){var i=1/0;for(p=0;p=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(o=!1,l0&&e[p-1][2]>l;p--)e[p]=e[p-1];e[p]=[a,n,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 a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={638:0,962:0};r.O.j=t=>0===e[t];var t=(t,a)=>{var n,l,i=a[0],o=a[1],c=a[2],s=0;if(i.some((t=>0!==e[t]))){for(n in o)r.o(o,n)&&(r.m[n]=o[n]);if(c)var p=c(r)}for(t&&t(a);sr(142)));n=r.O(n)})(); \ No newline at end of file diff --git a/wp-content/plugins/activitypub/includes/activity/class-activity.php b/wp-content/plugins/activitypub/includes/activity/class-activity.php index 96ee095b..dd698607 100644 --- a/wp-content/plugins/activitypub/includes/activity/class-activity.php +++ b/wp-content/plugins/activitypub/includes/activity/class-activity.php @@ -20,13 +20,13 @@ class Activity extends Base_Object { const CONTEXT = array( 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', + 'https://purl.archive.org/socialweb/webfinger', array( 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'PropertyValue' => 'schema:PropertyValue', 'schema' => 'http://schema.org#', 'pt' => 'https://joinpeertube.org/ns#', 'toot' => 'http://joinmastodon.org/ns#', - 'webfinger' => 'https://webfinger.net/#', 'litepub' => 'http://litepub.social/ns#', 'lemmy' => 'https://join-lemmy.org/ns#', 'value' => 'schema:value', @@ -51,7 +51,6 @@ class Activity extends Base_Object { 'discoverable' => 'toot:discoverable', 'indexable' => 'toot:indexable', 'sensitive' => 'as:sensitive', - 'resource' => 'webfinger:resource', ), ); @@ -196,7 +195,7 @@ class Activity extends Base_Object { public function set_object( $object ) { // convert array to object if ( is_array( $object ) ) { - $object = Base_Object::init_from_array( $object ); + $object = self::init_from_array( $object ); } // set object diff --git a/wp-content/plugins/activitypub/includes/activity/class-base-object.php b/wp-content/plugins/activitypub/includes/activity/class-base-object.php index b73c621c..1e256cbe 100644 --- a/wp-content/plugins/activitypub/includes/activity/class-base-object.php +++ b/wp-content/plugins/activitypub/includes/activity/class-base-object.php @@ -254,19 +254,6 @@ class Base_Object { */ protected $published; - /** - * A Collection containing objects considered to be responses to - * this object. - * - * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies - * - * @var string - * | Collection - * | Link - * | null - */ - protected $replies; - /** * The date and time describing the actual or expected starting time * of the object. @@ -437,6 +424,19 @@ class Base_Object { */ protected $source; + /** + * A Collection containing objects considered to be responses to + * this object. + * + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies + * + * @var string + * | Collection + * | Link + * | null + */ + protected $replies; + /** * Magic function to implement getter and setter * @@ -671,8 +671,25 @@ class Base_Object { * @return string The JSON string. */ public function to_json() { - $array = $this->to_array(); + $array = $this->to_array(); + $options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; - return \wp_json_encode( $array, \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT ); + /* + * Options to be passed to json_encode() + * + * @param int $options The current options flags + */ + $options = \apply_filters( 'activitypub_json_encode_options', $options ); + + return \wp_json_encode( $array, $options ); + } + + /** + * Returns the keys of the object vars. + * + * @return array The keys of the object vars. + */ + public function get_object_var_keys() { + return \array_keys( \get_object_vars( $this ) ); } } diff --git a/wp-content/plugins/activitypub/includes/class-activity-dispatcher.php b/wp-content/plugins/activitypub/includes/class-activity-dispatcher.php index 462a75af..99aa9600 100644 --- a/wp-content/plugins/activitypub/includes/class-activity-dispatcher.php +++ b/wp-content/plugins/activitypub/includes/class-activity-dispatcher.php @@ -2,10 +2,13 @@ namespace Activitypub; use WP_Post; +use WP_Comment; use Activitypub\Activity\Activity; use Activitypub\Collection\Users; use Activitypub\Collection\Followers; +use Activitypub\Transformer\Factory; use Activitypub\Transformer\Post; +use Activitypub\Transformer\Comment; use function Activitypub\is_single_user; use function Activitypub\is_user_disabled; @@ -25,17 +28,18 @@ class Activity_Dispatcher { public static function init() { \add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 ); \add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 ); + \add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 ); } /** * Send Activities to followers and mentioned users or `Announce` (boost) a blog post. * - * @param WP_Post $wp_post The ActivityPub Post. - * @param string $type The Activity-Type. + * @param mixed $wp_object The ActivityPub Post. + * @param string $type The Activity-Type. * * @return void */ - public static function send_activity_or_announce( WP_Post $wp_post, $type ) { + public static function send_activity_or_announce( $wp_object, $type ) { // check if a migration is needed before sending new posts Migration::maybe_migrate(); @@ -43,56 +47,48 @@ class Activity_Dispatcher { return; } - $wp_post->post_author = Users::BLOG_USER_ID; - if ( is_single_user() ) { - self::send_activity( $wp_post, $type ); + self::send_activity( $wp_object, $type, Users::BLOG_USER_ID ); } else { - self::send_announce( $wp_post, $type ); + self::send_announce( $wp_object, $type ); } } /** * Send Activities to followers and mentioned users. * - * @param WP_Post $wp_post The ActivityPub Post. - * @param string $type The Activity-Type. + * @param mixed $wp_object The ActivityPub Post. + * @param string $type The Activity-Type. * * @return void */ - public static function send_activity( WP_Post $wp_post, $type ) { - if ( is_user_disabled( $wp_post->post_author ) ) { + public static function send_activity( $wp_object, $type, $user_id = null ) { + $transformer = Factory::get_transformer( $wp_object ); + + if ( null !== $user_id ) { + $transformer->change_wp_user_id( $user_id ); + } + + $user_id = $transformer->get_wp_user_id(); + + if ( is_user_disabled( $user_id ) ) { return; } - $object = Post::transform( $wp_post )->to_object(); + $activity = $transformer->to_activity( $type ); - $activity = new Activity(); - $activity->set_type( $type ); - $activity->set_object( $object ); - - $follower_inboxes = Followers::get_inboxes( $wp_post->post_author ); - $mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() ); - - $inboxes = array_merge( $follower_inboxes, $mentioned_inboxes ); - $inboxes = array_unique( $inboxes ); - - $json = $activity->to_json(); - - foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, $wp_post->post_author ); - } + self::send_activity_to_inboxes( $activity, $user_id ); } /** * Send Announces to followers and mentioned users. * - * @param WP_Post $wp_post The ActivityPub Post. - * @param string $type The Activity-Type. + * @param mixed $wp_object The ActivityPub Post. + * @param string $type The Activity-Type. * * @return void */ - public static function send_announce( WP_Post $wp_post, $type ) { + public static function send_announce( $wp_object, $type ) { if ( ! in_array( $type, array( 'Create', 'Update' ), true ) ) { return; } @@ -101,25 +97,70 @@ class Activity_Dispatcher { return; } - $object = Post::transform( $wp_post )->to_object(); + $transformer = Factory::get_transformer( $wp_object ); + $transformer->change_wp_user_id( Users::BLOG_USER_ID ); + $user_id = $transformer->get_wp_user_id(); + $activity = $transformer->to_activity( 'Announce' ); + + self::send_activity_to_inboxes( $activity, $user_id ); + } + + /** + * Send a "Update" Activity when a user updates their profile. + * + * @param int $user_id The user ID to send an update for. + * + * @return void + */ + public static function send_profile_update( $user_id ) { + $user = Users::get_by_various( $user_id ); + + // bail if that's not a good user + if ( is_wp_error( $user ) ) { + return; + } + + // build the update $activity = new Activity(); - $activity->set_type( 'Announce' ); - // to pre-fill attributes like "published" and "id" - $activity->set_object( $object ); - // send only the id - $activity->set_object( $object->get_id() ); + $activity->set_id( $user->get_url() . '#update' ); + $activity->set_type( 'Update' ); + $activity->set_actor( $user->get_url() ); + $activity->set_object( $user->get_url() ); + $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); - $follower_inboxes = Followers::get_inboxes( $wp_post->post_author ); - $mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() ); + // send the update + self::send_activity_to_inboxes( $activity, $user_id ); + } + + /** + * Send an Activity to all followers and mentioned users. + * + * @param Activity $activity The ActivityPub Activity. + * @param int $user_id The user ID. + * + * @return void + */ + private static function send_activity_to_inboxes( $activity, $user_id ) { + $follower_inboxes = Followers::get_inboxes( $user_id ); + + $mentioned_inboxes = array(); + $cc = $activity->get_cc(); + if ( $cc ) { + $mentioned_inboxes = Mention::get_inboxes( $cc ); + } $inboxes = array_merge( $follower_inboxes, $mentioned_inboxes ); $inboxes = array_unique( $inboxes ); + if ( empty( $inboxes ) ) { + return; + } + $json = $activity->to_json(); foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, $wp_post->post_author ); + safe_remote_post( $inbox, $json, $user_id ); } } } diff --git a/wp-content/plugins/activitypub/includes/class-activitypub.php b/wp-content/plugins/activitypub/includes/class-activitypub.php index 6f654c5c..535ab40e 100644 --- a/wp-content/plugins/activitypub/includes/class-activitypub.php +++ b/wp-content/plugins/activitypub/includes/class-activitypub.php @@ -7,6 +7,8 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use function Activitypub\sanitize_url; +use function Activitypub\is_comment; +use function Activitypub\is_activitypub_request; /** * ActivityPub Class @@ -19,6 +21,7 @@ class Activitypub { */ public static function init() { \add_filter( 'template_include', array( self::class, 'render_json_template' ), 99 ); + \add_action( 'template_redirect', array( self::class, 'template_redirect' ) ); \add_filter( 'query_vars', array( self::class, 'add_query_vars' ) ); \add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 ); \add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 ); @@ -39,6 +42,8 @@ class Activitypub { \add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) ); + \add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 ); + // register several post_types self::register_post_types(); } @@ -50,7 +55,6 @@ class Activitypub { */ public static function activate() { self::flush_rewrite_rules(); - Scheduler::register_schedules(); } @@ -98,6 +102,8 @@ class Activitypub { if ( \is_author() ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php'; + } elseif ( is_comment() ) { + $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php'; } elseif ( \is_singular() ) { $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php'; } elseif ( \is_home() ) { @@ -115,11 +121,44 @@ class Activitypub { return $json_template; } + /** + * Custom redirects for ActivityPub requests. + * + * @return void + */ + public static function template_redirect() { + $comment_id = get_query_var( 'c', null ); + + // check if it seems to be a comment + if ( ! $comment_id ) { + return; + } + + $comment = get_comment( $comment_id ); + + // load a 404 page if `c` is set but not valid + if ( ! $comment ) { + global $wp_query; + $wp_query->set_404(); + return; + } + + // stop if it's not an ActivityPub comment + if ( is_activitypub_request() && $comment->user_id ) { + return; + } + + wp_safe_redirect( get_comment_link( $comment ) ); + exit; + } + /** * Add the 'activitypub' query variable so WordPress won't mangle it. */ public static function add_query_vars( $vars ) { $vars[] = 'activitypub'; + $vars[] = 'c'; + $vars[] = 'p'; return $vars; } @@ -195,10 +234,18 @@ class Activitypub { * @return string $url */ public static function remote_comment_link( $comment_link, $comment ) { - $remote_comment_link = get_comment_meta( $comment->comment_ID, 'source_url', true ); - if ( $remote_comment_link ) { - $comment_link = esc_url( $remote_comment_link ); + if ( ! $comment || is_admin() ) { + return $comment_link; } + + $comment_meta = \get_comment_meta( $comment->comment_ID ); + + if ( ! empty( $comment_meta['source_url'][0] ) ) { + return $comment_meta['source_url'][0]; + } elseif ( ! empty( $comment_meta['source_id'][0] ) ) { + return $comment_meta['source_id'][0]; + } + return $comment_link; } @@ -341,7 +388,7 @@ class Activitypub { * @return void */ private static function register_post_types() { - register_post_type( + \register_post_type( Followers::POST_TYPE, array( 'labels' => array( @@ -358,7 +405,7 @@ class Activitypub { ) ); - register_post_meta( + \register_post_meta( Followers::POST_TYPE, 'activitypub_inbox', array( @@ -368,13 +415,13 @@ class Activitypub { ) ); - register_post_meta( + \register_post_meta( Followers::POST_TYPE, 'activitypub_errors', array( 'type' => 'string', 'single' => false, - 'sanitize_callback' => function( $value ) { + 'sanitize_callback' => function ( $value ) { if ( ! is_string( $value ) ) { throw new Exception( 'Error message is no valid string' ); } @@ -384,30 +431,48 @@ class Activitypub { ) ); - register_post_meta( + \register_post_meta( Followers::POST_TYPE, 'activitypub_user_id', array( 'type' => 'string', 'single' => false, - 'sanitize_callback' => function( $value ) { + 'sanitize_callback' => function ( $value ) { return esc_sql( $value ); }, ) ); - register_post_meta( + \register_post_meta( Followers::POST_TYPE, 'activitypub_actor_json', array( 'type' => 'string', 'single' => true, - 'sanitize_callback' => function( $value ) { + 'sanitize_callback' => function ( $value ) { return sanitize_text_field( $value ); }, ) ); - do_action( 'activitypub_after_register_post_type' ); + \do_action( 'activitypub_after_register_post_type' ); + } + + /** + * Filters the CSS classes to add an ActivityPub class. + * + * @param string[] $classes An array of comment classes. + * @param string[] $css_class An array of additional classes added to the list. + * @param string $comment_id The comment ID as a numeric string. + * + * @return string[] An array of classes. + */ + public static function comment_class( $classes, $css_class, $comment_id ) { + // check if ActivityPub comment + if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) { + $classes[] = 'activitypub-comment'; + } + + return $classes; } } diff --git a/wp-content/plugins/activitypub/includes/class-admin.php b/wp-content/plugins/activitypub/includes/class-admin.php index f8afc8d2..dca3aee7 100644 --- a/wp-content/plugins/activitypub/includes/class-admin.php +++ b/wp-content/plugins/activitypub/includes/class-admin.php @@ -18,6 +18,7 @@ class Admin { \add_action( 'admin_init', array( self::class, 'register_settings' ) ); \add_action( 'personal_options_update', array( self::class, 'save_user_description' ) ); \add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) ); + \add_action( 'admin_notices', array( self::class, 'admin_notices' ) ); if ( ! is_user_disabled( get_current_user_id() ) ) { \add_action( 'show_user_profile', array( self::class, 'add_profile' ) ); @@ -46,6 +47,37 @@ class Admin { } } + /** + * Display admin menu notices about configuration problems or conflicts. + * + * @return void + */ + public static function admin_notices() { + $permalink_structure = \get_option( 'permalink_structure' ); + if ( empty( $permalink_structure ) ) { + $admin_notice = \__( 'You are using the ActivityPub plugin without setting a permalink structure. This will prevent ActivityPub from working. Please set a permalink structure.', 'activitypub' ); + self::show_admin_notice( $admin_notice, 'error' ); + } + } + + /** + * Display one admin menu notice about configuration problems or conflicts. + * + * @param string $admin_notice The notice to display. + * @param string $level The level of the notice (error, warning, success, info). + * + * @return void + */ + private static function show_admin_notice( $admin_notice, $level ) { + ?> + +

+

+
+ + \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ), 'show_in_rest' => true, 'default' => Blog_User::get_default_username(), - 'sanitize_callback' => function( $value ) { + 'sanitize_callback' => function ( $value ) { // hack to allow dots in the username $parts = explode( '.', $value ); $sanitized = array(); diff --git a/wp-content/plugins/activitypub/includes/class-blocks.php b/wp-content/plugins/activitypub/includes/class-blocks.php index 02f96591..bd6549be 100644 --- a/wp-content/plugins/activitypub/includes/class-blocks.php +++ b/wp-content/plugins/activitypub/includes/class-blocks.php @@ -3,7 +3,9 @@ namespace Activitypub; use Activitypub\Collection\Followers; use Activitypub\Collection\Users as User_Collection; -use Activitypub\is_user_type_disabled; + +use function Activitypub\object_to_uri; +use function Activitypub\is_user_type_disabled; class Blocks { public static function init() { @@ -73,7 +75,7 @@ class Blocks { if ( ! is_wp_error( $user ) ) { $attrs['profileData'] = self::filter_array_by_keys( $user->to_array(), - array( 'icon', 'name', 'resource' ) + array( 'icon', 'name', 'webfinger' ) ); } $wrapper_attributes = get_block_wrapper_attributes( @@ -94,7 +96,7 @@ class Blocks { $attrs['followerData']['total'] = $follower_data['total']; $attrs['followerData']['followers'] = array_map( - function( $follower ) { + function ( $follower ) { return self::filter_array_by_keys( $follower->to_array(), array( 'icon', 'name', 'preferredUsername', 'url' ) @@ -140,7 +142,7 @@ class Blocks { return sprintf( $template, - esc_url( $data['url'] ), + esc_url( object_to_uri( $data['url'] ) ), esc_attr( $data['name'] ), esc_attr( $data['icon']['url'] ), esc_html( $data['name'] ), diff --git a/wp-content/plugins/activitypub/includes/class-hashtag.php b/wp-content/plugins/activitypub/includes/class-hashtag.php index 6031d1f3..acd5a9aa 100644 --- a/wp-content/plugins/activitypub/includes/class-hashtag.php +++ b/wp-content/plugins/activitypub/includes/class-hashtag.php @@ -12,8 +12,8 @@ class Hashtag { */ public static function init() { if ( '1' === \get_option( 'activitypub_use_hashtags', '1' ) ) { - \add_filter( 'wp_insert_post', array( self::class, 'insert_post' ), 10, 2 ); - \add_filter( 'the_content', array( self::class, 'the_content' ), 10, 2 ); + \add_action( 'wp_insert_post', array( self::class, 'insert_post' ), 10, 2 ); + \add_filter( 'the_content', array( self::class, 'the_content' ), 10, 1 ); } } @@ -67,7 +67,7 @@ class Hashtag { $tag = strtolower( $m[2] ); if ( '/' === $m[1] ) { // Closing tag. - $i = array_search( $tag, $tag_stack ); + $i = array_search( $tag, $tag_stack, true ); // We can only remove the tag from the stack if it is in the stack. if ( false !== $i ) { $tag_stack = array_slice( $tag_stack, 0, $i ); diff --git a/wp-content/plugins/activitypub/includes/class-health-check.php b/wp-content/plugins/activitypub/includes/class-health-check.php index 74a6f9ec..e07684f9 100644 --- a/wp-content/plugins/activitypub/includes/class-health-check.php +++ b/wp-content/plugins/activitypub/includes/class-health-check.php @@ -39,11 +39,6 @@ class Health_Check { 'test' => array( self::class, 'test_webfinger' ), ); - $tests['direct']['activitypub_test_system_cron'] = array( - 'label' => __( 'System Cron Test', 'activitypub' ), - 'test' => array( self::class, 'test_system_cron' ), - ); - return $tests; } @@ -257,17 +252,10 @@ class Health_Check { * @return boolean|WP_Error */ public static function is_webfinger_endpoint_accessible() { - $user = \wp_get_current_user(); + $user = Users::get_by_id( Users::APPLICATION_USER_ID ); + $resource = $user->get_webfinger(); - if ( ! is_user_type_disabled( 'blog' ) ) { - $account = get_webfinger_resource( $user->ID ); - } elseif ( ! is_user_type_disabled( 'user' ) ) { - $account = get_webfinger_resource( Users::BLOG_USER_ID ); - } else { - $account = ''; - } - - $url = Webfinger::resolve( $account ); + $url = Webfinger::resolve( $resource ); if ( \is_wp_error( $url ) ) { $allowed = array( 'code' => array() ); $not_accessible = wp_kses( diff --git a/wp-content/plugins/activitypub/includes/class-mention.php b/wp-content/plugins/activitypub/includes/class-mention.php index beb62468..0b8c0b5e 100644 --- a/wp-content/plugins/activitypub/includes/class-mention.php +++ b/wp-content/plugins/activitypub/includes/class-mention.php @@ -14,7 +14,8 @@ class Mention { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_filter( 'the_content', array( self::class, 'the_content' ), 99, 2 ); + \add_filter( 'the_content', array( self::class, 'the_content' ), 99, 1 ); + \add_filter( 'comment_text', array( self::class, 'the_content' ), 10, 1 ); \add_filter( 'activitypub_extract_mentions', array( self::class, 'extract_mentions' ), 99, 2 ); } @@ -100,7 +101,14 @@ class Mention { if ( ! empty( $metadata['preferredUsername'] ) ) { $username = $metadata['preferredUsername']; } - return \sprintf( '@%s', esc_url( $metadata['url'] ), esc_html( $username ) ); + + $url = isset( $metadata['url'] ) ? $metadata['url'] : $metadata['url']; + + if ( \is_array( $url ) ) { + $url = $url[0]; + } + + return \sprintf( '@%s', esc_url( $url ), esc_html( $username ) ); } return $result[0]; diff --git a/wp-content/plugins/activitypub/includes/class-scheduler.php b/wp-content/plugins/activitypub/includes/class-scheduler.php index 11f40daf..1a05f49f 100644 --- a/wp-content/plugins/activitypub/includes/class-scheduler.php +++ b/wp-content/plugins/activitypub/includes/class-scheduler.php @@ -6,22 +6,77 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Post; +use function Activitypub\is_user_type_disabled; + /** * ActivityPub Scheduler Class * * @author Matthias Pfefferle */ class Scheduler { + /** * Initialize the class, registering WordPress hooks */ public static function init() { + // Post transitions \add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 ); + \add_action( + 'edit_attachment', + function ( $post_id ) { + self::schedule_post_activity( 'publish', 'publish', $post_id ); + } + ); + \add_action( + 'add_attachment', + function ( $post_id ) { + self::schedule_post_activity( 'publish', '', $post_id ); + } + ); + \add_action( + 'delete_attachment', + function ( $post_id ) { + self::schedule_post_activity( 'trash', '', $post_id ); + } + ); + // Comment transitions + \add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 ); + \add_action( + 'edit_comment', + function ( $comment_id ) { + self::schedule_comment_activity( 'approved', 'approved', $comment_id ); + } + ); + \add_action( + 'wp_insert_comment', + function ( $comment_id ) { + self::schedule_comment_activity( 'approved', '', $comment_id ); + } + ); + + // Follower Cleanups \add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) ); \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) ); + // Migration \add_action( 'admin_init', array( self::class, 'schedule_migration' ) ); + + // profile updates for blog options + if ( ! is_user_type_disabled( 'blog' ) ) { + \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) ); + } + + // profile updates for user options + if ( ! is_user_type_disabled( 'user' ) ) { + \add_action( 'wp_update_user', array( self::class, 'user_update' ) ); + \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); + // @todo figure out a feasible way of updating the header image since it's not unique to any user. + } } /** @@ -58,6 +113,8 @@ class Scheduler { * @param WP_Post $post Post object. */ public static function schedule_post_activity( $new_status, $old_status, $post ) { + $post = get_post( $post ); + // Do not send activities if post is password protected. if ( \post_password_required( $post ) ) { return; @@ -99,6 +156,58 @@ class Scheduler { ); } + /** + * Schedule Comment Activities + * + * transition_comment_status() + * + * @param string $new_status New comment status. + * @param string $old_status Old comment status. + * @param WP_Comment $comment Comment object. + */ + public static function schedule_comment_activity( $new_status, $old_status, $comment ) { + $comment = get_comment( $comment ); + + // Federate only approved comments. + if ( ! $comment->user_id ) { + return; + } + + if ( + 'approved' === $new_status && + 'approved' !== $old_status + ) { + $type = 'Create'; + } elseif ( 'approved' === $new_status ) { + $type = 'Update'; + \update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true ); + } elseif ( + 'trash' === $new_status || + 'spam' === $new_status + ) { + $type = 'Delete'; + } + + if ( ! $type ) { + return; + } + + \wp_schedule_single_event( + \time(), + 'activitypub_send_activity', + array( $comment, $type ) + ); + + \wp_schedule_single_event( + \time(), + sprintf( + 'activitypub_send_%s_activity', + \strtolower( $type ) + ), + array( $comment ) + ); + } + /** * Update followers * @@ -166,4 +275,68 @@ class Scheduler { \wp_schedule_single_event( \time(), 'activitypub_schedule_migration' ); } } + + /** + * Send a profile update when relevant user meta is updated. + * + * @param int $meta_id Meta ID being updated. + * @param int $user_id User ID being updated. + * @param string $meta_key Meta key being updated. + * + * @return void + */ + public static function user_meta_update( $meta_id, $user_id, $meta_key ) { + // don't bother if the user can't publish + if ( ! \user_can( $user_id, 'publish_posts' ) ) { + return; + } + // the user meta fields that affect a profile. + $fields = array( + 'activitypub_user_description', + 'description', + 'user_url', + 'display_name', + ); + if ( in_array( $meta_key, $fields, true ) ) { + self::schedule_profile_update( $user_id ); + } + } + + /** + * Send a profile update when a user is updated. + * + * @param int $user_id User ID being updated. + * + * @return void + */ + public static function user_update( $user_id ) { + // don't bother if the user can't publish + if ( ! \user_can( $user_id, 'publish_posts' ) ) { + return; + } + + self::schedule_profile_update( $user_id ); + } + + /** + * Theme mods only have a dynamic filter so we fudge it like this. + * @param mixed $value + * @return mixed + */ + public static function blog_user_update( $value = null ) { + self::schedule_profile_update( 0 ); + return $value; + } + + /** + * Send a profile update to all followers. Gets hooked into all relevant options/meta etc. + * @param int $user_id The user ID to update (Could be 0 for Blog-User). + */ + public static function schedule_profile_update( $user_id ) { + \wp_schedule_single_event( + \time(), + 'activitypub_send_update_profile_activity', + array( $user_id ) + ); + } } diff --git a/wp-content/plugins/activitypub/includes/class-shortcodes.php b/wp-content/plugins/activitypub/includes/class-shortcodes.php index 491a6add..664266d1 100644 --- a/wp-content/plugins/activitypub/includes/class-shortcodes.php +++ b/wp-content/plugins/activitypub/includes/class-shortcodes.php @@ -110,8 +110,13 @@ class Shortcodes { $excerpt = \get_post_field( 'post_excerpt', $item ); - if ( '' === $excerpt ) { - + if ( 'attachment' === $item->post_type ) { + // get title of attachment with fallback to alt text. + $content = wp_get_attachment_caption( $item->ID ); + if ( empty( $content ) ) { + $content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true ); + } + } elseif ( '' === $excerpt ) { $content = \get_post_field( 'post_content', $item ); // An empty string will make wp_trim_excerpt do stuff we do not want. @@ -127,8 +132,7 @@ class Shortcodes { // Strip out any remaining tags. $excerpt = \wp_strip_all_tags( $excerpt ); - /** This filter is documented in wp-includes/formatting.php */ - $excerpt_more = \apply_filters( 'excerpt_more', ' [...]' ); + $excerpt_more = \apply_filters( 'activitypub_excerpt_more', ' […]' ); $excerpt_more_len = strlen( $excerpt_more ); // We now have a excerpt, but we need to check it's length, it may be longer than we want for two reasons: @@ -208,20 +212,30 @@ class Shortcodes { $tag ); - $content = \get_post_field( 'post_content', $item ); + $content = ''; - if ( 'yes' === $atts['apply_filters'] ) { - $content = \apply_filters( 'the_content', $content ); + if ( 'attachment' === $item->post_type ) { + // get title of attachment with fallback to alt text. + $content = wp_get_attachment_caption( $item->ID ); + if ( empty( $content ) ) { + $content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true ); + } } else { - $content = do_blocks( $content ); - $content = wptexturize( $content ); - $content = wp_filter_content_tags( $content ); - } + $content = \get_post_field( 'post_content', $item ); - // replace script and style elements - $content = \preg_replace( '@<(script|style)[^>]*?>.*?@si', '', $content ); - $content = strip_shortcodes( $content ); - $content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) ); + if ( 'yes' === $atts['apply_filters'] ) { + $content = \apply_filters( 'the_content', $content ); + } else { + $content = do_blocks( $content ); + $content = wptexturize( $content ); + $content = wp_filter_content_tags( $content ); + } + + // replace script and style elements + $content = \preg_replace( '@<(script|style)[^>]*?>.*?@si', '', $content ); + $content = strip_shortcodes( $content ); + $content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) ); + } add_shortcode( 'ap_content', array( 'Activitypub\Shortcodes', 'content' ) ); diff --git a/wp-content/plugins/activitypub/includes/class-signature.php b/wp-content/plugins/activitypub/includes/class-signature.php index d021cf0e..e59a1f97 100644 --- a/wp-content/plugins/activitypub/includes/class-signature.php +++ b/wp-content/plugins/activitypub/includes/class-signature.php @@ -292,7 +292,8 @@ class Signature { if ( is_array( $headers['digest'] ) ) { $headers['digest'] = $headers['digest'][0]; } - $digest = explode( '=', $headers['digest'], 2 ); + $hashalg = 'sha256'; + $digest = explode( '=', $headers['digest'], 2 ); if ( 'SHA-256' === $digest[0] ) { $hashalg = 'sha256'; } diff --git a/wp-content/plugins/activitypub/includes/class-webfinger.php b/wp-content/plugins/activitypub/includes/class-webfinger.php index 75f7ff69..67b6d4f9 100644 --- a/wp-content/plugins/activitypub/includes/class-webfinger.php +++ b/wp-content/plugins/activitypub/includes/class-webfinger.php @@ -15,89 +15,81 @@ class Webfinger { /** * Returns a users WebFinger "resource" * - * @param int $user_id + * @param int $user_id The WordPress user id * * @return string The user-resource */ public static function get_user_resource( $user_id ) { - // use WebFinger plugin if installed - if ( \function_exists( '\get_webfinger_resource' ) ) { - return \get_webfinger_resource( $user_id, false ); - } - $user = Users::get_by_id( $user_id ); if ( ! $user || is_wp_error( $user ) ) { return ''; } - return $user->get_resource(); + return $user->get_webfinger(); } /** * Resolve a WebFinger resource * - * @param string $resource The WebFinger resource + * @param string $uri The WebFinger Resource * * @return string|WP_Error The URL or WP_Error */ - public static function resolve( $resource ) { - if ( ! $resource ) { - return null; + public static function resolve( $uri ) { + $data = self::get_data( $uri ); + + if ( \is_wp_error( $data ) ) { + return $data; } - if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $resource, $m ) ) { - return null; - } - - $transient_key = 'activitypub_resolve_' . ltrim( $resource, '@' ); - - $link = \get_transient( $transient_key ); - if ( $link ) { - return $link; - } - - $url = \add_query_arg( 'resource', 'acct:' . ltrim( $resource, '@' ), 'https://' . $m[2] . '/.well-known/webfinger' ); - if ( ! \wp_http_validate_url( $url ) ) { - $response = new WP_Error( 'invalid_webfinger_url', null, $url ); - \set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period. - return $response; - } - - // try to access author URL - $response = \wp_remote_get( - $url, - array( - 'headers' => array( 'Accept' => 'application/jrd+json' ), - 'redirection' => 2, - 'timeout' => 2, - ) - ); - - if ( \is_wp_error( $response ) ) { - $link = new WP_Error( 'webfinger_url_not_accessible', null, $url ); - \set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period. - return $link; - } - - $body = \wp_remote_retrieve_body( $response ); - $body = \json_decode( $body, true ); - - if ( empty( $body['links'] ) ) { - $link = new WP_Error( 'webfinger_url_invalid_response', null, $url ); - \set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period. - return $link; - } - - foreach ( $body['links'] as $link ) { - if ( 'self' === $link['rel'] && 'application/activity+json' === $link['type'] ) { - \set_transient( $transient_key, $link['href'], WEEK_IN_SECONDS ); + foreach ( $data['links'] as $link ) { + if ( + 'self' === $link['rel'] && + 'application/activity+json' === $link['type'] + ) { return $link['href']; } } - $link = new WP_Error( 'webfinger_url_no_activitypub', null, $body ); - \set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period. - return $link; + return new WP_Error( 'webfinger_url_no_activitypub', null, $data ); + } + + /** + * Transform a URI to an acct @ + * + * @param string $uri The URI (acct:, mailto:, http:, https:) + * + * @return string|WP_Error Error or acct URI + */ + public static function uri_to_acct( $uri ) { + $data = self::get_data( $uri ); + + if ( is_wp_error( $data ) ) { + return $data; + } + + // check if subject is an acct URI + if ( + isset( $data['subject'] ) && + \str_starts_with( $data['subject'], 'acct:' ) + ) { + return $data['subject']; + } + + // search for an acct URI in the aliases + if ( isset( $data['aliases'] ) ) { + foreach ( $data['aliases'] as $alias ) { + if ( \str_starts_with( $alias, 'acct:' ) ) { + return $alias; + } + } + } + + return new WP_Error( + 'webfinger_url_no_acct', + __( 'No acct URI found.', 'activitypub' ), + $data + ); } /** @@ -137,7 +129,7 @@ class Webfinger { } if ( empty( $host ) ) { - return new WP_Error( 'invalid_identifier', __( 'Invalid Identifier', 'activitypub' ) ); + return new WP_Error( 'webfinger_invalid_identifier', __( 'Invalid Identifier', 'activitypub' ) ); } return array( $identifier, $host ); @@ -146,55 +138,70 @@ class Webfinger { /** * Get the WebFinger data for a given URI * - * @param string $identifier The Identifier: @ - * @param string $host The Host: @ + * @param string $uri The Identifier: @ or URI * * @return WP_Error|array Error reaction or array with * identifier and host as values */ - public static function get_data( $identifier, $host ) { - $webfinger_url = 'https://' . $host . '/.well-known/webfinger?resource=' . rawurlencode( $identifier ); - - $response = wp_safe_remote_get( - $webfinger_url, - array( - 'headers' => array( 'Accept' => 'application/jrd+json' ), - 'redirection' => 0, - 'timeout' => 2, - ) - ); - - if ( is_wp_error( $response ) ) { - return new WP_Error( 'webfinger_url_not_accessible', null, $webfinger_url ); - } - - $body = wp_remote_retrieve_body( $response ); - - return json_decode( $body, true ); - } - - /** - * Undocumented function - * - * @return void - */ - public static function get_remote_follow_endpoint( $uri ) { + public static function get_data( $uri ) { $identifier_and_host = self::get_identifier_and_host( $uri ); if ( is_wp_error( $identifier_and_host ) ) { return $identifier_and_host; } + $transient_key = self::generate_cache_key( $uri ); + list( $identifier, $host ) = $identifier_and_host; - $data = self::get_data( $identifier, $host ); + $data = \get_transient( $transient_key ); + if ( $data ) { + return $data; + } + + $webfinger_url = 'https://' . $host . '/.well-known/webfinger?resource=' . rawurlencode( $identifier ); + + $response = wp_safe_remote_get( + $webfinger_url, + array( + 'headers' => array( 'Accept' => 'application/jrd+json' ), + ) + ); + + if ( is_wp_error( $response ) ) { + return new WP_Error( + 'webfinger_url_not_accessible', + __( 'The WebFinger Resource is not accessible.', 'activitypub' ), + $webfinger_url + ); + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + + \set_transient( $transient_key, $data, WEEK_IN_SECONDS ); + + return $data; + } + + /** + * Get the Remote-Follow endpoint for a given URI + * + * @return string|WP_Error Error or the Remote-Follow endpoint URI. + */ + public static function get_remote_follow_endpoint( $uri ) { + $data = self::get_data( $uri ); if ( is_wp_error( $data ) ) { return $data; } if ( empty( $data['links'] ) ) { - return new WP_Error( 'webfinger_url_invalid_response', null, $data ); + return new WP_Error( + 'webfinger_missing_links', + __( 'No valid Link elements found.', 'activitypub' ), + $data + ); } foreach ( $data['links'] as $link ) { @@ -203,6 +210,27 @@ class Webfinger { } } - return new WP_Error( 'webfinger_remote_follow_endpoint_invalid', $data, array( 'status' => 417 ) ); + return new WP_Error( + 'webfinger_missing_remote_follow_endpoint', + __( 'No valid Remote-Follow endpoint found.', 'activitypub' ), + $data + ); + } + + /** + * Generate a cache key for a given URI + * + * @param string $uri A WebFinger Resource URI + * + * @return string The cache key + */ + public static function generate_cache_key( $uri ) { + $uri = ltrim( $uri, '@' ); + + if ( filter_var( $uri, FILTER_VALIDATE_EMAIL ) ) { + $uri = 'acct:' . $uri; + } + + return 'webfinger_' . md5( $uri ); } } diff --git a/wp-content/plugins/activitypub/includes/collection/class-followers.php b/wp-content/plugins/activitypub/includes/collection/class-followers.php index be98a46c..b51224fd 100644 --- a/wp-content/plugins/activitypub/includes/collection/class-followers.php +++ b/wp-content/plugins/activitypub/includes/collection/class-followers.php @@ -182,7 +182,7 @@ class Followers { $query = new WP_Query( $args ); $total = $query->found_posts; $followers = array_map( - function( $post ) { + function ( $post ) { return Follower::init_from_cpt( $post ); }, $query->get_posts() diff --git a/wp-content/plugins/activitypub/includes/collection/class-interactions.php b/wp-content/plugins/activitypub/includes/collection/class-interactions.php index 7a0fdcf9..67410f66 100644 --- a/wp-content/plugins/activitypub/includes/collection/class-interactions.php +++ b/wp-content/plugins/activitypub/includes/collection/class-interactions.php @@ -31,12 +31,13 @@ class Interactions { return false; } - $in_reply_to = \esc_url_raw( $activity['object']['inReplyTo'] ); - $comment_post_id = \url_to_postid( $in_reply_to ); - $parent_comment = object_id_to_comment( $in_reply_to ); + $in_reply_to = \esc_url_raw( $activity['object']['inReplyTo'] ); + $comment_post_id = \url_to_postid( $in_reply_to ); + $parent_comment_id = url_to_commentid( $in_reply_to ); // save only replys and reactions - if ( ! $comment_post_id && $parent_comment ) { + if ( ! $comment_post_id && $parent_comment_id ) { + $parent_comment = get_comment( $parent_comment_id ); $comment_post_id = $parent_comment->comment_post_ID; } @@ -53,15 +54,14 @@ class Interactions { $commentdata = array( 'comment_post_ID' => $comment_post_id, - 'comment_author' => \esc_attr( $meta['name'] ), + 'comment_author' => isset( $meta['name'] ) ? \esc_attr( $meta['name'] ) : \esc_attr( $meta['preferredUsername'] ), 'comment_author_url' => \esc_url_raw( $meta['url'] ), 'comment_content' => \addslashes( $activity['object']['content'] ), 'comment_type' => 'comment', 'comment_author_email' => '', - 'comment_parent' => $parent_comment ? $parent_comment->comment_ID : 0, + 'comment_parent' => $parent_comment_id ? $parent_comment_id : 0, 'comment_meta' => array( 'source_id' => \esc_url_raw( $activity['object']['id'] ), - 'source_url' => \esc_url_raw( $activity['object']['url'] ), 'protocol' => 'activitypub', ), ); @@ -70,6 +70,10 @@ class Interactions { $commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] ); } + if ( isset( $activity['object']['url'] ) ) { + $commentdata['comment_meta']['source_url'] = \esc_url_raw( $activity['object']['url'] ); + } + // disable flood control \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 ); // do not require email for AP entries @@ -77,7 +81,7 @@ class Interactions { // No nonce possible for this submission route \add_filter( 'akismet_comment_nonce', - function() { + function () { return 'inactive'; } ); @@ -98,20 +102,20 @@ class Interactions { * * @param array $activity The activity-object * - * @return array|false The commentdata or false on failure + * @return array|string|int|\WP_Error|false The commentdata or false on failure */ public static function update_comment( $activity ) { $meta = get_remote_metadata_by_actor( $activity['actor'] ); //Determine comment_ID - $object_comment_id = url_to_commentid( \esc_url_raw( $activity['object']['id'] ) ); + $comment = object_id_to_comment( \esc_url_raw( $activity['object']['id'] ) ); + $commentdata = \get_comment( $comment, ARRAY_A ); - if ( ! $object_comment_id ) { + if ( ! $commentdata ) { return false; } //found a local comment id - $commentdata = \get_comment( $object_comment_id, ARRAY_A ); $commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] ); $commentdata['comment_content'] = \addslashes( $activity['object']['content'] ); if ( isset( $meta['icon']['url'] ) ) { @@ -125,20 +129,24 @@ class Interactions { // No nonce possible for this submission route \add_filter( 'akismet_comment_nonce', - function() { + function () { return 'inactive'; } ); \add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 ); - $comment = \wp_update_comment( $commentdata, true ); + $state = \wp_update_comment( $commentdata, true ); \remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10 ); \remove_filter( 'pre_option_require_name_email', '__return_false' ); // re-add flood control \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); - return $comment; + if ( 1 === $state ) { + return $commentdata; + } else { + return $state; // Either `false` or a `WP_Error` instance or `0` or `1`! + } } /** diff --git a/wp-content/plugins/activitypub/includes/collection/class-users.php b/wp-content/plugins/activitypub/includes/collection/class-users.php index f6e35a64..ad94297c 100644 --- a/wp-content/plugins/activitypub/includes/collection/class-users.php +++ b/wp-content/plugins/activitypub/includes/collection/class-users.php @@ -7,6 +7,7 @@ use Activitypub\Model\User; use Activitypub\Model\Blog_User; use Activitypub\Model\Application_User; +use function Activitypub\url_to_authorid; use function Activitypub\is_user_disabled; class Users { @@ -103,6 +104,8 @@ class Users { return self::get_by_id( $user->results[0] ); } + $username = str_replace( array( '*', '%' ), '', $username ); + // check for login or nicename. $user = new WP_User_Query( array( @@ -133,29 +136,79 @@ class Users { * @return \Acitvitypub\Model\User The User. */ public static function get_by_resource( $resource ) { - if ( \strpos( $resource, '@' ) === false ) { - return new WP_Error( - 'activitypub_unsupported_resource', - \__( 'Resource is invalid', 'activitypub' ), - array( 'status' => 400 ) - ); + $scheme = 'acct'; + $match = array(); + // try to extract the scheme and the host + if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) { + // extract the scheme + $scheme = esc_attr( $match[1] ); } - $resource = \str_replace( 'acct:', '', $resource ); + switch ( $scheme ) { + // check for http(s) URIs + case 'http': + case 'https': + $url_parts = wp_parse_url( $resource ); - $resource_identifier = \substr( $resource, 0, \strrpos( $resource, '@' ) ); - $resource_host = self::normalize_host( \substr( \strrchr( $resource, '@' ), 1 ) ); - $blog_host = self::normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) ); + // check for http(s)://blog.example.com/@username + if ( + isset( $url_parts['path'] ) && + str_starts_with( $url_parts['path'], '/@' ) + ) { + $identifier = str_replace( '/@', '', $url_parts['path'] ); + $identifier = untrailingslashit( $identifier ); - if ( $blog_host !== $resource_host ) { - return new WP_Error( - 'activitypub_wrong_host', - \__( 'Resource host does not match blog host', 'activitypub' ), - array( 'status' => 404 ) - ); + return self::get_by_username( $identifier ); + } + + // check for http(s)://blog.example.com/author/username + $user_id = url_to_authorid( $resource ); + + if ( $user_id ) { + return self::get_by_id( $user_id ); + } + + // check for http(s)://blog.example.com/ + if ( + self::normalize_url( site_url() ) === self::normalize_url( $resource ) || + self::normalize_url( home_url() ) === self::normalize_url( $resource ) + ) { + return self::get_by_id( self::BLOG_USER_ID ); + } + + return new WP_Error( + 'activitypub_no_user_found', + \__( 'User not found', 'activitypub' ), + array( 'status' => 404 ) + ); + // check for acct URIs + case 'acct': + $resource = \str_replace( 'acct:', '', $resource ); + $identifier = \substr( $resource, 0, \strrpos( $resource, '@' ) ); + $host = self::normalize_host( \substr( \strrchr( $resource, '@' ), 1 ) ); + $blog_host = self::normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) ); + + if ( $blog_host !== $host ) { + return new WP_Error( + 'activitypub_wrong_host', + \__( 'Resource host does not match blog host', 'activitypub' ), + array( 'status' => 404 ) + ); + } + + // prepare wildcards https://github.com/mastodon/mastodon/issues/22213 + if ( in_array( $identifier, array( '_', '*', '' ), true ) ) { + return self::get_by_id( self::BLOG_USER_ID ); + } + + return self::get_by_username( $identifier ); + default: + return new WP_Error( + 'activitypub_wrong_scheme', + \__( 'Wrong scheme', 'activitypub' ), + array( 'status' => 404 ) + ); } - - return self::get_by_username( $resource_identifier ); } /** @@ -168,7 +221,12 @@ class Users { public static function get_by_various( $id ) { if ( is_numeric( $id ) ) { return self::get_by_id( $id ); - } elseif ( filter_var( $id, FILTER_VALIDATE_URL ) ) { + } elseif ( + // is URL + filter_var( $id, FILTER_VALIDATE_URL ) || + // is acct + str_starts_with( $id, 'acct:' ) + ) { return self::get_by_resource( $id ); } else { return self::get_by_username( $id ); @@ -176,7 +234,7 @@ class Users { } /** - * Normalize the host. + * Normalize a host. * * @param string $host The host. * @@ -186,6 +244,22 @@ class Users { return \str_replace( 'www.', '', $host ); } + /** + * Normalize a URL. + * + * @param string $url The URL. + * + * @return string The normalized URL. + */ + public static function normalize_url( $url ) { + $url = \untrailingslashit( $url ); + $url = \str_replace( 'https://', '', $url ); + $url = \str_replace( 'http://', '', $url ); + $url = \str_replace( 'www.', '', $url ); + + return $url; + } + /** * Get the User collection. * diff --git a/wp-content/plugins/activitypub/includes/compat.php b/wp-content/plugins/activitypub/includes/compat.php index c0996af8..8c274c88 100644 --- a/wp-content/plugins/activitypub/includes/compat.php +++ b/wp-content/plugins/activitypub/includes/compat.php @@ -47,3 +47,53 @@ if ( ! function_exists( 'is_countable' ) ) { return is_array( $value ) || $value instanceof \Countable; } } + +/** + * Polyfill for `array_is_list()` function added in PHP 7.3. + * + * @param array $array The array to check. + * + * @return bool True if `$array` is a list, otherwise false. + */ +if ( ! function_exists( 'array_is_list' ) ) { + function array_is_list( $array ) { + if ( ! is_array( $array ) ) { + return false; + } + + if ( array_values( $array ) === $array ) { + return true; + } + + $next_key = -1; + + foreach ( $array as $k => $v ) { + if ( ++$next_key !== $k ) { + return false; + } + } + + return true; + } +} + +if ( ! function_exists( 'str_contains' ) ) { + /** + * Polyfill for `str_contains()` function added in PHP 8.0. + * + * Performs a case-sensitive check indicating if needle is + * contained in haystack. + * + * @param string $haystack The string to search in. + * @param string $needle The substring to search for in the `$haystack`. + * + * @return bool True if `$needle` is in `$haystack`, otherwise false. + */ + function str_contains( $haystack, $needle ) { + if ( '' === $needle ) { + return true; + } + + return false !== strpos( $haystack, $needle ); + } +} diff --git a/wp-content/plugins/activitypub/includes/functions.php b/wp-content/plugins/activitypub/includes/functions.php index 9b2c64d0..1f8190e7 100644 --- a/wp-content/plugins/activitypub/includes/functions.php +++ b/wp-content/plugins/activitypub/includes/functions.php @@ -4,6 +4,7 @@ namespace Activitypub; use WP_Error; use WP_Comment_Query; use Activitypub\Http; +use Activitypub\Webfinger; use Activitypub\Activity\Activity; use Activitypub\Collection\Followers; use Activitypub\Collection\Users; @@ -168,6 +169,27 @@ function url_to_authorid( $url ) { return 0; } +/** + * Verify if url is a wp_ap_comment, + * Or if it is a previously received remote comment + * + * @return int comment_id + */ +function is_comment() { + $comment_id = get_query_var( 'c', null ); + + if ( ! is_null( $comment_id ) ) { + $comment = \get_comment( $comment_id ); + + // Only return local origin comments + if ( $comment && $comment->user_id ) { + return $comment_id; + } + } + + return false; +} + /** * Check for Tombstone Objects * @@ -579,7 +601,7 @@ function get_active_users( $duration = 1 ) { global $wpdb; $query = "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )"; $query = $wpdb->prepare( $query, $duration ); - $count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching set_transient( $transient_key, $count, DAY_IN_SECONDS ); } @@ -674,16 +696,33 @@ function url_to_commentid( $url ) { return null; } + // check for local comment + if ( \wp_parse_url( \site_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) { + $query = \wp_parse_url( $url, PHP_URL_QUERY ); + + if ( $query ) { + parse_str( $query, $params ); + + if ( ! empty( $params['c'] ) ) { + $comment = \get_comment( $params['c'] ); + + if ( $comment ) { + return $comment->comment_ID; + } + } + } + } + $args = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( 'relation' => 'OR', array( - 'key' => 'source_url', + 'key' => 'source_url', 'value' => $url, ), array( - 'key' => 'source_id', + 'key' => 'source_id', 'value' => $url, ), ), @@ -698,3 +737,40 @@ function url_to_commentid( $url ) { return null; } + +/** + * Get the URI of an ActivityPub object + * + * @param array $object The ActivityPub object + * + * @return string The URI of the ActivityPub object + */ +function object_to_uri( $object ) { + // check if it is already simple + if ( ! $object || is_string( $object ) ) { + return $object; + } + + // check if it is a list, then take first item + // this plugin does not support collections + if ( array_is_list( $object ) ) { + $object = $object[0]; + } + + // check if it is simplified now + if ( is_string( $object ) ) { + return $object; + } + + // return part of Object that makes most sense + switch ( $object['type'] ) { + case 'Link': + $object = $object['href']; + break; + default: + $object = $object['id']; + break; + } + + return $object; +} diff --git a/wp-content/plugins/activitypub/includes/handler/class-create.php b/wp-content/plugins/activitypub/includes/handler/class-create.php index 2e5d76ad..64788da1 100644 --- a/wp-content/plugins/activitypub/includes/handler/class-create.php +++ b/wp-content/plugins/activitypub/includes/handler/class-create.php @@ -15,7 +15,12 @@ class Create { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_action( 'activitypub_inbox_create', array( self::class, 'handle_create' ), 10, 3 ); + \add_action( + 'activitypub_inbox_create', + array( self::class, 'handle_create' ), + 10, + 3 + ); } /** diff --git a/wp-content/plugins/activitypub/includes/handler/class-delete.php b/wp-content/plugins/activitypub/includes/handler/class-delete.php index a8611d43..f2571c05 100644 --- a/wp-content/plugins/activitypub/includes/handler/class-delete.php +++ b/wp-content/plugins/activitypub/includes/handler/class-delete.php @@ -15,11 +15,24 @@ class Delete { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_action( 'activitypub_inbox_delete', array( self::class, 'handle_delete' ), 10, 2 ); + \add_action( + 'activitypub_inbox_delete', + array( self::class, 'handle_delete' ) + ); + // defer signature verification for `Delete` requests. - \add_filter( 'activitypub_defer_signature_verification', array( self::class, 'defer_signature_verification' ), 10, 2 ); + \add_filter( + 'activitypub_defer_signature_verification', + array( self::class, 'defer_signature_verification' ), + 10, + 2 + ); + // side effect - \add_action( 'activitypub_delete_actor_interactions', array( self::class, 'delete_interactions' ), 10, 1 ); + \add_action( + 'activitypub_delete_actor_interactions', + array( self::class, 'delete_interactions' ) + ); } /** @@ -28,7 +41,7 @@ class Delete { * @param array $activity The delete activity. * @param int $user_id The ID of the user performing the delete activity. */ - public static function handle_delete( $activity, $user_id ) { + public static function handle_delete( $activity ) { $object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : ''; switch ( $object_type ) { @@ -39,7 +52,7 @@ class Delete { case 'Organization': case 'Service': case 'Application': - self::maybe_delete_follower( $user_id, $activity ); + self::maybe_delete_follower( $activity ); break; // Object and Link Types // @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types diff --git a/wp-content/plugins/activitypub/includes/handler/class-follow.php b/wp-content/plugins/activitypub/includes/handler/class-follow.php index 6855dbd8..810680b7 100644 --- a/wp-content/plugins/activitypub/includes/handler/class-follow.php +++ b/wp-content/plugins/activitypub/includes/handler/class-follow.php @@ -14,8 +14,17 @@ class Follow { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow' ), 10, 2 ); - \add_action( 'activitypub_followers_post_follow', array( self::class, 'send_follow_response' ), 10, 4 ); + \add_action( + 'activitypub_inbox_follow', + array( self::class, 'handle_follow' ) + ); + + \add_action( + 'activitypub_followers_post_follow', + array( self::class, 'send_follow_response' ), + 10, + 4 + ); } /** @@ -24,11 +33,30 @@ class Follow { * @param array $activity The activity object * @param int $user_id The user ID */ - public static function handle_follow( $activity, $user_id ) { - // save follower - $follower = Followers::add_follower( $user_id, $activity['actor'] ); + public static function handle_follow( $activity ) { + $user = Users::get_by_resource( $activity['object'] ); - do_action( 'activitypub_followers_post_follow', $activity['actor'], $activity, $user_id, $follower ); + if ( ! $user || is_wp_error( $user ) ) { + // If we can not find a user, + // we can not initiate a follow process + return; + } + + $user_id = $user->get__id(); + + // save follower + $follower = Followers::add_follower( + $user_id, + $activity['actor'] + ); + + do_action( + 'activitypub_followers_post_follow', + $activity['actor'], + $activity, + $user_id, + $follower + ); } /** diff --git a/wp-content/plugins/activitypub/includes/handler/class-undo.php b/wp-content/plugins/activitypub/includes/handler/class-undo.php index 13c06f3d..74d3dcae 100644 --- a/wp-content/plugins/activitypub/includes/handler/class-undo.php +++ b/wp-content/plugins/activitypub/includes/handler/class-undo.php @@ -1,6 +1,7 @@ get__id(); + Followers::remove_follower( $user_id, $activity['actor'] ); } } diff --git a/wp-content/plugins/activitypub/includes/handler/class-update.php b/wp-content/plugins/activitypub/includes/handler/class-update.php index 00e0430b..0542d560 100644 --- a/wp-content/plugins/activitypub/includes/handler/class-update.php +++ b/wp-content/plugins/activitypub/includes/handler/class-update.php @@ -14,7 +14,10 @@ class Update { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_action( 'activitypub_inbox_update', array( self::class, 'handle_update' ), 10, 2 ); + \add_action( + 'activitypub_inbox_update', + array( self::class, 'handle_update' ) + ); } /** @@ -23,7 +26,7 @@ class Update { * @param array $array The activity-object * @param int $user_id The id of the local blog-user */ - public static function handle_update( $array, $user_id ) { + public static function handle_update( $array ) { $object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : ''; switch ( $object_type ) { @@ -45,7 +48,7 @@ class Update { case 'Video': case 'Event': case 'Document': - self::update_interaction( $array, $user_id ); + self::update_interaction( $array ); break; // Minimal Activity // @see https://www.w3.org/TR/activitystreams-core/#example-1 @@ -62,15 +65,18 @@ class Update { * * @return void */ - public static function update_interaction( $activity, $user_id ) { - $state = Interactions::update_comment( $activity ); - $reaction = null; + public static function update_interaction( $activity ) { + $commentdata = Interactions::update_comment( $activity ); + $reaction = null; - if ( $state && ! \is_wp_error( $reaction ) ) { - $reaction = \get_comment( $state ); + if ( ! empty( $commentdata['comment_ID'] ) ) { + $state = 1; + $reaction = \get_comment( $commentdata['comment_ID'] ); + } else { + $state = $commentdata; } - \do_action( 'activitypub_handled_update', $activity, $user_id, $state, $reaction ); + \do_action( 'activitypub_handled_update', $activity, null, $state, $reaction ); } /** diff --git a/wp-content/plugins/activitypub/includes/model/class-application-user.php b/wp-content/plugins/activitypub/includes/model/class-application-user.php index cf4d9cc4..8fb4ee60 100644 --- a/wp-content/plugins/activitypub/includes/model/class-application-user.php +++ b/wp-content/plugins/activitypub/includes/model/class-application-user.php @@ -38,6 +38,15 @@ class Application_User extends Blog_User { return get_rest_url_by_path( 'application' ); } + /** + * Returns the User-URL with @-Prefix for the username. + * + * @return string The User-URL with @-Prefix for the username. + */ + public function get_alternate_url() { + return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() ); + } + public function get_name() { return 'application'; } @@ -69,4 +78,8 @@ class Application_User extends Blog_User { public function get_indexable() { return false; } + + public function get_type() { + return $this->type; + } } diff --git a/wp-content/plugins/activitypub/includes/model/class-blog-user.php b/wp-content/plugins/activitypub/includes/model/class-blog-user.php index 21a066ed..53f07d48 100644 --- a/wp-content/plugins/activitypub/includes/model/class-blog-user.php +++ b/wp-content/plugins/activitypub/includes/model/class-blog-user.php @@ -100,12 +100,12 @@ class Blog_User extends User { } /** - * Returns the User-URL with @-Prefix for the username. + * Get blog's homepage URL. * - * @return string The User-URL with @-Prefix for the username. + * @return string The User-Url. */ - public function get_at_url() { - return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() ); + public function get_alternate_url() { + return \esc_url( \trailingslashit( get_home_url() ) ); } /** diff --git a/wp-content/plugins/activitypub/includes/model/class-post.php b/wp-content/plugins/activitypub/includes/model/class-post.php index d967ad9a..29aadfc9 100644 --- a/wp-content/plugins/activitypub/includes/model/class-post.php +++ b/wp-content/plugins/activitypub/includes/model/class-post.php @@ -1,6 +1,7 @@ get_user_id() ); + $user = Users::get_by_id( $this->get_user_id() ); return $user->get_url(); } diff --git a/wp-content/plugins/activitypub/includes/model/class-user.php b/wp-content/plugins/activitypub/includes/model/class-user.php index 95c83d73..e773da4d 100644 --- a/wp-content/plugins/activitypub/includes/model/class-user.php +++ b/wp-content/plugins/activitypub/includes/model/class-user.php @@ -64,7 +64,7 @@ class User extends Actor { * * @var string */ - protected $resource; + protected $webfinger; /** * Restrict posting to mods @@ -135,8 +135,8 @@ class User extends Actor { * * @return string The User-URL with @-Prefix for the username. */ - public function get_at_url() { - return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_username() ); + public function get_alternate_url() { + return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() ); } public function get_preferred_username() { @@ -226,6 +226,18 @@ class User extends Actor { return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) ); } + public function get_endpoints() { + $endpoints = null; + + if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) { + $endpoints = array( + 'sharedInbox' => get_rest_url_by_path( 'inbox' ), + ); + } + + return $endpoints; + } + /** * Extend the User-Output with Attachments. * @@ -274,10 +286,14 @@ class User extends Actor { * * @return string The Webfinger-Identifier. */ - public function get_resource() { + public function get_webfinger() { return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST ); } + public function get_resource() { + return $this->get_webfinger(); + } + public function get_canonical_url() { return $this->get_url(); } diff --git a/wp-content/plugins/activitypub/includes/peer/class-followers.php b/wp-content/plugins/activitypub/includes/peer/class-followers.php deleted file mode 100644 index e0e6ddba..00000000 --- a/wp-content/plugins/activitypub/includes/peer/class-followers.php +++ /dev/null @@ -1,34 +0,0 @@ -orderedItems = array_map( - function( $item ) use ( $context ) { + function ( $item ) use ( $context ) { if ( 'full' === $context ) { return $item->to_array(); } diff --git a/wp-content/plugins/activitypub/includes/rest/class-inbox.php b/wp-content/plugins/activitypub/includes/rest/class-inbox.php index d38ffb51..c527040f 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-inbox.php +++ b/wp-content/plugins/activitypub/includes/rest/class-inbox.php @@ -8,6 +8,7 @@ use Activitypub\Activity\Activity; use Activitypub\Collection\Users as User_Collection; use function Activitypub\get_context; +use function Activitypub\object_to_uri; use function Activitypub\url_to_authorid; use function Activitypub\get_rest_url_by_path; use function Activitypub\get_remote_metadata_by_actor; @@ -93,11 +94,8 @@ class Inbox { $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->type = 'OrderedCollectionPage'; $json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) ); // phpcs:ignore - $json->totalItems = 0; // phpcs:ignore - $json->orderedItems = array(); // phpcs:ignore - $json->first = $json->partOf; // phpcs:ignore // filter output @@ -154,37 +152,10 @@ class Inbox { $data = $request->get_json_params(); $activity = Activity::init_from_array( $data ); $type = $request->get_param( 'type' ); - $users = self::get_recipients( $data ); + $type = \strtolower( $type ); - if ( ! $users ) { - return new WP_Error( - 'rest_invalid_param', - \__( 'No recipients found', 'activitypub' ), - array( - 'status' => 400, - 'params' => array( - 'to' => \__( 'Please check/validate "to" field', 'activitypub' ), - 'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ), - 'cc' => \__( 'Please check/validate "cc" field', 'activitypub' ), - 'bcc' => \__( 'Please check/validate "bcc" field', 'activitypub' ), - 'audience' => \__( 'Please check/validate "audience" field', 'activitypub' ), - ), - ) - ); - } - - foreach ( $users as $user ) { - $user = User_Collection::get_by_various( $user ); - - if ( is_wp_error( $user ) ) { - continue; - } - - $type = \strtolower( $type ); - - \do_action( 'activitypub_inbox', $data, $user->ID, $type, $activity ); - \do_action( "activitypub_inbox_{$type}", $data, $user->ID, $activity ); - } + \do_action( 'activitypub_inbox', $data, null, $type, $activity ); + \do_action( "activitypub_inbox_{$type}", $data, null, $activity ); $rest_response = new WP_REST_Response( array(), 202 ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); @@ -236,15 +207,8 @@ class Inbox { $params['actor'] = array( 'required' => true, - 'sanitize_callback' => function( $param, $request, $key ) { - if ( \is_array( $param ) ) { - if ( isset( $param['id'] ) ) { - $param = $param['id']; - } else { - $param = $param['url']; - } - } - return \esc_url_raw( $param ); + 'sanitize_callback' => function ( $param, $request, $key ) { + return object_to_uri( $param ); }, ); @@ -252,7 +216,7 @@ class Inbox { 'required' => true, //'type' => 'enum', //'enum' => array( 'Create' ), - //'sanitize_callback' => function( $param, $request, $key ) { + //'sanitize_callback' => function ( $param, $request, $key ) { // return \strtolower( $param ); //}, ); @@ -285,11 +249,8 @@ class Inbox { $params['actor'] = array( 'required' => true, //'type' => array( 'object', 'string' ), - 'sanitize_callback' => function( $param, $request, $key ) { - if ( ! \is_string( $param ) ) { - $param = $param['id']; - } - return \esc_url_raw( $param ); + 'sanitize_callback' => function ( $param, $request, $key ) { + return object_to_uri( $param ); }, ); @@ -297,7 +258,7 @@ class Inbox { 'required' => true, //'type' => 'enum', //'enum' => array( 'Create' ), - //'sanitize_callback' => function( $param, $request, $key ) { + //'sanitize_callback' => function ( $param, $request, $key ) { // return \strtolower( $param ); //}, ); @@ -309,7 +270,7 @@ class Inbox { $params['to'] = array( 'required' => false, - 'sanitize_callback' => function( $param, $request, $key ) { + 'sanitize_callback' => function ( $param, $request, $key ) { if ( \is_string( $param ) ) { $param = array( $param ); } @@ -319,7 +280,7 @@ class Inbox { ); $params['cc'] = array( - 'sanitize_callback' => function( $param, $request, $key ) { + 'sanitize_callback' => function ( $param, $request, $key ) { if ( \is_string( $param ) ) { $param = array( $param ); } @@ -329,7 +290,7 @@ class Inbox { ); $params['bcc'] = array( - 'sanitize_callback' => function( $param, $request, $key ) { + 'sanitize_callback' => function ( $param, $request, $key ) { if ( \is_string( $param ) ) { $param = array( $param ); } diff --git a/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php b/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php index 62151fff..0c49026f 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php +++ b/wp-content/plugins/activitypub/includes/rest/class-nodeinfo.php @@ -105,6 +105,12 @@ class Nodeinfo { 'outbound' => array(), ); + $nodeinfo['metadata'] = array( + 'nodeName' => \get_bloginfo( 'name' ), + 'nodeDescription' => \get_bloginfo( 'description' ), + 'nodeIcon' => \get_site_icon_url(), + ); + return new WP_REST_Response( $nodeinfo, 200 ); } @@ -169,6 +175,10 @@ class Nodeinfo { 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', 'href' => get_rest_url_by_path( 'nodeinfo' ), ), + array( + 'rel' => 'https://www.w3.org/ns/activitystreams#Application', + 'href' => get_rest_url_by_path( 'application' ), + ), ); return new \WP_REST_Response( $discovery, 200 ); diff --git a/wp-content/plugins/activitypub/includes/rest/class-users.php b/wp-content/plugins/activitypub/includes/rest/class-users.php index 9fb10bab..ed3d2aa8 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-users.php +++ b/wp-content/plugins/activitypub/includes/rest/class-users.php @@ -50,14 +50,13 @@ class Users { array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( self::class, 'remote_follow_get' ), - + 'permission_callback' => '__return_true', 'args' => array( 'resource' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), ), - 'permission_callback' => '__return_true', ), ) ); @@ -124,7 +123,7 @@ class Users { return $template; } - $resource = $user->get_resource(); + $resource = $user->get_webfinger(); $url = str_replace( '{uri}', $resource, $template ); return new WP_REST_Response( diff --git a/wp-content/plugins/activitypub/includes/rest/class-webfinger.php b/wp-content/plugins/activitypub/includes/rest/class-webfinger.php index 34ae3929..a86fde06 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-webfinger.php +++ b/wp-content/plugins/activitypub/includes/rest/class-webfinger.php @@ -72,7 +72,7 @@ class Webfinger { $params['resource'] = array( 'required' => true, 'type' => 'string', - 'pattern' => '^acct:(.+)@(.+)$', + 'pattern' => '^(acct:)|^(https?://)(.+)$', ); return $params; @@ -94,10 +94,13 @@ class Webfinger { $aliases = array( $user->get_url(), + $user->get_alternate_url(), ); + $aliases = array_unique( $aliases ); + $profile = array( - 'subject' => $resource, + 'subject' => sprintf( 'acct:%s', $user->get_webfinger() ), 'aliases' => array_values( array_unique( $aliases ) ), 'links' => array( array( @@ -113,9 +116,9 @@ class Webfinger { ), ); - if ( 'Group' === $user->get_type() ) { + if ( 'Person' !== $user->get_type() ) { $profile['links'][0]['properties'] = array( - 'https://www.w3.org/ns/activitystreams#type' => 'Group', + 'https://www.w3.org/ns/activitystreams#type' => $user->get_type(), ); } diff --git a/wp-content/plugins/activitypub/includes/table/class-followers.php b/wp-content/plugins/activitypub/includes/table/class-followers.php index 3045dddf..df9747bd 100644 --- a/wp-content/plugins/activitypub/includes/table/class-followers.php +++ b/wp-content/plugins/activitypub/includes/table/class-followers.php @@ -5,6 +5,8 @@ use WP_List_Table; use Activitypub\Collection\Users; use Activitypub\Collection\Followers as FollowerCollection; +use function Activitypub\object_to_uri; + if ( ! \class_exists( '\WP_List_Table' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } @@ -101,7 +103,7 @@ class Followers extends WP_List_Table { 'icon' => esc_attr( $follower->get_icon_url() ), 'post_title' => esc_attr( $follower->get_name() ), 'username' => esc_attr( $follower->get_preferred_username() ), - 'url' => esc_attr( $follower->get_url() ), + 'url' => esc_attr( object_to_uri( $follower->get_url() ) ), 'identifier' => esc_attr( $follower->get_id() ), 'published' => esc_attr( $follower->get_published() ), 'modified' => esc_attr( $follower->get_updated() ), diff --git a/wp-content/plugins/activitypub/includes/transformer/class-attachment.php b/wp-content/plugins/activitypub/includes/transformer/class-attachment.php new file mode 100644 index 00000000..fc21225e --- /dev/null +++ b/wp-content/plugins/activitypub/includes/transformer/class-attachment.php @@ -0,0 +1,49 @@ +wp_object->ID ); + $media_type = preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type ); + + switch ( $media_type ) { + case 'audio': + case 'video': + $type = 'Document'; + break; + case 'image': + $type = 'Image'; + break; + } + + $attachment = array( + 'type' => $type, + 'url' => wp_get_attachment_url( $this->wp_object->ID ), + 'mediaType' => $mime_type, + ); + + $alt = \get_post_meta( $this->wp_object->ID, '_wp_attachment_image_alt', true ); + if ( $alt ) { + $attachment['name'] = $alt; + } + + return $attachment; + } +} diff --git a/wp-content/plugins/activitypub/includes/transformer/class-base.php b/wp-content/plugins/activitypub/includes/transformer/class-base.php new file mode 100644 index 00000000..92df86fd --- /dev/null +++ b/wp-content/plugins/activitypub/includes/transformer/class-base.php @@ -0,0 +1,110 @@ +wp_object = $wp_object; + } + + /** + * Transform the WordPress Object into an ActivityPub Object. + * + * @return Activitypub\Activity\Base_Object + */ + public function to_object() { + $activitypub_object = new Base_Object(); + + $vars = $activitypub_object->get_object_var_keys(); + + foreach ( $vars as $var ) { + $getter = 'get_' . $var; + + if ( method_exists( $this, $getter ) ) { + $value = call_user_func( array( $this, $getter ) ); + + if ( isset( $value ) ) { + $setter = 'set_' . $var; + + call_user_func( array( $activitypub_object, $setter ), $value ); + } + } + } + + return $activitypub_object; + } + + /** + * Transforms the ActivityPub Object to an Activity + * + * @param string $type The Activity-Type. + * + * @return \Activitypub\Activity\Activity The Activity. + */ + public function to_activity( $type ) { + $object = $this->to_object(); + + $activity = new Activity(); + $activity->set_type( $type ); + $activity->set_object( $object ); + + // Use simple Object (only ID-URI) for Like and Announce + if ( in_array( $type, array( 'Like', 'Announce' ), true ) ) { + $activity->set_object( $object->get_id() ); + } + + return $activity; + } + + /** + * Returns the ID of the WordPress Object. + * + * @return int The ID of the WordPress Object + */ + abstract public function get_wp_user_id(); + + /** + * Change the User-ID of the WordPress Post. + * + * @return int The User-ID of the WordPress Post + */ + abstract public function change_wp_user_id( $user_id ); +} diff --git a/wp-content/plugins/activitypub/includes/transformer/class-comment.php b/wp-content/plugins/activitypub/includes/transformer/class-comment.php new file mode 100644 index 00000000..f77cd212 --- /dev/null +++ b/wp-content/plugins/activitypub/includes/transformer/class-comment.php @@ -0,0 +1,274 @@ +wp_object->user_id; + } + + /** + * Change the User-ID of the WordPress Comment. + * + * @return int The User-ID of the WordPress Comment + */ + public function change_wp_user_id( $user_id ) { + $this->wp_object->user_id = $user_id; + } + + /** + * Transforms the WP_Comment object to an ActivityPub Object + * + * @see \Activitypub\Activity\Base_Object + * + * @return \Activitypub\Activity\Base_Object The ActivityPub Object + */ + public function to_object() { + $comment = $this->wp_object; + $object = parent::to_object(); + + $object->set_url( \get_comment_link( $comment->comment_ID ) ); + $object->set_type( 'Note' ); + + $published = \strtotime( $comment->comment_date_gmt ); + $object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) ); + + $updated = \get_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', true ); + if ( $updated > $published ) { + $object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) ); + } + + $object->set_content_map( + array( + $this->get_locale() => $this->get_content(), + ) + ); + $path = sprintf( 'users/%d/followers', intval( $comment->comment_author ) ); + + $object->set_to( + array( + 'https://www.w3.org/ns/activitystreams#Public', + get_rest_url_by_path( $path ), + ) + ); + + return $object; + } + + /** + * Returns the User-URL of the Author of the Post. + * + * If `single_user` mode is enabled, the URL of the Blog-User is returned. + * + * @return string The User-URL. + */ + protected function get_attributed_to() { + if ( is_single_user() ) { + $user = new Blog_User(); + return $user->get_url(); + } + + return Users::get_by_id( $this->wp_object->user_id )->get_url(); + } + + /** + * Returns the content for the ActivityPub Item. + * + * The content will be generated based on the user settings. + * + * @return string The content. + */ + protected function get_content() { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $comment = $this->wp_object; + $content = $comment->comment_content; + + $content = \wpautop( $content ); + $content = \preg_replace( '/[\n\r\t]/', '', $content ); + $content = \trim( $content ); + $content = \apply_filters( 'the_content', $content, $comment ); + + return $content; + } + + /** + * Returns the in-reply-to for the ActivityPub Item. + * + * @return int The URL of the in-reply-to. + */ + protected function get_in_reply_to() { + $comment = $this->wp_object; + + $parent_comment = \get_comment( $comment->comment_parent ); + + if ( $parent_comment ) { + $comment_meta = \get_comment_meta( $parent_comment->comment_ID ); + + if ( ! empty( $comment_meta['source_id'][0] ) ) { + $in_reply_to = $comment_meta['source_id'][0]; + } elseif ( ! empty( $comment_meta['source_url'][0] ) ) { + $in_reply_to = $comment_meta['source_url'][0]; + } else { + $in_reply_to = $this->generate_id( $parent_comment ); + } + } else { + $in_reply_to = \get_permalink( $comment->comment_post_ID ); + } + + return $in_reply_to; + } + + /** + * Returns the ID of the ActivityPub Object. + * + * @see https://www.w3.org/TR/activitypub/#obj-id + * @see https://github.com/tootsuite/mastodon/issues/13879 + * + * @return string ActivityPub URI for comment + */ + protected function get_id() { + $comment = $this->wp_object; + return $this->generate_id( $comment ); + } + + /** + * Generates an ActivityPub URI for a comment + * + * @param WP_Comment|int $comment A comment object or comment ID + * + * @return string ActivityPub URI for comment + */ + protected function generate_id( $comment ) { + $comment = get_comment( $comment ); + + return \add_query_arg( + array( + 'c' => $comment->comment_ID, + ), + \trailingslashit( site_url() ) + ); + } + + /** + * Returns a list of Mentions, used in the Comment. + * + * @see https://docs.joinmastodon.org/spec/activitypub/#Mention + * + * @return array The list of Mentions. + */ + protected function get_cc() { + $cc = array(); + + $mentions = $this->get_mentions(); + if ( $mentions ) { + foreach ( $mentions as $mention => $url ) { + $cc[] = $url; + } + } + + $comment_query = new WP_Comment_Query( + array( + 'post_id' => $this->wp_object->comment_post_ID, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + array( + 'key' => 'source_id', + 'compare' => 'EXISTS', + ), + ), + ) + ); + + if ( $comment_query->comments ) { + foreach ( $comment_query->comments as $comment ) { + if ( empty( $comment->comment_author_url ) ) { + continue; + } + $cc[] = \esc_url( $comment->comment_author_url ); + } + } + + $cc = \array_unique( $cc ); + + return $cc; + } + + /** + * Returns a list of Tags, used in the Comment. + * + * This includes Hash-Tags and Mentions. + * + * @return array The list of Tags. + */ + protected function get_tag() { + $tags = array(); + + $mentions = $this->get_mentions(); + if ( $mentions ) { + foreach ( $mentions as $mention => $url ) { + $tag = array( + 'type' => 'Mention', + 'href' => \esc_url( $url ), + 'name' => \esc_html( $mention ), + ); + $tags[] = $tag; + } + } + + return \array_unique( $tags, SORT_REGULAR ); + } + + /** + * Helper function to get the @-Mentions from the comment content. + * + * @return array The list of @-Mentions. + */ + protected function get_mentions() { + return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object ); + } + + /** + * Returns the locale of the post. + * + * @return string The locale of the post. + */ + public function get_locale() { + $comment_id = $this->wp_object->ID; + $lang = \strtolower( \strtok( \get_locale(), '_-' ) ); + + /** + * Filter the locale of the comment. + * + * @param string $lang The locale of the comment. + * @param int $comment_id The comment ID. + * @param WP_Post $post The comment object. + * + * @return string The filtered locale of the comment. + */ + return apply_filters( 'activitypub_comment_locale', $lang, $comment_id, $this->wp_object ); + } +} diff --git a/wp-content/plugins/activitypub/includes/transformer/class-factory.php b/wp-content/plugins/activitypub/includes/transformer/class-factory.php new file mode 100644 index 00000000..7df78363 --- /dev/null +++ b/wp-content/plugins/activitypub/includes/transformer/class-factory.php @@ -0,0 +1,61 @@ +post_type === 'event' ) { + * return new My_Event_Transformer( $object ); + * } + * return $transformer; + * }, 10, 3 ); + * + * @param Activitypub\Transformer\Base $transformer The transformer to use. + * @param mixed $object The object to transform. + * @param string $object_class The class of the object to transform. + * + * @return mixed The transformer to use. + */ + $transformer = apply_filters( 'activitypub_transformer', null, $object, get_class( $object ) ); + + if ( $transformer ) { + return $transformer; + } + + // use default transformer + switch ( get_class( $object ) ) { + case 'WP_Post': + if ( 'attachment' === $object->post_type ) { + return new Attachment( $object ); + } + return new Post( $object ); + case 'WP_Comment': + return new Comment( $object ); + default: + return null; + } + } +} diff --git a/wp-content/plugins/activitypub/includes/transformer/class-post.php b/wp-content/plugins/activitypub/includes/transformer/class-post.php index 721bbec0..5338ceaf 100644 --- a/wp-content/plugins/activitypub/includes/transformer/class-post.php +++ b/wp-content/plugins/activitypub/includes/transformer/class-post.php @@ -2,10 +2,11 @@ namespace Activitypub\Transformer; use WP_Post; -use Activitypub\Collection\Users; -use Activitypub\Model\Blog_User; -use Activitypub\Activity\Base_Object; use Activitypub\Shortcodes; +use Activitypub\Model\Blog_User; +use Activitypub\Transformer\Base; +use Activitypub\Collection\Users; +use Activitypub\Activity\Base_Object; use function Activitypub\esc_hashtag; use function Activitypub\is_single_user; @@ -15,42 +16,32 @@ use function Activitypub\site_supports_blocks; /** * WordPress Post Transformer * - * The Post Transformer is responsible for transforming a WP_Post object into different othe + * The Post Transformer is responsible for transforming a WP_Post object into different other * Object-Types. * * Currently supported are: * * - Activitypub\Activity\Base_Object */ -class Post { - +class Post extends Base { /** - * The WP_Post object. + * Returns the ID of the WordPress Post. * - * @var WP_Post + * @return int The ID of the WordPress Post */ - protected $wp_post; - - /** - * Static function to Transform a WP_Post Object. - * - * This helps to chain the output of the Transformer. - * - * @param WP_Post $wp_post The WP_Post object - * - * @return void - */ - public static function transform( WP_Post $wp_post ) { - return new static( $wp_post ); + public function get_wp_user_id() { + return $this->wp_object->post_author; } /** + * Change the User-ID of the WordPress Post. * - * - * @param WP_Post $wp_post + * @return int The User-ID of the WordPress Post */ - public function __construct( WP_Post $wp_post ) { - $this->wp_post = $wp_post; + public function change_wp_user_id( $user_id ) { + $this->wp_object->post_author = $user_id; + + return $this; } /** @@ -61,31 +52,25 @@ class Post { * @return \Activitypub\Activity\Base_Object The ActivityPub Object */ public function to_object() { - $wp_post = $this->wp_post; - $object = new Base_Object(); + $post = $this->wp_object; + $object = parent::to_object(); - $object->set_id( $this->get_id() ); - $object->set_url( $this->get_url() ); - $object->set_type( $this->get_object_type() ); - - $published = \strtotime( $wp_post->post_date_gmt ); + $published = \strtotime( $post->post_date_gmt ); $object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) ); - $updated = \strtotime( $wp_post->post_modified_gmt ); + $updated = \strtotime( $post->post_modified_gmt ); if ( $updated > $published ) { $object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) ); } - $object->set_attributed_to( $this->get_attributed_to() ); - $object->set_content( $this->get_content() ); $object->set_content_map( array( $this->get_locale() => $this->get_content(), ) ); - $path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) ); + $path = sprintf( 'users/%d/followers', intval( $post->post_author ) ); $object->set_to( array( @@ -93,9 +78,6 @@ class Post { get_rest_url_by_path( $path ), ) ); - $object->set_cc( $this->get_cc() ); - $object->set_attachment( $this->get_attachments() ); - $object->set_tag( $this->get_tags() ); return $object; } @@ -115,7 +97,7 @@ class Post { * @return string The Posts URL. */ public function get_url() { - $post = $this->wp_post; + $post = $this->wp_object; if ( 'trash' === get_post_status( $post ) ) { $permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true ); @@ -134,12 +116,19 @@ class Post { * @return string The User-URL. */ protected function get_attributed_to() { + $blog_user = new Blog_User(); + if ( is_single_user() ) { - $user = new Blog_User(); + return $blog_user->get_url(); + } + + $user = Users::get_by_id( $this->wp_object->post_author ); + + if ( $user && ! is_wp_error( $user ) ) { return $user->get_url(); } - return Users::get_by_id( $this->wp_post->post_author )->get_url(); + return $blog_user->get_url(); } /** @@ -147,12 +136,12 @@ class Post { * * @return array The Attachments. */ - protected function get_attachments() { + protected function get_attachment() { // Once upon a time we only supported images, but we now support audio/video as well. // We maintain the image-centric naming for backwards compatibility. $max_media = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) ); - if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) { + if ( site_supports_blocks() && \has_blocks( $this->wp_object->post_content ) ) { return $this->get_block_attachments( $max_media ); } @@ -172,7 +161,7 @@ class Post { return array(); } - $id = $this->wp_post->ID; + $id = $this->wp_object->ID; $media_ids = array(); @@ -182,7 +171,7 @@ class Post { } if ( $max_media > 0 ) { - $blocks = \parse_blocks( $this->wp_post->post_content ); + $blocks = \parse_blocks( $this->wp_object->post_content ); $media_ids = self::get_media_ids_from_blocks( $blocks, $media_ids, $max_media ); } @@ -191,6 +180,103 @@ class Post { /** * Get image attachments from the classic editor. + * This is imperfect as the contained images aren't necessarily the + * same as the attachments. + * + * @param int $max_images The maximum number of images to return. + * + * @return array The attachment IDs. + */ + protected function get_classic_editor_image_attachments( $max_images ) { + // max images can't be negative or zero + if ( $max_images <= 0 ) { + return array(); + } + $image_ids = array(); + $query = new \WP_Query( + array( + 'post_parent' => $this->wp_object->ID, + 'post_status' => 'inherit', + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'order' => 'ASC', + 'orderby' => 'menu_order ID', + 'posts_per_page' => $max_images, + ) + ); + foreach ( $query->get_posts() as $attachment ) { + if ( ! \in_array( $attachment->ID, $image_ids, true ) ) { + $image_ids[] = $attachment->ID; + } + } + return $image_ids; + } + + /** + * Get image embeds from the classic editor by parsing HTML. + * + * @param int $max_images The maximum number of images to return. + * + * @return array The attachment IDs. + */ + protected function get_classic_editor_image_embeds( $max_images ) { + // if someone calls that function directly, bail + if ( ! \class_exists( '\WP_HTML_Tag_Processor' ) ) { + return array(); + } + + // max images can't be negative or zero + if ( $max_images <= 0 ) { + return array(); + } + + $image_ids = array(); + $base = \wp_get_upload_dir()['baseurl']; + $content = \get_post_field( 'post_content', $this->wp_object ); + $tags = new \WP_HTML_Tag_Processor( $content ); + + // This linter warning is a false positive - we have to + // re-count each time here as we modify $image_ids. + // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found + while ( $tags->next_tag( 'img' ) && ( \count( $image_ids ) < $max_images ) ) { + $src = $tags->get_attribute( 'src' ); + + // If the img source is in our uploads dir, get the + // associated ID. Note: if there's a -500x500 + // type suffix, we remove it, but we try the original + // first in case the original image is actually called + // that. Likewise, we try adding the -scaled suffix for + // the case that this is a small version of an image + // that was big enough to get scaled down on upload: + // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/ + if ( null !== $src && \str_starts_with( $src, $base ) ) { + $img_id = \attachment_url_to_postid( $src ); + + if ( 0 === $img_id ) { + $count = 0; + $src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count ); + if ( $count > 0 ) { + $img_id = \attachment_url_to_postid( $src ); + } + } + + if ( 0 === $img_id ) { + $src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src ); + $img_id = \attachment_url_to_postid( $src ); + } + + if ( 0 !== $img_id ) { + if ( ! \in_array( $img_id, $image_ids, true ) ) { + $image_ids[] = $img_id; + } + } + } + } + return $image_ids; + } + + /** + * Get post images from the classic editor. * Note that audio/video attachments are only supported in the block editor. * * @param int $max_images The maximum number of images to return. @@ -203,35 +289,24 @@ class Post { return array(); } - $id = $this->wp_post->ID; + $id = $this->wp_object->ID; $image_ids = array(); // list post thumbnail first if this post has one if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) { $image_ids[] = \get_post_thumbnail_id( $id ); - --$max_images; } - if ( $max_images > 0 ) { - $query = new \WP_Query( - array( - 'post_parent' => $id, - 'post_status' => 'inherit', - 'post_type' => 'attachment', - 'post_mime_type' => 'image', - 'order' => 'ASC', - 'orderby' => 'menu_order ID', - 'posts_per_page' => $max_images, - ) - ); - foreach ( $query->get_posts() as $attachment ) { - if ( ! \in_array( $attachment->ID, $image_ids, true ) ) { - $image_ids[] = $attachment->ID; - } + if ( \count( $image_ids ) < $max_images ) { + if ( \class_exists( '\WP_HTML_Tag_Processor' ) ) { + $image_ids = \array_merge( $image_ids, $this->get_classic_editor_image_embeds( $max_images ) ); + } else { + $image_ids = \array_merge( $image_ids, $this->get_classic_editor_image_attachments( $max_images ) ); } } - $image_ids = \array_unique( $image_ids ); + // unique then slice as the thumbnail may duplicate another image + $image_ids = \array_slice( \array_unique( $image_ids ), 0, $max_images ); return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $image_ids ) ); } @@ -316,7 +391,7 @@ class Post { */ $thumbnail = apply_filters( 'activitypub_get_image', - self::get_image( $id, $image_size ), + self::get_wordpress_attachment( $id, $image_size ), $id, $image_size ); @@ -365,7 +440,7 @@ class Post { * * @return array|false Array of image data, or boolean false if no image is available. */ - protected static function get_image( $id, $image_size = 'full' ) { + protected static function get_wordpress_attachment( $id, $image_size = 'full' ) { /** * Hook into the image retrieval process. Before image retrieval. * @@ -395,17 +470,17 @@ class Post { * * @return string The Object-Type. */ - protected function get_object_type() { + protected function get_type() { if ( 'wordpress-post-format' !== \get_option( 'activitypub_object_type', 'note' ) ) { return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) ); } // Default to Article. $object_type = 'Article'; - $post_type = \get_post_type( $this->wp_post ); + $post_type = \get_post_type( $this->wp_object ); switch ( $post_type ) { case 'post': - $post_format = \get_post_format( $this->wp_post ); + $post_format = \get_post_format( $this->wp_object ); switch ( $post_format ) { case 'aside': case 'status': @@ -481,10 +556,10 @@ class Post { * * @return array The list of Tags. */ - protected function get_tags() { + protected function get_tag() { $tags = array(); - $post_tags = \get_the_tags( $this->wp_post->ID ); + $post_tags = \get_the_tags( $this->wp_object->ID ); if ( $post_tags ) { foreach ( $post_tags as $post_tag ) { $tag = array( @@ -531,7 +606,7 @@ class Post { do_action( 'activitypub_before_get_content', $post ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - $post = $this->wp_post; + $post = $this->wp_object; $content = $this->get_post_content_template(); // Register our shortcodes just in time. @@ -559,19 +634,24 @@ class Post { * @return string The Template. */ protected function get_post_content_template() { - if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) { - return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]"; + $type = \get_option( 'activitypub_post_content_type', 'content' ); + + switch ( $type ) { + case 'excerpt': + $template = "[ap_excerpt]\n\n[ap_permalink type=\"html\"]"; + break; + case 'title': + $template = "[ap_title]\n\n[ap_permalink type=\"html\"]"; + break; + case 'content': + $template = "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]"; + break; + default: + $template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ); + break; } - if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) { - return "[ap_title]\n\n[ap_permalink type=\"html\"]"; - } - - if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) { - return "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]"; - } - - return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ); + return apply_filters( 'activitypub_object_content_template', $template, $this->wp_object ); } /** @@ -580,7 +660,7 @@ class Post { * @return array The list of @-Mentions. */ protected function get_mentions() { - return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post ); + return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->post_content, $this->wp_object ); } /** @@ -589,7 +669,7 @@ class Post { * @return string The locale of the post. */ public function get_locale() { - $post_id = $this->wp_post->ID; + $post_id = $this->wp_object->ID; $lang = \strtolower( \strtok( \get_locale(), '_-' ) ); /** @@ -601,6 +681,6 @@ class Post { * * @return string The filtered locale of the post. */ - return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_post ); + return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_object ); } } diff --git a/wp-content/plugins/activitypub/integration/class-nodeinfo.php b/wp-content/plugins/activitypub/integration/class-nodeinfo.php index dea6c678..02f3a643 100644 --- a/wp-content/plugins/activitypub/integration/class-nodeinfo.php +++ b/wp-content/plugins/activitypub/integration/class-nodeinfo.php @@ -3,6 +3,7 @@ namespace Activitypub\Integration; use function Activitypub\get_total_users; use function Activitypub\get_active_users; +use function Activitypub\get_rest_url_by_path; /** * Compatibility with the NodeInfo plugin @@ -14,8 +15,10 @@ class Nodeinfo { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_filter( 'nodeinfo_data', array( self::class, 'add_nodeinfo_discovery' ), 10, 2 ); - \add_filter( 'nodeinfo2_data', array( self::class, 'add_nodeinfo2_discovery' ), 10 ); + \add_filter( 'nodeinfo_data', array( self::class, 'add_nodeinfo_data' ), 10, 2 ); + \add_filter( 'nodeinfo2_data', array( self::class, 'add_nodeinfo2_data' ), 10 ); + + \add_filter( 'wellknown_nodeinfo_data', array( self::class, 'add_wellknown_nodeinfo_data' ), 10, 2 ); } /** @@ -26,7 +29,7 @@ class Nodeinfo { * * @return array The extended array */ - public static function add_nodeinfo_discovery( $nodeinfo, $version ) { + public static function add_nodeinfo_data( $nodeinfo, $version ) { if ( $version >= '2.0' ) { $nodeinfo['protocols'][] = 'activitypub'; } else { @@ -50,7 +53,7 @@ class Nodeinfo { * * @return array The extended array */ - public static function add_nodeinfo2_discovery( $nodeinfo ) { + public static function add_nodeinfo2_data( $nodeinfo ) { $nodeinfo['protocols'][] = 'activitypub'; $nodeinfo['usage']['users'] = array( @@ -61,4 +64,20 @@ class Nodeinfo { return $nodeinfo; } + + /** + * Extend the well-known nodeinfo data + * + * @param array $data The well-known nodeinfo data + * + * @return array The extended array + */ + public static function add_wellknown_nodeinfo_data( $data ) { + $data['links'][] = array( + 'rel' => 'https://www.w3.org/ns/activitystreams#Application', + 'href' => get_rest_url_by_path( 'application' ), + ); + + return $data; + } } diff --git a/wp-content/plugins/activitypub/integration/class-webfinger.php b/wp-content/plugins/activitypub/integration/class-webfinger.php index c9dd565e..c9727861 100644 --- a/wp-content/plugins/activitypub/integration/class-webfinger.php +++ b/wp-content/plugins/activitypub/integration/class-webfinger.php @@ -14,8 +14,8 @@ class Webfinger { * Initialize the class, registering WordPress hooks */ public static function init() { - \add_filter( 'webfinger_user_data', array( self::class, 'add_user_discovery' ), 10, 3 ); - \add_filter( 'webfinger_data', array( self::class, 'add_pseudo_user_discovery' ), 99, 2 ); + \add_filter( 'webfinger_user_data', array( self::class, 'add_user_discovery' ), 1, 3 ); + \add_filter( 'webfinger_data', array( self::class, 'add_pseudo_user_discovery' ), 1, 2 ); } /** @@ -34,6 +34,11 @@ class Webfinger { return $array; } + $array['subject'] = sprintf( 'acct:%s', $user->get_webfinger() ); + + $array['aliases'][] = $user->get_url(); + $array['aliases'][] = $user->get_alternate_url(); + $array['links'][] = array( 'rel' => 'self', 'type' => 'application/activity+json', @@ -53,10 +58,12 @@ class Webfinger { * @return array the jrd array */ public static function add_pseudo_user_discovery( $array, $resource ) { - if ( $array ) { + $user = Webfinger_Rest::get_profile( $resource ); + + if ( ! $user || is_wp_error( $user ) ) { return $array; } - return Webfinger_Rest::get_profile( $resource ); + return $user; } } diff --git a/wp-content/plugins/activitypub/readme.txt b/wp-content/plugins/activitypub/readme.txt index b7c9b67a..eac5397f 100644 --- a/wp-content/plugins/activitypub/readme.txt +++ b/wp-content/plugins/activitypub/readme.txt @@ -1,9 +1,9 @@ === ActivityPub === Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nuriapena, cavalierlife Tags: OStatus, fediverse, activitypub, activitystream -Requires at least: 4.7 +Requires at least: 5.5 Tested up to: 6.4 -Stable tag: 1.3.0 +Stable tag: 2.0.1 Requires PHP: 5.6 License: MIT License URI: http://opensource.org/licenses/MIT @@ -68,10 +68,10 @@ Implemented: * share posts * receive comments/reactions * signature verification +* threaded comments support To implement: -* threaded comments support * replace shortcodes with blocks for layout = What is "ActivityPub for WordPress" = @@ -101,10 +101,34 @@ Add the following to the site.conf in sites-available: Where 'blog' is the path to the subdirectory at which your blog resides. += What if you are running your blog in a subdirectory, but have a different [wp_siteurl](https://wordpress.org/documentation/article/giving-wordpress-its-own-directory/)? = + +In that case you don't need the redirect, because the index.php will take care of that. + == Changelog == Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). += 2.0.1 = + +* Fixed: Comment `Update` Federation +* Workaround: Re-Added Post Model Class because of some weird caching issues +* Fixed: WebFinger check +* Fixed: Classic editor image finding for large images + += 2.0.0 = + +* Added: Bidirectional Comment Federation +* Removed: Deprecated Classes +* Fixed: Normalize attributes that can have mixed value types +* Added: URL support for WebFinger +* Added: Make Post-Template filterable +* Added: CSS class for ActivityPub comments to allow custom designs +* Added: FEP-2677: Identifying the Application Actor +* Added: FEP-2c59: Discovery of a Webfinger address from an ActivityPub actor +* Added: Profile Update Activities +* Improved: WebFinger endpoints + = 1.3.0 = * Added: Threaded-Comments support diff --git a/wp-content/plugins/activitypub/templates/author-json.php b/wp-content/plugins/activitypub/templates/author-json.php index 70fb43b2..7043131f 100644 --- a/wp-content/plugins/activitypub/templates/author-json.php +++ b/wp-content/plugins/activitypub/templates/author-json.php @@ -10,23 +10,8 @@ $user->set_context( */ \do_action( 'activitypub_json_author_pre', $user->get__id() ); -$options = 0; -// JSON_PRETTY_PRINT added in PHP 5.4 -if ( \get_query_var( 'pretty' ) ) { - $options |= \JSON_PRETTY_PRINT; // phpcs:ignore -} - -$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; - -/* - * Options to be passed to json_encode() - * - * @param int $options The current options flags - */ -$options = \apply_filters( 'activitypub_json_author_options', $options, $user->get__id() ); - \header( 'Content-Type: application/activity+json' ); -echo \wp_json_encode( $user->to_array(), $options ); +echo $user->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped /* * Action triggerd after the ActivityPub profile has been created and sent to the client diff --git a/wp-content/plugins/activitypub/templates/blog-json.php b/wp-content/plugins/activitypub/templates/blog-json.php index 7ce6a270..3ca54dc2 100644 --- a/wp-content/plugins/activitypub/templates/blog-json.php +++ b/wp-content/plugins/activitypub/templates/blog-json.php @@ -10,23 +10,8 @@ $user->set_context( */ \do_action( 'activitypub_json_author_pre', $user->get__id() ); -$options = 0; -// JSON_PRETTY_PRINT added in PHP 5.4 -if ( \get_query_var( 'pretty' ) ) { - $options |= \JSON_PRETTY_PRINT; // phpcs:ignore -} - -$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; - -/* - * Options to be passed to json_encode() - * - * @param int $options The current options flags - */ -$options = \apply_filters( 'activitypub_json_author_options', $options, $user->get__id() ); - \header( 'Content-Type: application/activity+json' ); -echo \wp_json_encode( $user->to_array(), $options ); +echo $user->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped /* * Action triggerd after the ActivityPub profile has been created and sent to the client diff --git a/wp-content/plugins/activitypub/templates/comment-json.php b/wp-content/plugins/activitypub/templates/comment-json.php new file mode 100644 index 00000000..aea4268c --- /dev/null +++ b/wp-content/plugins/activitypub/templates/comment-json.php @@ -0,0 +1,36 @@ + \Activitypub\get_context() ), $object->to_object()->to_array() ); + +// filter output +$json = \apply_filters( 'activitypub_json_comment_array', $json ); + +/* + * Action triggerd prior to the ActivityPub profile being created and sent to the client + */ +\do_action( 'activitypub_json_comment_pre' ); + +$options = 0; +// JSON_PRETTY_PRINT added in PHP 5.4 +if ( \get_query_var( 'pretty' ) ) { + $options |= \JSON_PRETTY_PRINT; // phpcs:ignore +} + +$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; + +/* + * Options to be passed to json_encode() + * + * @param int $options The current options flags + */ +$options = \apply_filters( 'activitypub_json_comment_options', $options ); + +\header( 'Content-Type: application/activity+json' ); +echo \wp_json_encode( $json, $options ); + +/* + * Action triggerd after the ActivityPub profile has been created and sent to the client + */ +\do_action( 'activitypub_json_comment_comment' ); diff --git a/wp-content/plugins/activitypub/templates/post-json.php b/wp-content/plugins/activitypub/templates/post-json.php index 89467c46..1db29a6e 100644 --- a/wp-content/plugins/activitypub/templates/post-json.php +++ b/wp-content/plugins/activitypub/templates/post-json.php @@ -2,34 +2,16 @@ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $post = \get_post(); -$object = new \Activitypub\Transformer\Post( $post ); -$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() ); - -// filter output -$json = \apply_filters( 'activitypub_json_post_array', $json ); +$post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_object(); +$post_object->set_context( \Activitypub\get_context() ); /* * Action triggerd prior to the ActivityPub profile being created and sent to the client */ \do_action( 'activitypub_json_post_pre' ); -$options = 0; -// JSON_PRETTY_PRINT added in PHP 5.4 -if ( \get_query_var( 'pretty' ) ) { - $options |= \JSON_PRETTY_PRINT; // phpcs:ignore -} - -$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT; - -/* - * Options to be passed to json_encode() - * - * @param int $options The current options flags - */ -$options = \apply_filters( 'activitypub_json_post_options', $options ); - \header( 'Content-Type: application/activity+json' ); -echo \wp_json_encode( $json, $options ); +echo $post_object->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped /* * Action triggerd after the ActivityPub profile has been created and sent to the client diff --git a/wp-content/plugins/activitypub/templates/user-settings.php b/wp-content/plugins/activitypub/templates/user-settings.php index afbb4046..0d7b3f6e 100644 --- a/wp-content/plugins/activitypub/templates/user-settings.php +++ b/wp-content/plugins/activitypub/templates/user-settings.php @@ -11,11 +11,11 @@ $user = \Activitypub\Collection\Users::get_by_id( \get_current_user_id() ); ?>

- get_resource() ); ?> or + get_webfinger() ); ?> or get_url() ); ?>

-

get_resource() ) ); ?>

+

get_webfinger() ) ); ?>

diff --git a/wp-content/plugins/activitypub/templates/welcome.php b/wp-content/plugins/activitypub/templates/welcome.php index cf26cfea..a2663286 100644 --- a/wp-content/plugins/activitypub/templates/welcome.php +++ b/wp-content/plugins/activitypub/templates/welcome.php @@ -30,7 +30,7 @@

- +

@@ -62,7 +62,7 @@

- +